Compare commits

...
50 Commits
Author SHA1 Message Date
johan12345 63a10d7f68 Release 2.1.3 2026-05-10 13:30:24 +02:00
johan12345 e117c844ed Merge branch 'car-app-library-1.8' 2026-05-10 13:25:22 +02:00
johan12345 36174297e5 fix Tesla login
redirect URI changed
fixes #422
2026-05-06 20:35:26 +02:00
johan12345 ac37cdde32 new map screen: keep scroll position on refresh 2026-04-24 22:12:40 +02:00
johan12345 4345ee6550 Upgrade Car App Library to 1.8.0-beta01 2026-04-24 22:12:40 +02:00
johan12345 562f2f2192 Release 2.1.2 2026-04-24 22:02:55 +02:00
johan12345 80b4ab3011 Remove NewMotionAvailabilityDetectorTest 2026-04-20 23:52:05 +02:00
johan12345 178fe804dc Remove NewMotionAvailabilityDetector
old Shell Recharge/NewMotion map doesn't exist anymore, the new one is limited to Shell's own chargers
2026-04-20 23:45:50 +02:00
johan12345 4a7c3f0b8f EnBwAvailabilityDetector: try ungrouping markers multiple times 2026-04-20 23:36:58 +02:00
johan12345 3b76e8fdfd update copyright year
fixes #421
2026-04-16 23:55:34 +02:00
johan12345 084f090d53 Release 2.1.1 2026-04-15 23:02:40 +02:00
johan12345 707b83d830 Revert "upgrade AGP"
This reverts commit 045fcc137d.
2026-04-15 22:58:35 +02:00
johan12345 e881d18f1d upgrade StfalconImageViewer 2026-04-15 22:52:00 +02:00
johan12345 045fcc137d upgrade AGP 2026-04-15 22:51:07 +02:00
johan12345 463116e87e upgrade android-spatialite 2026-04-15 21:44:24 +02:00
dependabot[bot] 0dcd06539f Bump addressable from 2.8.0 to 2.9.0
Bumps [addressable](https://github.com/sporkmonger/addressable) from 2.8.0 to 2.9.0.
- [Changelog](https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sporkmonger/addressable/compare/addressable-2.8.0...addressable-2.9.0)

---
updated-dependencies:
- dependency-name: addressable
  dependency-version: 2.9.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-14 21:29:44 +02:00
johan12345 ff651a37cf log when app to handle intent is not found 2026-03-17 20:36:01 +01:00
Robert Högberg c414e4bcc2 Nobil: Support new charger speeds 2026-03-07 17:10:45 +01:00
dependabot[bot] aebcdaa06c Bump faraday from 1.0.1 to 1.10.5
Bumps [faraday](https://github.com/lostisland/faraday) from 1.0.1 to 1.10.5.
- [Release notes](https://github.com/lostisland/faraday/releases)
- [Changelog](https://github.com/lostisland/faraday/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lostisland/faraday/compare/v1.0.1...v1.10.5)

---
updated-dependencies:
- dependency-name: faraday
  dependency-version: 1.10.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-13 17:21:36 +01:00
Hosted WeblateandRobert Högberg 4ae6a74d96 Translated using Weblate (Swedish)
Currently translated at 100.0% (379 of 379 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (94 of 94 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (378 of 378 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (378 of 378 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Robert Högberg <robert.hogberg@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/sv/
Translate-URL: https://hosted.weblate.org/projects/evmap/app-store-metadata/sv/
Translation: EVMap/Android
Translation: EVMap/App Store metadata
2026-01-25 17:39:29 +01:00
Hosted WeblateandFjuro 6868206a71 Translated using Weblate (Czech)
Currently translated at 100.0% (379 of 379 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (378 of 378 strings)

Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/cs/
Translation: EVMap/Android
2026-01-25 17:39:29 +01:00
Hosted WeblateandPriit Jõerüüt 174a0c81d3 Translated using Weblate (Estonian)
Currently translated at 100.0% (379 of 379 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (378 of 378 strings)

Translated using Weblate (Estonian)

Currently translated at 100.0% (378 of 378 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Priit Jõerüüt <jrthwlate@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/et/
Translation: EVMap/Android
2026-01-25 17:39:28 +01:00
johan12345 68948948c2 add another fallback for maps + browser intents
possible fix for #398, #414
2026-01-25 17:38:17 +01:00
johan12345 ce4f7418ea don't crash when no sharing app is found 2026-01-21 21:24:40 +01:00
Johan von Forstner 4507912f97 Fix #410: confirm before deleting profile on Automotive 2026-01-02 18:58:59 +01:00
Robert Högberg fa0826ca24 Auto: Sort nearby locations by distance in meters
The return value of Distance() differs depending on which version of that's
used:

Distance( geom1 Geometry , geom2 Geometry ) : Double precision
  return the distance between geom1 and geom2 (always measured in CRS units).

Distance( geom1 Geometry , geom2 Geometry ,
          use_ellipsoid Boolean ) : Double precision
  return the distance between geom1 and geom2 (measured in meters).

Ordering by CRS units causes unexpected item order.

Fixes #411
2026-01-02 18:53:55 +01:00
Johan von Forstner e69b3229d6 remove JetBrains from sponsors 2025-12-29 09:53:17 +01:00
Johan von Forstner 0d466e2f16 CI builds - add EVMap API key 2025-12-25 17:00:54 +01:00
Johan von Forstner cf8c7f6024 fix README typo 2025-12-25 16:49:57 +01:00
Johan von Forstner bc9292ac26 Release 2.1.0 2025-12-24 16:48:13 +01:00
Johan von Forstner 897b439cca fix crash when favorite exists but charger has been deleted from cache 2025-12-24 16:40:16 +01:00
Robert Högberg 141b2c76b1 Nobil: Add real-time availability support 2025-12-24 16:40:16 +01:00
Johan von Forstner fc22b16111 NewMotionAvailabilityDetector: get correct charger power if available 2025-12-24 12:08:43 +01:00
Hosted WeblateandPixelcode f41ea230de Translated using Weblate (German)
Currently translated at 100.0% (377 of 377 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Pixelcode <pixelcode@dismail.de>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/de/
Translation: EVMap/Android
2025-12-24 11:46:56 +01:00
dependabot[bot] ceb5081757 Bump aws-sdk-s3 from 1.78.0 to 1.208.0
Bumps [aws-sdk-s3](https://github.com/aws/aws-sdk-ruby) from 1.78.0 to 1.208.0.
- [Release notes](https://github.com/aws/aws-sdk-ruby/releases)
- [Changelog](https://github.com/aws/aws-sdk-ruby/blob/version-3/gems/aws-sdk-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-ruby/commits)

---
updated-dependencies:
- dependency-name: aws-sdk-s3
  dependency-version: 1.208.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-20 06:11:21 -05:00
Johan von Forstner 28bb8cef5f Update androidx.core.splashscreen 2025-12-10 17:42:39 +01:00
Robert Högberg ba17cb989a Nobil: Fix data expiration 2025-12-03 22:18:21 +01:00
johan12345 d08aaa3325 Auto: make AboutScreen only accessible when parked 2025-12-02 17:46:20 +01:00
Johan von Forstner 0f24608d2a Remove references to Chargeprice from README.md 2025-11-30 15:09:19 +01:00
Hosted WeblateandRobert Högberg 92e9539286 Translated using Weblate (Norwegian Bokmål)
Currently translated at 74.0% (279 of 377 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Robert Högberg <robert.hogberg@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/nb_NO/
Translation: EVMap/Android
2025-11-22 12:37:02 +01:00
b373f49180 Translated using Weblate (Swedish)
Currently translated at 100.0% (377 of 377 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (377 of 377 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Robert Högberg <robert.hogberg@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/sv/
Translation: EVMap/Android
2025-11-22 12:37:02 +01:00
Hosted WeblateandRobert Högberg ec8728a253 Translated using Weblate (French)
Currently translated at 91.2% (344 of 377 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Robert Högberg <robert.hogberg@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/fr/
Translation: EVMap/Android
2025-11-22 12:37:02 +01:00
Hosted WeblateandRobert Högberg 9ca470cd46 Translated using Weblate (Dutch)
Currently translated at 80.9% (305 of 377 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Robert Högberg <robert.hogberg@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/nl/
Translation: EVMap/Android
2025-11-22 12:37:02 +01:00
johan12345 38a1bf2da5 fix #403: add edit URL for OSM 2025-11-22 12:34:56 +01:00
johan12345 5c1dad82b1 add x64 versions of libc++_shared.so to fortify exceptions 2025-11-07 20:22:58 +01:00
Hosted WeblateandRobert Högberg 5647820f3e Translated using Weblate (Swedish)
Currently translated at 99.7% (376 of 377 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Robert Högberg <robert.hogberg@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/sv/
Translation: EVMap/Android
2025-11-07 20:10:27 +01:00
Hosted WeblateandFjuro 092a3e50bc Translated using Weblate (Czech)
Currently translated at 100.0% (377 of 377 strings)

Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/cs/
Translation: EVMap/Android
2025-11-07 20:10:27 +01:00
Hosted WeblateandPriit Jõerüüt 7b27fe2cac Translated using Weblate (Estonian)
Currently translated at 100.0% (377 of 377 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Priit Jõerüüt <jrthwlate@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/et/
Translation: EVMap/Android
2025-11-07 20:10:27 +01:00
johan12345 8991cb4e4a increase minSdk to 23 2025-11-07 20:04:33 +01:00
johan12345 66d6aee97e upgrade spatia-room 2025-11-07 19:57:11 +01:00
49 changed files with 1456 additions and 476 deletions

No files matched your search

+1
View File
@@ -26,6 +26,7 @@ jobs:
- name: Build app release & export licenses
env:
EVMAP_API_KEY: ${{ secrets.EVMAP_API_KEY }}
GOINGELECTRIC_API_KEY: ${{ secrets.GOINGELECTRIC_API_KEY }}
OPENCHARGEMAP_API_KEY: ${{ secrets.OPENCHARGEMAP_API_KEY }}
CHARGEPRICE_API_KEY: ${{ secrets.CHARGEPRICE_API_KEY }}
+3 -1
View File
@@ -75,8 +75,10 @@ jobs:
run: |
checksec --output=json --dir=lib > checksec_output.json
jq --argjson exceptions '[
"lib/arm64-v8a/libc++_shared.so",
"lib/armeabi-v7a/libc++_shared.so",
"lib/x86/libc++_shared.so"
"lib/x86/libc++_shared.so",
"lib/x86_64/libc++_shared.so"
]' '
to_entries
| map(select(.value.fortify_source == "no" and (.key as $lib | $exceptions | index($lib) | not)))
+49 -22
View File
@@ -2,26 +2,31 @@ GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.2)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
addressable (2.9.0)
public_suffix (>= 2.0.2, < 8.0)
atomos (0.1.3)
aws-eventstream (1.1.0)
aws-partitions (1.354.0)
aws-sdk-core (3.104.3)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.36.0)
aws-sdk-core (~> 3, >= 3.99.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.78.0)
aws-sdk-core (~> 3, >= 3.104.3)
aws-eventstream (1.4.0)
aws-partitions (1.1196.0)
aws-sdk-core (3.240.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
bigdecimal
jmespath (~> 1, >= 1.6.1)
logger
aws-sdk-kms (1.118.0)
aws-sdk-core (~> 3, >= 3.239.1)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.208.0)
aws-sdk-core (~> 3, >= 3.234.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.2.1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.12.1)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.3)
base64 (0.3.0)
bigdecimal (4.0.1)
claide (1.0.3)
colored (1.2)
colored2 (3.1.2)
@@ -36,11 +41,32 @@ GEM
dotenv (2.7.6)
emoji_regex (3.0.0)
excon (0.76.0)
faraday (1.0.1)
multipart-post (>= 1.2, < 3)
faraday (1.10.5)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.1)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.2.0)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
faraday_middleware (1.0.0)
faraday (~> 1.0)
fastimage (2.2.0)
@@ -113,9 +139,10 @@ GEM
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.6.1)
jmespath (1.6.2)
json (2.3.1)
jwt (2.2.1)
logger (1.7.0)
memoist (0.16.2)
mini_magick (4.10.1)
mini_mime (1.0.2)
@@ -125,7 +152,7 @@ GEM
naturally (2.2.0)
os (1.1.1)
plist (3.5.0)
public_suffix (4.0.6)
public_suffix (7.0.5)
rake (13.0.1)
representable (3.0.4)
declarative (< 0.1.0)
@@ -133,6 +160,7 @@ GEM
uber (< 0.2.0)
retriable (3.1.2)
rouge (2.0.7)
ruby2_keywords (0.0.5)
rubyzip (2.3.0)
security (0.1.3)
signet (0.14.0)
@@ -155,7 +183,6 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.7)
unf_ext (0.0.7.7-x64-mingw32)
unicode-display_width (1.7.0)
word_wrap (1.0.0)
xcodeproj (1.18.0)
@@ -170,7 +197,7 @@ GEM
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
x64-mingw32
x64-mingw-ucrt
x86_64-linux
DEPENDENCIES
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020-2024 Johan von Forstner and contributors
Copyright (c) 2020-2026 Johan von Forstner and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -9
View File
@@ -20,7 +20,6 @@ Features
- Search for places
- Advanced filtering options, including saved filter profiles
- Favorites list, also with availability information
- Integrated price comparison using [Chargeprice.app](https://chargeprice.app) (only in Europe)
- Android Auto & Android Automotive OS integration
- No ads, fully open source
- Compatible with Android 5.0 and above
@@ -87,12 +86,5 @@ Many users currently support the development EVMap with their donations. You can
information on the [Donate page](https://ev-map.app/donate/) on the EVMap website.
<a href="https://www.jawg.io"><img src="https://www.jawg.io/static/Blue@10x-9cdc4596e4e59acbd9ead55e9c28613e.png" alt="JawgMaps" height="38"/></a><br>
Since May 2024, **JawgMaps** provides their OpenStreetMap vector map tiles service to EVMap for
Since May 2024, **JawgMaps** provide their OpenStreetMap vector map tiles service to EVMap for
free, i.e. the background map displayed in the app if OpenStreetMap is selected as the data source.
<a href="https://chargeprice.app"><img src="https://raw.githubusercontent.com/ev-map/EVMap/master/_img/powered_by_chargeprice.svg" alt="Powered by Chargeprice" height="38"/></a><br>
Since April 2021, **Chargeprice.app** provide their price comparison API at a greatly reduced
price for EVMap. This data is used in EVMap's price comparison feature.
<a href="https://www.jetbrains.com/community/opensource/"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg" alt="JetBrains logo" height="38"/></a><br>
As part of its support program for Open-source projects, **JetBrains** supports the development of EVMap since December 2023 with a license of their software suite.
+1
View File
@@ -8,4 +8,5 @@
<string name="nobil_key" translatable="false">ci</string>
<string name="fronyx_key" translatable="false">ci</string>
<string name="acra_credentials" translatable="false">ci:ci</string>
<string name="evmap_key" translatable="false">ci</string>
</resources>
+20 -11
View File
@@ -18,11 +18,11 @@ android {
defaultConfig {
applicationId = "net.vonforst.evmap"
compileSdk = 36
minSdk = 21
minSdk = 23
targetSdk = 36
// NOTE: always increase versionCode by 2 since automotive flavor uses versionCode + 1
versionCode = 268
versionName = "2.0.2"
versionCode = 276
versionName = "2.1.3"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -129,6 +129,17 @@ android {
// add API keys from environment variable if not set in apikeys.xml
applicationVariants.all {
var evmapKey =
System.getenv("EVMAP_API_KEY") ?: project.findProperty("EVMAP_API_KEY")?.toString()
if (evmapKey == null && project.hasProperty("EVMAP_API_KEY_ENCRYPTED")) {
evmapKey = decode(
project.findProperty("EVMAP_API_KEY_ENCRYPTED").toString(),
"FmK.d,-f*p+rD+WK!eds"
)
}
if (evmapKey != null) {
resValue("string", "evmap_key", evmapKey)
}
val goingelectricKey =
System.getenv("GOINGELECTRIC_API_KEY") ?: project.findProperty("GOINGELECTRIC_API_KEY")
?.toString()
@@ -287,7 +298,7 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion")
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("androidx.core:core-ktx:1.17.0")
implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.core:core-splashscreen:1.2.0")
implementation("androidx.activity:activity-ktx:1.11.0")
implementation("androidx.fragment:fragment-ktx:1.8.9")
implementation("androidx.cardview:cardview:1.0.0")
@@ -309,7 +320,7 @@ dependencies {
implementation("com.squareup.moshi:moshi-adapters:1.15.2")
implementation("com.markomilos.jsonapi:jsonapi-retrofit:1.1.0")
implementation("io.coil-kt:coil:2.7.0")
implementation("com.github.ev-map:StfalconImageViewer:5082ebd392")
implementation("com.github.ev-map:StfalconImageViewer:c6ba219517")
implementation("com.mikepenz:aboutlibraries-core:$aboutLibsVersion")
implementation("com.mikepenz:aboutlibraries:$aboutLibsVersion")
implementation("com.airbnb.android:lottie:6.6.10")
@@ -319,7 +330,7 @@ dependencies {
implementation("com.github.ev-map:locale-config-x:58b036abf4")
// Android Auto
val carAppVersion = "1.7.0"
val carAppVersion = "1.8.0-beta01"
implementation("androidx.car.app:app:$carAppVersion")
normalImplementation("androidx.car.app:app-projected:$carAppVersion")
automotiveImplementation("androidx.car.app:app-automotive:$carAppVersion")
@@ -358,13 +369,11 @@ dependencies {
implementation("androidx.room:room-runtime:$roomVersion")
ksp("androidx.room:room-compiler:$roomVersion")
implementation("androidx.room:room-ktx:$roomVersion")
implementation("com.github.anboralabs:spatia-room:0.3.0") {
implementation("com.github.anboralabs:spatia-room:1.0.1") {
exclude("com.github.dalgarins", "android-spatialite")
}
// forked version with upgraded sqlite & libxml & 16 KB page size support
// https://github.com/dalgarins/android-spatialite/pull/11
// https://github.com/dalgarins/android-spatialite/pull/12
implementation("io.github.ev-map:android-spatialite:2.2.1-alpha")
// forked version with upgraded libxml 2.15.2 & APP_STL := c++_static
implementation("io.github.ev-map:android-spatialite:2.3.0-alpha")
// billing library
val billingVersion = "7.0.0"
@@ -0,0 +1,938 @@
{
"formatVersion": 1,
"database": {
"version": 28,
"identityHash": "84f71cce385c444726ba336834ddf6b4",
"entities": [
{
"tableName": "ChargeLocation",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `dataSource` TEXT NOT NULL, `name` TEXT NOT NULL, `coordinates` BLOB NOT NULL, `chargepoints` TEXT NOT NULL, `network` TEXT, `dataSourceUrl` TEXT NOT NULL, `url` TEXT, `editUrl` TEXT, `verified` INTEGER NOT NULL, `barrierFree` INTEGER, `operator` TEXT, `generalInformation` TEXT, `amenities` TEXT, `locationDescription` TEXT, `photos` TEXT, `chargecards` TEXT, `accessibility` TEXT, `license` TEXT, `networkUrl` TEXT, `chargerUrl` TEXT, `timeRetrieved` INTEGER NOT NULL, `isDetailed` INTEGER NOT NULL, `coordinatesProjected` BLOB NOT NULL, `city` TEXT, `country` TEXT, `postcode` TEXT, `street` TEXT, `fault_report_created` INTEGER, `fault_report_description` TEXT, `twentyfourSeven` INTEGER, `description` TEXT, `mostart` TEXT, `moend` TEXT, `tustart` TEXT, `tuend` TEXT, `westart` TEXT, `weend` TEXT, `thstart` TEXT, `thend` TEXT, `frstart` TEXT, `frend` TEXT, `sastart` TEXT, `saend` TEXT, `sustart` TEXT, `suend` TEXT, `hostart` TEXT, `hoend` TEXT, `freecharging` INTEGER, `freeparking` INTEGER, `descriptionShort` TEXT, `descriptionLong` TEXT, `chargepricecountry` TEXT, `chargepricenetwork` TEXT, `chargepriceplugTypes` TEXT, PRIMARY KEY(`id`, `dataSource`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dataSource",
"columnName": "dataSource",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "coordinates",
"columnName": "coordinates",
"affinity": "BLOB",
"notNull": true
},
{
"fieldPath": "chargepoints",
"columnName": "chargepoints",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "network",
"columnName": "network",
"affinity": "TEXT"
},
{
"fieldPath": "dataSourceUrl",
"columnName": "dataSourceUrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT"
},
{
"fieldPath": "editUrl",
"columnName": "editUrl",
"affinity": "TEXT"
},
{
"fieldPath": "verified",
"columnName": "verified",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "barrierFree",
"columnName": "barrierFree",
"affinity": "INTEGER"
},
{
"fieldPath": "operator",
"columnName": "operator",
"affinity": "TEXT"
},
{
"fieldPath": "generalInformation",
"columnName": "generalInformation",
"affinity": "TEXT"
},
{
"fieldPath": "amenities",
"columnName": "amenities",
"affinity": "TEXT"
},
{
"fieldPath": "locationDescription",
"columnName": "locationDescription",
"affinity": "TEXT"
},
{
"fieldPath": "photos",
"columnName": "photos",
"affinity": "TEXT"
},
{
"fieldPath": "chargecards",
"columnName": "chargecards",
"affinity": "TEXT"
},
{
"fieldPath": "accessibility",
"columnName": "accessibility",
"affinity": "TEXT"
},
{
"fieldPath": "license",
"columnName": "license",
"affinity": "TEXT"
},
{
"fieldPath": "networkUrl",
"columnName": "networkUrl",
"affinity": "TEXT"
},
{
"fieldPath": "chargerUrl",
"columnName": "chargerUrl",
"affinity": "TEXT"
},
{
"fieldPath": "timeRetrieved",
"columnName": "timeRetrieved",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isDetailed",
"columnName": "isDetailed",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "coordinatesProjected",
"columnName": "coordinatesProjected",
"affinity": "BLOB",
"notNull": true
},
{
"fieldPath": "address.city",
"columnName": "city",
"affinity": "TEXT"
},
{
"fieldPath": "address.country",
"columnName": "country",
"affinity": "TEXT"
},
{
"fieldPath": "address.postcode",
"columnName": "postcode",
"affinity": "TEXT"
},
{
"fieldPath": "address.street",
"columnName": "street",
"affinity": "TEXT"
},
{
"fieldPath": "faultReport.created",
"columnName": "fault_report_created",
"affinity": "INTEGER"
},
{
"fieldPath": "faultReport.description",
"columnName": "fault_report_description",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.twentyfourSeven",
"columnName": "twentyfourSeven",
"affinity": "INTEGER"
},
{
"fieldPath": "openinghours.description",
"columnName": "description",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.monday.start",
"columnName": "mostart",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.monday.end",
"columnName": "moend",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.tuesday.start",
"columnName": "tustart",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.tuesday.end",
"columnName": "tuend",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.wednesday.start",
"columnName": "westart",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.wednesday.end",
"columnName": "weend",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.thursday.start",
"columnName": "thstart",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.thursday.end",
"columnName": "thend",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.friday.start",
"columnName": "frstart",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.friday.end",
"columnName": "frend",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.saturday.start",
"columnName": "sastart",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.saturday.end",
"columnName": "saend",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.sunday.start",
"columnName": "sustart",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.sunday.end",
"columnName": "suend",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.holiday.start",
"columnName": "hostart",
"affinity": "TEXT"
},
{
"fieldPath": "openinghours.days.holiday.end",
"columnName": "hoend",
"affinity": "TEXT"
},
{
"fieldPath": "cost.freecharging",
"columnName": "freecharging",
"affinity": "INTEGER"
},
{
"fieldPath": "cost.freeparking",
"columnName": "freeparking",
"affinity": "INTEGER"
},
{
"fieldPath": "cost.descriptionShort",
"columnName": "descriptionShort",
"affinity": "TEXT"
},
{
"fieldPath": "cost.descriptionLong",
"columnName": "descriptionLong",
"affinity": "TEXT"
},
{
"fieldPath": "chargepriceData.country",
"columnName": "chargepricecountry",
"affinity": "TEXT"
},
{
"fieldPath": "chargepriceData.network",
"columnName": "chargepricenetwork",
"affinity": "TEXT"
},
{
"fieldPath": "chargepriceData.plugTypes",
"columnName": "chargepriceplugTypes",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id",
"dataSource"
]
}
},
{
"tableName": "Favorite",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`favoriteId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `chargerId` INTEGER NOT NULL, `chargerDataSource` TEXT NOT NULL, FOREIGN KEY(`chargerId`, `chargerDataSource`) REFERENCES `ChargeLocation`(`id`, `dataSource`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "favoriteId",
"columnName": "favoriteId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "chargerId",
"columnName": "chargerId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "chargerDataSource",
"columnName": "chargerDataSource",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"favoriteId"
]
},
"indices": [
{
"name": "index_Favorite_chargerId_chargerDataSource",
"unique": false,
"columnNames": [
"chargerId",
"chargerDataSource"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_Favorite_chargerId_chargerDataSource` ON `${TABLE_NAME}` (`chargerId`, `chargerDataSource`)"
}
],
"foreignKeys": [
{
"table": "ChargeLocation",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"chargerId",
"chargerDataSource"
],
"referencedColumns": [
"id",
"dataSource"
]
}
]
},
{
"tableName": "BooleanFilterValue",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` INTEGER NOT NULL, `dataSource` TEXT NOT NULL, `profile` INTEGER NOT NULL, PRIMARY KEY(`key`, `profile`, `dataSource`), FOREIGN KEY(`profile`, `dataSource`) REFERENCES `FilterProfile`(`id`, `dataSource`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dataSource",
"columnName": "dataSource",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "profile",
"columnName": "profile",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"key",
"profile",
"dataSource"
]
},
"indices": [
{
"name": "index_BooleanFilterValue_profile_dataSource",
"unique": false,
"columnNames": [
"profile",
"dataSource"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_BooleanFilterValue_profile_dataSource` ON `${TABLE_NAME}` (`profile`, `dataSource`)"
}
],
"foreignKeys": [
{
"table": "FilterProfile",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"profile",
"dataSource"
],
"referencedColumns": [
"id",
"dataSource"
]
}
]
},
{
"tableName": "MultipleChoiceFilterValue",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `values` TEXT NOT NULL, `all` INTEGER NOT NULL, `dataSource` TEXT NOT NULL, `profile` INTEGER NOT NULL, PRIMARY KEY(`key`, `profile`, `dataSource`), FOREIGN KEY(`profile`, `dataSource`) REFERENCES `FilterProfile`(`id`, `dataSource`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "values",
"columnName": "values",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "all",
"columnName": "all",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dataSource",
"columnName": "dataSource",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "profile",
"columnName": "profile",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"key",
"profile",
"dataSource"
]
},
"indices": [
{
"name": "index_MultipleChoiceFilterValue_profile_dataSource",
"unique": false,
"columnNames": [
"profile",
"dataSource"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_MultipleChoiceFilterValue_profile_dataSource` ON `${TABLE_NAME}` (`profile`, `dataSource`)"
}
],
"foreignKeys": [
{
"table": "FilterProfile",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"profile",
"dataSource"
],
"referencedColumns": [
"id",
"dataSource"
]
}
]
},
{
"tableName": "SliderFilterValue",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` INTEGER NOT NULL, `dataSource` TEXT NOT NULL, `profile` INTEGER NOT NULL, PRIMARY KEY(`key`, `profile`, `dataSource`), FOREIGN KEY(`profile`, `dataSource`) REFERENCES `FilterProfile`(`id`, `dataSource`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "dataSource",
"columnName": "dataSource",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "profile",
"columnName": "profile",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"key",
"profile",
"dataSource"
]
},
"indices": [
{
"name": "index_SliderFilterValue_profile_dataSource",
"unique": false,
"columnNames": [
"profile",
"dataSource"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_SliderFilterValue_profile_dataSource` ON `${TABLE_NAME}` (`profile`, `dataSource`)"
}
],
"foreignKeys": [
{
"table": "FilterProfile",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"profile",
"dataSource"
],
"referencedColumns": [
"id",
"dataSource"
]
}
]
},
{
"tableName": "FilterProfile",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `dataSource` TEXT NOT NULL, `id` INTEGER NOT NULL, `order` INTEGER NOT NULL, PRIMARY KEY(`dataSource`, `id`))",
"fields": [
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "dataSource",
"columnName": "dataSource",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"dataSource",
"id"
]
},
"indices": [
{
"name": "index_FilterProfile_dataSource_name",
"unique": true,
"columnNames": [
"dataSource",
"name"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_FilterProfile_dataSource_name` ON `${TABLE_NAME}` (`dataSource`, `name`)"
}
]
},
{
"tableName": "RecentAutocompletePlace",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `dataSource` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `primaryText` TEXT NOT NULL, `secondaryText` TEXT NOT NULL, `latLng` TEXT NOT NULL, `viewport` TEXT, `types` TEXT NOT NULL, PRIMARY KEY(`id`, `dataSource`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "dataSource",
"columnName": "dataSource",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "primaryText",
"columnName": "primaryText",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "secondaryText",
"columnName": "secondaryText",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "latLng",
"columnName": "latLng",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "viewport",
"columnName": "viewport",
"affinity": "TEXT"
},
{
"fieldPath": "types",
"columnName": "types",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id",
"dataSource"
]
}
},
{
"tableName": "GEPlug",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))",
"fields": [
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"name"
]
}
},
{
"tableName": "GENetwork",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))",
"fields": [
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"name"
]
}
},
{
"tableName": "GEChargeCard",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "OCMConnectionType",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `formalName` TEXT, `discontinued` INTEGER, `obsolete` INTEGER, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "formalName",
"columnName": "formalName",
"affinity": "TEXT"
},
{
"fieldPath": "discontinued",
"columnName": "discontinued",
"affinity": "INTEGER"
},
{
"fieldPath": "obsolete",
"columnName": "obsolete",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "OCMCountry",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `isoCode` TEXT NOT NULL, `continentCode` TEXT, `title` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isoCode",
"columnName": "isoCode",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "continentCode",
"columnName": "continentCode",
"affinity": "TEXT"
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "OCMOperator",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `websiteUrl` TEXT, `title` TEXT NOT NULL, `contactEmail` TEXT, `contactTelephone1` TEXT, `contactTelephone2` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "websiteUrl",
"columnName": "websiteUrl",
"affinity": "TEXT"
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "contactEmail",
"columnName": "contactEmail",
"affinity": "TEXT"
},
{
"fieldPath": "contactTelephone1",
"columnName": "contactTelephone1",
"affinity": "TEXT"
},
{
"fieldPath": "contactTelephone2",
"columnName": "contactTelephone2",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "OSMNetwork",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))",
"fields": [
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"name"
]
}
},
{
"tableName": "SavedRegion",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`region` BLOB NOT NULL, `dataSource` TEXT NOT NULL, `timeRetrieved` INTEGER NOT NULL, `filters` TEXT, `isDetailed` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"fields": [
{
"fieldPath": "region",
"columnName": "region",
"affinity": "BLOB",
"notNull": true
},
{
"fieldPath": "dataSource",
"columnName": "dataSource",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "timeRetrieved",
"columnName": "timeRetrieved",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "filters",
"columnName": "filters",
"affinity": "TEXT"
},
{
"fieldPath": "isDetailed",
"columnName": "isDetailed",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_SavedRegion_filters_dataSource",
"unique": false,
"columnNames": [
"filters",
"dataSource"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_SavedRegion_filters_dataSource` ON `${TABLE_NAME}` (`filters`, `dataSource`)"
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '84f71cce385c444726ba336834ddf6b4')"
]
}
}
@@ -9,12 +9,14 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.SystemClock
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
@@ -254,11 +256,9 @@ class MapsActivity : AppCompatActivity(),
fun showLocation(charger: ChargeLocation, rootView: View) {
val coord = charger.coordinates
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(
"geo:${coord.lat},${coord.lng}?q=${coord.lat},${coord.lng}(${
Uri.encode(charger.name)
})"
)
intent.data = "geo:${coord.lat},${coord.lng}?q=${coord.lat},${coord.lng}(${
Uri.encode(charger.name)
})".toUri()
val resolveInfo =
packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
@@ -277,11 +277,19 @@ class MapsActivity : AppCompatActivity(),
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
Snackbar.make(
rootView,
R.string.no_maps_app_found,
Snackbar.LENGTH_SHORT
).show()
try {
// if the intent is not working with an explicit package for some reason,
// as a last resort, try again without package
intent.setPackage(null)
startActivity(intent)
} catch (e: ActivityNotFoundException) {
Snackbar.make(
rootView,
R.string.no_maps_app_found,
Snackbar.LENGTH_SHORT
).show()
Log.e("MapsActivity", "no navigation app found")
}
}
}
@@ -294,12 +302,12 @@ class MapsActivity : AppCompatActivity(),
)
.build()
val uri = Uri.parse(url)
val uri = url.toUri()
val viewIntent = Intent(Intent.ACTION_VIEW, uri)
if (preferBrowser) {
// EVMap may be set as default app for this link, but we want to open it in a browser
// try to find default web browser
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("http://"))
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
val resolveInfo =
packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
val pkg = resolveInfo?.activityInfo?.packageName.takeIf { it != "android" }
@@ -334,20 +342,37 @@ class MapsActivity : AppCompatActivity(),
try {
intent.launchUrl(this, uri)
} catch (e: ActivityNotFoundException) {
Snackbar.make(
rootView,
R.string.no_browser_app_found,
Snackbar.LENGTH_SHORT
).show()
try {
// if the intent is not working with an explicit package for some reason,
// as a last resort, try again without package
intent.intent.setPackage(null)
intent.launchUrl(this, uri)
} catch (e: ActivityNotFoundException) {
Snackbar.make(
rootView,
R.string.no_browser_app_found,
Snackbar.LENGTH_SHORT
).show()
Log.e("MapsActivity", "no web browser found")
}
}
}
fun shareUrl(url: String) {
fun shareUrl(url: String, rootView: View) {
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, url)
}
startActivity(intent)
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
Snackbar.make(
rootView,
R.string.no_share_app_found,
Snackbar.LENGTH_SHORT
).show()
Log.e("MapsActivity", "no sharing app found")
}
}
override fun onPreferenceStartFragment(
@@ -175,8 +175,8 @@ class AvailabilityRepository(context: Context) {
RheinenergieAvailabilityDetector(okhttp),
teslaOwnerAvailabilityDetector,
TeslaGuestAvailabilityDetector(okhttp),
EnBwAvailabilityDetector(okhttp),
NewMotionAvailabilityDetector(okhttp)
NobilAvailabilityDetector(okhttp, context),
EnBwAvailabilityDetector(okhttp)
)
suspend fun getAvailability(charger: ChargeLocation): Resource<ChargeLocationStatus> {
@@ -136,17 +136,8 @@ 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)
}
repeat(3) {
markers = ungroupMarkers(markers)
}
if (markers.any { it.grouped }) throw AvailabilityDetectorException("markers still grouped")
@@ -241,6 +232,20 @@ class EnBwAvailabilityDetector(client: OkHttpClient, baseUrl: String? = null) :
)
}
private suspend fun ungroupMarkers(markers: List<EnBwApi.EnBwLocation>): List<EnBwApi.EnBwLocation> =
markers.flatMap {
if (it.grouped) {
api.getMarkers(
it.viewPort.lowerLeftLon,
it.viewPort.upperRightLon,
it.viewPort.lowerLeftLat,
it.viewPort.upperRightLat
)
} else {
listOf(it)
}
}
override fun isChargerSupported(charger: ChargeLocation): Boolean {
val country = charger.chargepriceData?.country ?: charger.address?.country
@@ -1,230 +0,0 @@
package net.vonforst.evmap.api.availability
import com.squareup.moshi.FromJson
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
import com.squareup.moshi.ToJson
import net.vonforst.evmap.model.ChargeLocation
import net.vonforst.evmap.model.Chargepoint
import net.vonforst.evmap.utils.distanceBetween
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Path
import java.time.ZonedDateTime
import java.util.Locale
private const val coordRange = 0.005 // range of latitude and longitude for loading the map
private const val maxDistance = 60 // max distance between reported positions in meters
interface NewMotionApi {
@GET("markers/{lngMin}/{lngMax}/{latMin}/{latMax}/{zoom}")
suspend fun getMarkers(
@Path("lngMin") lngMin: Double,
@Path("lngMax") lngMax: Double,
@Path("latMin") latMin: Double,
@Path("latMax") latMax: Double,
@Path("zoom") zoom: Int = 22
): List<NMMarker>
@GET("locations/{id}")
suspend fun getLocation(@Path("id") id: Long): NMLocation
@JsonClass(generateAdapter = true)
data class NMMarker(val coordinates: NMCoordinates, val locationUid: Long, val evseCount: Int)
@JsonClass(generateAdapter = true)
data class NMCoordinates(val latitude: Double, val longitude: Double)
@JsonClass(generateAdapter = true)
data class NMLocation(
val uid: Long,
val coordinates: NMCoordinates,
val operatorName: String,
val evses: List<NMEvse>
)
@JsonClass(generateAdapter = true)
data class NMEvse(
val evseId: String?,
val status: String,
val connectors: List<NMConnector>,
val updated: ZonedDateTime?
)
@JsonClass(generateAdapter = true)
data class NMConnector(
val uid: Long,
val connectorType: String,
val electricalProperties: NMElectricalProperties
)
@JsonClass(generateAdapter = true)
data class NMElectricalProperties(val powerType: String, val voltage: Int, val amperage: Int) {
fun getPower(): Double {
val phases = when (powerType) {
"AC1Phase" -> 1
"AC3Phase" -> 3
else -> 1
}
val volt = when (voltage) {
277 -> 230
else -> voltage
}
val power = volt * amperage * phases
return when (power) {
3680 -> 3.7
11040 -> 11.0
22080 -> 22.0
43470 -> 43.0
else -> power / 1000.0
}
}
}
companion object {
fun create(client: OkHttpClient, baseUrl: String? = null): NewMotionApi {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl ?: "https://ui-map.shellrecharge.com/api/map/v2/")
.addConverterFactory(
MoshiConverterFactory.create(
Moshi.Builder().add(ZonedDateTimeAdapter()).build()
)
)
.client(client)
.build()
return retrofit.create(NewMotionApi::class.java)
}
}
}
internal class ZonedDateTimeAdapter {
@FromJson
fun fromJson(value: String): ZonedDateTime? = ZonedDateTime.parse(value)
@ToJson
fun toJson(value: ZonedDateTime): String = value.toString()
}
data class NmStatus(
val conn: NewMotionApi.NMConnector,
val status: String,
val evseId: String?,
val updated: ZonedDateTime?
)
class NewMotionAvailabilityDetector(client: OkHttpClient, baseUrl: String? = null) :
BaseAvailabilityDetector(client) {
val api = NewMotionApi.create(client, baseUrl)
override suspend fun getAvailability(location: ChargeLocation): ChargeLocationStatus {
val lat = location.coordinates.lat
val lng = location.coordinates.lng
// find nearest station to this position
var markers =
api.getMarkers(lng - coordRange, lng + coordRange, lat - coordRange, lat + coordRange)
val nearest = markers.minByOrNull { marker ->
distanceBetween(marker.coordinates.latitude, marker.coordinates.longitude, lat, lng)
} ?: throw AvailabilityDetectorException("no candidates found.")
if (distanceBetween(
nearest.coordinates.latitude,
nearest.coordinates.longitude,
lat,
lng
) > radius
) {
throw AvailabilityDetectorException("no candidates found")
}
markers = if (nearest.evseCount < location.totalChargepoints) {
// combine related stations
markers.filter { marker ->
distanceBetween(
marker.coordinates.latitude,
marker.coordinates.longitude,
nearest.coordinates.latitude,
nearest.coordinates.longitude
) < maxDistance
}
} else {
listOf(nearest)
}
// load details
var details = markers.map {
api.getLocation(it.locationUid)
}
// only include stations from same operator
details = details.filter {
it.operatorName == details[0].operatorName
}
val connectorStatus = details.flatMap { it.evses }.flatMap { evse ->
evse.connectors.map { connector ->
NmStatus(connector, evse.status, evse.evseId, evse.updated)
}
}
val nmConnectors = mutableMapOf<Long, Pair<Double, String>>()
val nmStatus = mutableMapOf<Long, ChargepointStatus>()
val nmEvseId = mutableMapOf<Long, String>()
val nmUpdated = mutableMapOf<Long, ZonedDateTime>()
connectorStatus.forEach { (connector, statusStr, evseId, updated) ->
val id = connector.uid
val power = connector.electricalProperties.getPower()
val type = when (connector.connectorType.lowercase(Locale.ROOT)) {
"type3" -> Chargepoint.TYPE_3C
"type2" -> Chargepoint.TYPE_2_UNKNOWN
"type1" -> Chargepoint.TYPE_1
"domestic" -> Chargepoint.SCHUKO
"type1combo" -> Chargepoint.CCS_TYPE_1 // US CCS, aka type1_combo
"type2combo" -> Chargepoint.CCS_TYPE_2 // EU CCS, aka type2_combo
"tepcochademo" -> Chargepoint.CHADEMO
"unspecified" -> "unknown"
"unknown" -> "unknown"
"saej1772" -> "unknown"
else -> "unknown"
}
val status = when (statusStr) {
"Unavailable" -> ChargepointStatus.FAULTED
"Available" -> ChargepointStatus.AVAILABLE
"Occupied" -> ChargepointStatus.CHARGING
"Unspecified" -> ChargepointStatus.UNKNOWN
else -> ChargepointStatus.UNKNOWN
}
nmConnectors.put(id, power to type)
nmStatus.put(id, status)
evseId?.let { nmEvseId[id] = it }
updated?.let { nmUpdated[id] = it }
}
val match = matchChargepoints(nmConnectors, location.chargepointsMerged)
val chargepointStatus = match.mapValues { entry ->
entry.value.map { nmStatus[it]!! }
}
val evseIds = if (nmEvseId.size == nmStatus.size) match.mapValues { entry ->
entry.value.map { nmEvseId[it]!! }
} else null
val updated = match.mapValues { entry -> entry.value.map { nmUpdated[it]?.toInstant() } }
return ChargeLocationStatus(
chargepointStatus,
"NewMotion",
evseIds,
lastChange = updated
)
}
override fun isChargerSupported(charger: ChargeLocation): Boolean {
// NewMotion is our fallback
return when (charger.dataSource) {
"goingelectric" -> charger.network != "Tesla Supercharger"
"nobil" -> charger.network != "Tesla"
"openchargemap" -> charger.chargepriceData?.network !in listOf("23", "3534")
"openstreetmap" -> charger.operator !in listOf("Tesla, Inc.", "Tesla")
else -> false
}
}
}
@@ -0,0 +1,109 @@
package net.vonforst.evmap.api.availability
import android.content.Context
import com.squareup.moshi.FromJson
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
import com.squareup.moshi.ToJson
import net.vonforst.evmap.R
import net.vonforst.evmap.model.ChargeLocation
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Path
import java.time.Instant
internal class InstantStringAdapter {
@FromJson
fun fromJson(value: String?): Instant? = value?.let {
Instant.parse(value)
}
@ToJson
fun toJson(value: Instant?): String? = value?.toString()
}
interface NobilRealtimeApi {
@GET("{nobilId}")
suspend fun getAvailability(
@Path("nobilId") nobilId: String,
@Header("X-Api-Key") apiKey: String
): List<NobilChargepointState>
companion object {
fun create(client: OkHttpClient): NobilRealtimeApi {
val retrofit = Retrofit.Builder()
.baseUrl("https://api.ev-map.app/nobil/api/realtime/")
.addConverterFactory(
MoshiConverterFactory.create(
Moshi.Builder().add(InstantStringAdapter()).build()
)
)
.client(client)
.build()
return retrofit.create(NobilRealtimeApi::class.java)
}
}
}
@JsonClass(generateAdapter = true)
data class NobilChargepointState(
val evseUid: String,
val status: String,
val timestamp: Instant
)
class NobilAvailabilityDetector(client: OkHttpClient, context: Context) :
BaseAvailabilityDetector(client) {
val api = NobilRealtimeApi.create(client)
val apiKey = context.getString(R.string.evmap_key)
override suspend fun getAvailability(location: ChargeLocation): ChargeLocationStatus {
val nobilId = when (location.address?.country) {
"Norway" -> "NOR"
"Sweden" -> "SWE"
else -> throw AvailabilityDetectorException("nobil: unsupported country")
} + "_%05d".format(location.id)
val availability = api.getAvailability(nobilId, apiKey)
if (availability.isEmpty()) {
throw AvailabilityDetectorException("nobil: no real-time data available")
}
return ChargeLocationStatus(
location.chargepointsMerged.associateWith { cp ->
cp.evseUIds!!.map { evseUId ->
when (availability.find { it.evseUid == evseUId }?.status) {
"AVAILABLE" -> ChargepointStatus.AVAILABLE
"BLOCKED" -> ChargepointStatus.OCCUPIED
"CHARGING" -> ChargepointStatus.CHARGING
"INOPERATIVE" -> ChargepointStatus.FAULTED
"OUTOFORDER" -> ChargepointStatus.FAULTED
"PLANNED" -> ChargepointStatus.FAULTED
"REMOVED" -> ChargepointStatus.FAULTED
"RESERVED" -> ChargepointStatus.OCCUPIED
"UNKNOWN" -> ChargepointStatus.UNKNOWN
else -> ChargepointStatus.UNKNOWN
}
}
},
"Nobil",
location.chargepointsMerged.associateWith { cp ->
if (cp.evseIds != null) cp.evseIds.map { it ?: "??" } else listOf()
},
lastChange = location.chargepointsMerged.associateWith { cp ->
cp.evseUIds!!.map { evseUId ->
availability.find { it.evseUid == evseUId }?.timestamp
}
}
)
}
override fun isChargerSupported(charger: ChargeLocation): Boolean {
return when (charger.dataSource) {
"nobil" -> charger.chargepoints.any { it.evseUIds?.isNotEmpty() == true }
else -> false
}
}
}
@@ -25,7 +25,7 @@ interface TeslaAuthenticationApi {
class AuthCodeRequest(
val code: String,
@Json(name = "code_verifier") val codeVerifier: String,
@Json(name = "redirect_uri") val redirectUri: String = "https://auth.tesla.com/void/callback",
@Json(name = "redirect_uri") val redirectUri: String = "tesla://auth/callback",
scope: String = "openid email offline_access",
@Json(name = "client_id") clientId: String = "ownerapi"
) : OAuth2Request(scope, clientId)
@@ -98,13 +98,13 @@ interface TeslaAuthenticationApi {
.appendQueryParameter("client_id", "ownerapi")
.appendQueryParameter("code_challenge", codeChallenge)
.appendQueryParameter("code_challenge_method", "S256")
.appendQueryParameter("redirect_uri", "https://auth.tesla.com/void/callback")
.appendQueryParameter("redirect_uri", "tesla://auth/callback")
.appendQueryParameter("response_type", "code")
.appendQueryParameter("scope", "openid email offline_access phone")
.appendQueryParameter("is_in_app", "true")
.appendQueryParameter("state", "123").build()
val resultUrlPrefix = "https://auth.tesla.com/void/callback"
val resultUrlPrefix = "tesla://auth/callback"
}
}
@@ -274,14 +274,23 @@ data class NobilChargerStation(
"43" -> 600.0 // 600 kW DC
"44" -> 700.0 // 700 kW DC
"45" -> 800.0 // 800 kW DC
"46" -> 320.0
"47" -> 900.0
"48" -> 1000.0
"49" -> 1100.0
"50" -> 1200.0
"51" -> 1300.0
"52" -> 1400.0
"53" -> 1500.0
else -> null
}
val connectionVoltage = if (attribs["12"]?.attrVal is String) attribs["12"]?.attrVal.toString().toDoubleOrNull() else null
val connectionCurrent = if (attribs["31"]?.attrVal is String) attribs["31"]?.attrVal.toString().toDoubleOrNull() else null
val evseUId = if (attribs["27"]?.attrVal is String) listOf(attribs["27"]?.attrVal.toString()) else null
val evseId = if (attribs["28"]?.attrVal is String) listOf(attribs["28"]?.attrVal.toString()) else null
return Chargepoint(connectionType, connectionPower, 1, connectionCurrent, connectionVoltage, evseId)
return Chargepoint(connectionType, connectionPower, 1, connectionCurrent, connectionVoltage, evseId, evseUId)
}
}
}
@@ -21,6 +21,7 @@ import androidx.car.app.model.Pane
import androidx.car.app.model.Place
import androidx.car.app.model.PlaceMarker
import androidx.car.app.model.Row
import androidx.car.app.model.RowSection
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.IconCompat
import net.vonforst.evmap.R
@@ -54,7 +55,7 @@ class ChargerListFormatter(
private val iconGen = ChargerIconGenerator(carContext, null, height = 96)
var favorites: Set<Long> = emptySet()
fun buildChargerList(
fun buildChargerListLegacy(
chargers: List<ChargeLocation>?,
availabilities: Map<Long, Pair<ZonedDateTime, ChargeLocationStatus?>>
): ItemList? {
@@ -103,6 +104,55 @@ class ChargerListFormatter(
}
}
fun buildChargerList(
chargers: List<ChargeLocation>?,
availabilities: Map<Long, Pair<ZonedDateTime, ChargeLocationStatus?>>
): RowSection? {
return if (chargers != null) {
val chargerList = chargers.take(screen.maxRows)
val builder = RowSection.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,
availabilities,
showCity,
charger.id in favorites
)
)
}
builder.setNoItemsMessage(
carContext.getString(
if (screen.filterStatus == FILTERS_FAVORITES) {
R.string.auto_no_favorites_found
} else {
R.string.auto_no_chargers_found
}
)
)
builder.setOnItemVisibilityChangedListener(screen)
builder.build()
} else {
if (screen.loadingError) {
val builder = RowSection.Builder()
builder.setNoItemsMessage(
carContext.getString(R.string.connection_error)
)
builder.build()
} else if (screen.locationError) {
val builder = RowSection.Builder()
builder.setNoItemsMessage(
carContext.getString(R.string.location_error)
)
builder.build()
} else {
null
}
}
}
private fun formatCharger(
charger: ChargeLocation,
availabilities: Map<Long, Pair<ZonedDateTime, ChargeLocationStatus?>>,
@@ -15,8 +15,10 @@ import androidx.car.app.model.CarColor
import androidx.car.app.model.CarIcon
import androidx.car.app.model.CarText
import androidx.car.app.model.ForegroundCarColorSpan
import androidx.car.app.model.Header
import androidx.car.app.model.ItemList
import androidx.car.app.model.ListTemplate
import androidx.car.app.model.MessageTemplate
import androidx.car.app.model.Pane
import androidx.car.app.model.PaneTemplate
import androidx.car.app.model.ParkedOnlyOnClickListener
@@ -241,20 +243,23 @@ class FilterScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
)
setOnClickListener {
lifecycleScope.launch {
db.filterProfileDao().delete(it)
if (prefs.filterStatus == it.id) {
prefs.filterStatus = FILTERS_DISABLED
val confirmScreen = ConfirmDeleteScreen(carContext, it) {
lifecycleScope.launch {
db.filterProfileDao().delete(it)
if (prefs.filterStatus == it.id) {
prefs.filterStatus = FILTERS_DISABLED
}
CarToast.makeText(
carContext,
carContext.getString(
R.string.deleted_item,
it.name
),
CarToast.LENGTH_SHORT
).show()
}
CarToast.makeText(
carContext,
carContext.getString(
R.string.deleted_item,
it.name
),
CarToast.LENGTH_SHORT
).show()
}
screenManager.push(confirmScreen)
}
}.build())
}
@@ -299,6 +304,31 @@ class FilterScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
}
}
class ConfirmDeleteScreen(ctx: CarContext, val profile: FilterProfile, val deleteListener: () -> Unit): Screen(ctx) {
override fun onGetTemplate(): Template {
val message = carContext.getString(R.string.auto_confirm_delete_profile, profile.name)
return MessageTemplate.Builder(message)
.setHeader(Header.Builder()
.setTitle(carContext.getString(R.string.delete))
.setStartHeaderAction(Action.BACK)
.build())
.addAction(Action.Builder()
.setTitle(carContext.getString(R.string.delete))
.setBackgroundColor(CarColor.PRIMARY)
.setFlags(Action.FLAG_PRIMARY)
.setOnClickListener {
deleteListener()
screenManager.pop()
}
.build())
.addAction(Action.Builder()
.setTitle(carContext.getString(R.string.cancel))
.setOnClickListener { screenManager.pop() }
.build())
.build()
}
}
@androidx.car.app.annotations.ExperimentalCarApi
class EditFiltersScreen(ctx: CarContext) : Screen(ctx) {
private val vm = FilterViewModel(carContext.applicationContext as Application)
@@ -360,18 +390,21 @@ class EditFiltersScreen(ctx: CarContext) : Screen(ctx) {
)
setOnClickListener {
lifecycleScope.launch {
vm.deleteCurrentProfile()
CarToast.makeText(
carContext,
carContext.getString(
R.string.deleted_item,
currentProfile.name
),
CarToast.LENGTH_SHORT
).show()
screenManager.pop()
val confirmScreen = ConfirmDeleteScreen(carContext, currentProfile) {
lifecycleScope.launch {
vm.deleteCurrentProfile()
CarToast.makeText(
carContext,
carContext.getString(
R.string.deleted_item,
currentProfile.name
),
CarToast.LENGTH_SHORT
).show()
screenManager.pop()
}
}
screenManager.push(confirmScreen)
}
}.build())
}
@@ -58,7 +58,6 @@ import java.io.IOException
import java.time.Duration
import java.time.Instant
import java.time.ZonedDateTime
import kotlin.collections.set
import kotlin.math.abs
import kotlin.math.min
@@ -164,7 +163,7 @@ class LegacyMapScreen(ctx: CarContext, val session: EVMapSession) :
setAnchor(Place.Builder(CarLocation.create(it.latitude, it.longitude)).build())
}
}
formatter.buildChargerList(chargers, availabilities)?.let {
formatter.buildChargerListLegacy(chargers, availabilities)?.let {
setItemList(it)
} ?: setLoading(true)
setCurrentLocationEnabled(true)
@@ -21,9 +21,9 @@ import androidx.car.app.model.ActionStrip
import androidx.car.app.model.CarColor
import androidx.car.app.model.CarIcon
import androidx.car.app.model.Header
import androidx.car.app.model.ListTemplate
import androidx.car.app.model.MessageTemplate
import androidx.car.app.model.PaneTemplate
import androidx.car.app.model.SectionedItemTemplate
import androidx.car.app.model.Template
import androidx.car.app.navigation.model.MapController
import androidx.car.app.navigation.model.MapWithContentTemplate
@@ -213,7 +213,7 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
}.build())
}.build()
} else {
ListTemplate.Builder().apply {
SectionedItemTemplate.Builder().apply {
setHeader(Header.Builder().apply {
setTitle(title)
setStartHeaderAction(Action.APP_ICON)
@@ -223,8 +223,10 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
chargers?.filterIsInstance<ChargeLocation>(),
availabilities
)?.let {
setSingleList(it)
addSection(it)
} ?: setLoading(true)
setScrollStatePersistenceStrategy(SectionedItemTemplate.SCROLL_STATE_PRESERVE_INDEX)
}.build()
}
return MapWithContentTemplate.Builder().apply {
@@ -143,9 +143,9 @@ class SettingsScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx),
).setTint(CarColor.DEFAULT).build()
)
.setBrowsable(true)
.setOnClickListener {
.setOnClickListener(ParkedOnlyOnClickListener.create {
screenManager.push(AboutScreen(carContext, session))
}
})
.build()
)
}.build())
@@ -513,7 +513,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MenuProvider {
R.id.menu_share -> {
val charger = vm.charger.value?.data
if (charger != null && charger.url != null) {
(activity as? MapsActivity)?.shareUrl(charger.url)
(activity as? MapsActivity)?.shareUrl(charger.url, binding.root)
}
true
}
@@ -140,10 +140,12 @@ data class ChargeLocation(
.filter { it.type == variant.type && it.power == variant.power }
val count = filtered.sumOf { it.count }
val mergedEvseIds = filtered.map { if (it.evseIds == null) List(it.count) {null} else it.evseIds }.flatten()
val mergedEvseUIds = filtered.map { if (it.evseUIds == null) List(it.count) {null} else it.evseUIds }.flatten()
Chargepoint(variant.type, variant.power, count,
filtered.map { it.current }.distinct().singleOrNull(),
filtered.map { it.voltage }.distinct().singleOrNull(),
if (mergedEvseIds.all { it == null }) null else mergedEvseIds
if (mergedEvseIds.all { it == null }) null else mergedEvseIds,
if (mergedEvseUIds.all { it == null }) null else mergedEvseUIds
)
}
}
@@ -425,7 +427,9 @@ data class Chargepoint(
// (each of the three can be separately limited)
val voltage: Double? = null,
// Electric Vehicle Supply Equipment Ids for this Chargepoint's plugs/sockets
val evseIds: List<String?>? = null
val evseIds: List<String?>? = null,
// Electric Vehicle Supply Equipment Unique Ids for this Chargepoint's plugs/sockets
val evseUIds: List<String?>? = null
) : Equatable, Parcelable {
fun hasKnownPower(): Boolean = power != null
fun hasKnownVoltageAndCurrent(): Boolean = voltage != null && current != null
@@ -25,4 +25,4 @@ data class Favorite(
data class FavoriteWithDetail(
@Embedded val favorite: Favorite,
@Embedded val charger: ChargeLocation
)
)
@@ -37,6 +37,7 @@ class CustomNavigator(
"goingelectric" -> "https://www.goingelectric.de/stromtankstellen/new/"
"nobil" -> "http://nobil.no/api/chargerregistration/chargerregistration.php?action=register"
"openchargemap" -> "https://openchargemap.org/site/poi/add"
"openstreetmap" -> "https://www.openstreetmap.org/edit"
else -> throw IllegalArgumentException()
}
launchCustomTab(url)
@@ -101,7 +101,7 @@ abstract class ChargeLocationsDao {
): List<ChargeLocation>
@SkipQueryVerification
@Query("SELECT * FROM chargelocation WHERE dataSource == :dataSource AND PtDistWithin(coordinates, MakePoint(:lng, :lat, 4326), :radius) AND timeRetrieved > :after AND ROWID IN (SELECT ROWID FROM SpatialIndex WHERE f_table_name = 'ChargeLocation' AND f_geometry_column = 'coordinates' AND search_frame = BuildCircleMbr(:lng, :lat, :radius)) ORDER BY Distance(coordinates, MakePoint(:lng, :lat, 4326))")
@Query("SELECT * FROM chargelocation WHERE dataSource == :dataSource AND PtDistWithin(coordinates, MakePoint(:lng, :lat, 4326), :radius) AND timeRetrieved > :after AND ROWID IN (SELECT ROWID FROM SpatialIndex WHERE f_table_name = 'ChargeLocation' AND f_geometry_column = 'coordinates' AND search_frame = BuildCircleMbr(:lng, :lat, :radius)) ORDER BY Distance(coordinates, MakePoint(:lng, :lat, 4326), 0)")
abstract suspend fun getChargeLocationsRadius(
lat: Double,
lng: Double,
@@ -583,7 +583,7 @@ class ChargeLocationsRepository(
val region =
radiusSpatialIndexQuery(location, radius)
val order =
"ORDER BY Distance(coordinates, MakePoint(${location.longitude}, ${location.latitude}, 4326))"
"ORDER BY Distance(coordinates, MakePoint(${location.longitude}, ${location.latitude}, 4326), 0)"
return queryWithFilters(api, filters, region, order)
}
@@ -15,7 +15,7 @@ class CleanupCacheWorker(appContext: Context, workerParams: WorkerParameters) :
val savedRegionDao = db.savedRegionDao()
val now = Instant.now()
val dataSources = listOf("openchargemap", "openstreetmap", "goingelectric")
val dataSources = listOf("openchargemap", "openstreetmap", "goingelectric", "nobil")
for (dataSource in dataSources) {
val api = createApi(dataSource, applicationContext)
val limit = now.minus(api.cacheLimit).toEpochMilli()
@@ -40,7 +40,7 @@ import net.vonforst.evmap.model.SliderFilterValue
OCMOperator::class,
OSMNetwork::class,
SavedRegion::class
], version = 27
], version = 28
)
@TypeConverters(Converters::class, GeometryConverters::class)
abstract class AppDatabase : RoomDatabase() {
@@ -85,7 +85,7 @@ abstract class AppDatabase : RoomDatabase() {
MIGRATION_12, MIGRATION_13, MIGRATION_14, MIGRATION_15, MIGRATION_16,
MIGRATION_17, MIGRATION_18, MIGRATION_19, MIGRATION_20, MIGRATION_21,
MIGRATION_22, MIGRATION_23, MIGRATION_24, MIGRATION_25, MIGRATION_26,
MIGRATION_27
MIGRATION_27, MIGRATION_28
)
.addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
@@ -547,6 +547,14 @@ abstract class AppDatabase : RoomDatabase() {
db.execSQL("ALTER TABLE `ChargeLocation` ADD `accessibility` TEXT")
}
}
private val MIGRATION_28 = object : Migration(27, 28) {
override fun migrate(db: SupportSQLiteDatabase) {
// Force nobil data refresh to fetch EVSE UId attributes needed for real-time data
db.execSQL("DELETE FROM SavedRegion WHERE `dataSource` = 'nobil'")
db.execSQL("DELETE FROM ChargeLocation WHERE `dataSource` = 'nobil'")
}
}
}
/**
@@ -13,10 +13,10 @@ interface FavoritesDao {
@Delete
suspend fun delete(vararg favorites: Favorite)
@Query("SELECT * FROM favorite LEFT JOIN chargelocation ON favorite.chargerDataSource = chargelocation.dataSource AND favorite.chargerId = chargelocation.id")
@Query("SELECT * FROM favorite LEFT JOIN chargelocation ON favorite.chargerDataSource = chargelocation.dataSource AND favorite.chargerId = chargelocation.id WHERE chargelocation.id is not NULL")
fun getAllFavorites(): LiveData<List<FavoriteWithDetail>>
@Query("SELECT * FROM favorite LEFT JOIN chargelocation ON favorite.chargerDataSource = chargelocation.dataSource AND favorite.chargerId = chargelocation.id")
@Query("SELECT * FROM favorite LEFT JOIN chargelocation ON favorite.chargerDataSource = chargelocation.dataSource AND favorite.chargerId = chargelocation.id WHERE chargelocation.id is not NULL")
suspend fun getAllFavoritesAsync(): List<FavoriteWithDetail>
@SkipQueryVerification
+3
View File
@@ -392,4 +392,7 @@
<string name="accessibility_residents">Obyvatelé</string>
<string name="chargeprice_removal_2025_dialog_title">Omlouváme se!</string>
<string name="chargeprice_removal_2025_dialog_detail">Náklady na přístup k údajům ze služby Chargeprice prudce vzrostly a nelze je pokrýt z darů, takže EVMap již nemůže tyto údaje přímo zobrazovat. Prozatím se otevře webová stránka Chargeprice. Alternativní řešení se vyvíjí, ale bude to nějakou dobu trvat a zpočátku bude mít omezené funkce. Děkujeme za trpělivost a podporu!</string>
<string name="auto_use_new_map_screen">Nová obrazovka mapy (beta)</string>
<string name="auto_confirm_delete_profile">Odstranit profil filtru „%s“?</string>
<string name="no_share_app_found">Nepodařilo se najít aplikaci ke sdílení</string>
</resources>
+3 -1
View File
@@ -6,6 +6,7 @@
<string name="no_maps_app_found">Bitte installiere eine Navigations-App</string>
<string name="no_browser_app_found">Bitte installiere einen Webbrowser</string>
<string name="no_email_app_found">Bitte installiere eine E-Mail-App</string>
<string name="no_share_app_found">Keine App zum Teilen gefunden</string>
<string name="address">Adresse</string>
<string name="operator">Betreiber</string>
<string name="network">Verbund</string>
@@ -380,7 +381,7 @@
<string name="pref_chargeprice_native_integration_on">Preise werden direkt in EVMap angezeigt</string>
<string name="pref_chargeprice_native_integration_off">Preisvergleich verlinkt auf die App oder Website von Chargeprice</string>
<string name="auto_zoom_for_details">Für Details hineinzoomen</string>
<string name="plug_type_2_tethered">Typ 2 Kabel mit Stecker</string>
<string name="plug_type_2_tethered">Typ-2-Kabel mit Stecker</string>
<string name="accessibility_public">Öffentlich</string>
<string name="accessibility_visitors">Besucher</string>
<string name="accessibility_employees">Mitarbeiter</string>
@@ -389,4 +390,5 @@
<string name="chargeprice_removal_2025_dialog_title">Sorry!</string>
<string name="chargeprice_removal_2025_dialog_detail">Die Kosten für den Zugriff auf Chargeprice-Daten sind stark gestiegen und können nicht mehr durch Spenden gedeckt werden. Daher kann EVMap diese Daten nicht mehr direkt anzeigen. Hier öffnet sich nun die Chargeprice-Website. Eine alternative Lösung ist in Arbeit, wird aber Zeit brauchen und anfangs nur eingeschränkt funktionieren. Danke für eure Geduld und Unterstützung!</string>
<string name="auto_use_new_map_screen">Neue Kartendarstellung (beta)</string>
<string name="auto_confirm_delete_profile">Filterprofil „%s“ löschen?</string>
</resources>
+4 -1
View File
@@ -293,7 +293,7 @@
<string name="reload">Laadi uuesti</string>
<string name="auto_chargeprice_vehicle_ambiguous">Mitu selles rakenduses valitud mudelit vastavad selle sõiduki mudelile (%1$s %2$s).</string>
<string name="navigate">Näita teed</string>
<string name="donation_dialog_detail">EVMap põhineb avatud lähtekoodil on tasuta ja vabaks kasutamiseks. Me hea meelega näeme ka huviliste kaastööd GitHubis. Andmekasutusega seotud kulude kastmiseks palun kaalu võimalust arendaja rahaliseks toetamiseks sinu valitud summaga.</string>
<string name="donation_dialog_detail">EVMap põhineb avatud lähtekoodil on tasuta ja vabaks kasutamiseks. Me hea meelega näeme ka huviliste kaastööd GitHubis. Andmekasutusega seotud kulude katmiseks palun kaalu võimalust arendaja rahaliseks toetamiseks sinu valitud summaga.</string>
<string name="chargeprice_donation_dialog_title">Sa tõesti otsid parimaid pakkumisi!</string>
<string name="chargeprice_donation_dialog_detail">Kasutad hoolega hinnavõrdluse funktsionaalsust. Sellega andmevahetusega seotud kulude katmiseks palun kaalu EVMapi rahalist toetamist.</string>
<string name="deleted_item">Kustutasime „%s“</string>
@@ -388,4 +388,7 @@
<string name="accessibility_residents">Elanikele</string>
<string name="chargeprice_removal_2025_dialog_title">Vabandust!</string>
<string name="chargeprice_removal_2025_dialog_detail">Chargeprice\'i andmete maksumus on 2025. aastast järsult kasvanud ja rahalistest toetustest meile pole võimalik seda enam rahastada. Seega EVMap ei saa neid andmeid enam otse näidata. Asendusena on esialgu kasutusel link Chargeprice\'i veebisaiti. Oleme arendamas ka alternatiivset lahendust, aga selleks kulub aega ning ta võib kasutusele tulla piiratud funktsionaalsuses. Suur tänu teie toe eest!</string>
<string name="auto_use_new_map_screen">Uus kaardivaade (beetaversioon)</string>
<string name="auto_confirm_delete_profile">Kas soovid kustutada filtriprofiili „%s“?</string>
<string name="no_share_app_found">Ei leidunud rakendust, millega sisu jagada</string>
</resources>
+9 -9
View File
@@ -9,14 +9,14 @@
<string name="operator">Opérateur</string>
<string name="network">Réseau</string>
<string name="hours">Heures d\'ouverture</string>
<string name="open_247"><b>Ouvert 24h/24 et 7j/7</b></string>
<string name="open_closesat"><b>Ouvert</b> · Ferme à %s</string>
<string name="open_247"><![CDATA[<b>Ouvert 24h/24 et 7j/7</b>]]></string>
<string name="open_closesat"><![CDATA[<b>Ouvert</b> · Ferme à %s]]></string>
<string name="closed_unfmt">Fermé</string>
<string name="cost">Coût</string>
<string name="closed"><b>Fermé</b></string>
<string name="closed_opensat"><b>Fermé</b> · Ouvre à %s</string>
<string name="closed"><![CDATA[<b>Fermé</b>]]></string>
<string name="closed_opensat"><![CDATA[<b>Fermé</b> · Ouvre à %s]]></string>
<string name="holiday">Jour férié</string>
<string name="cost_detail"><b>Recharge :</b> %1$s · <b>Stationnement :</b> %2$s</string>
<string name="cost_detail"><![CDATA[<b>Recharge :</b> %1$s · <b>Stationnement :</b> %2$s]]></string>
<string name="realtime_data_unavailable">Statut en temps réel non disponible</string>
<string name="source">Source : %s</string>
<string name="menu_favs">Favoris</string>
@@ -235,17 +235,17 @@
<string name="crash_report_text">EVMap a planté. Veuillez envoyer un rapport de plantage au développeur.</string>
<string name="unknown_operator">Opérateur inconnu</string>
<string name="data_source_goingelectric_desc">Idéal dans les pays germanophones. Descriptions en allemand. Maintenu par la communauté.</string>
<string name="data_source_openchargemap_desc">Couverture mondiale avec une qualité variable. Descriptions en anglais ou dans la langue locale. Données ouvertes maintenues par la communauté et provenant de sources gouvernementales dans certains pays (par exemple, Amérique du Nord, Royaume-Uni, France, Norvège).</string>
<string name="data_source_openchargemap_desc"><![CDATA[Couverture mondiale avec une qualité variable. Descriptions en anglais ou dans la langue locale. Données ouvertes maintenues par la communauté et provenant de sources gouvernementales dans certains pays (par exemple, Amérique du Nord, Royaume-Uni, France, Norvège).]]></string>
<string name="faq_link">https://ev-map.app/faq/</string>
<string name="chargeprice_faq_link">https://ev-map.app/faq/#price-comparison-feature</string>
<string name="settings_data_sources">Sources de données</string>
<string name="data_sources_description">Veuillez choisir une source de données pour les stations de recharge. Vous pourrez la modifier ultérieurement dans les paramètres de l\'application.</string>
<string name="pref_search_provider_info">Les données pour la recherche de lieux, en particulier celles de Google Maps, sont relativement coûteuses à récupérer. Veuillez envisager de faire un don via \"À propos\" → \"Faire un don\".</string>
<string name="pref_search_provider_info"><![CDATA[Les données pour la recherche de lieux, en particulier celles de Google Maps, sont relativement coûteuses à récupérer. Veuillez envisager de faire un don via "À propos" → "Faire un don".]]></string>
<string name="help">Aide</string>
<string name="pref_chargeprice_allow_unbalanced_load_summary">Autoriser la charge en courant alternatif monophasé de plus de 4,5 kW</string>
<string name="pref_map_rotate_gestures_on">Utilisez deux doigts pour faire pivoter la carte</string>
<string name="cost_detail_charging"><b>Recharge %s</b></string>
<string name="cost_detail_parking"><b>Stationnement %s</b></string>
<string name="cost_detail_charging"><![CDATA[<b>Recharge %s</b>]]></string>
<string name="cost_detail_parking"><![CDATA[<b>Stationnement %s</b>]]></string>
<string name="navigate">Naviguer vers</string>
<string name="charge_price_format">%1$.2f %2$s</string>
<string name="charge_price_average_format">⌀ %1$.2f %2$s/kWh</string>
+9 -9
View File
@@ -2,8 +2,8 @@
<resources>
<string name="app_name">EVMap</string>
<string name="no_maps_app_found">Installer et navigeringsprogram først</string>
<string name="closed"><b>Stengt</b></string>
<string name="open_closesat"><b>Åpen</b> · Stenger %s</string>
<string name="closed"><![CDATA[<b>Stengt</b>]]></string>
<string name="open_closesat"><![CDATA[<b>Åpen</b> · Stenger %s]]></string>
<string name="holiday">Ferie</string>
<string name="cost">Kostnad</string>
<string name="general_info">Generell info</string>
@@ -40,18 +40,18 @@
<string name="edit_filter_profile">Rediger «%s»</string>
<string name="help">Hjelp</string>
<string name="hours">Åpningstider</string>
<string name="open_247"><b>Døgnåpen</b></string>
<string name="open_247"><![CDATA[<b>Døgnåpen</b>]]></string>
<string name="settings_ui">Grensesnitt</string>
<string name="title_activity_maps">EVMap</string>
<string name="no_browser_app_found">Installer en nettleser først</string>
<string name="address">Adresse</string>
<string name="network">Nettverk</string>
<string name="closed_unfmt">Stengt</string>
<string name="cost_detail_charging"><b>%s-lading</b></string>
<string name="cost_detail_parking"><b>%s-parkering</b></string>
<string name="cost_detail_charging"><![CDATA[<b>%s-lading</b>]]></string>
<string name="cost_detail_parking"><![CDATA[<b>%s-parkering</b>]]></string>
<string name="menu_map">Kart</string>
<string name="category_petrol_station">Bensinstasjon</string>
<string name="closed_opensat"><b>Stengt</b> · Åpner %s</string>
<string name="closed_opensat"><![CDATA[<b>Stengt</b> · Åpner %s]]></string>
<string name="retry">Prøv igjen</string>
<string name="source">Kilde: %s</string>
<string name="menu_favs">Favoritter</string>
@@ -88,7 +88,7 @@
<string name="realtime_data_source">Kilde for sanntidsstatus (beta): %s</string>
<string name="realtime_data_unavailable">Sanntidsstatus utilgjengelig</string>
<string name="other">Andre</string>
<string name="cost_detail"><b>Lading:</b> %1$s · <b>Parkering:</b> %2$s</string>
<string name="cost_detail"><![CDATA[<b>Lading:</b> %1$s · <b>Parkering:</b> %2$s]]></string>
<string name="pref_navigate_use_maps_on">Navigasjonsnkappen starter ruteveiledning på Google Maps</string>
<string name="filter_free_parking">Kun ladere med gratis parkering</string>
<string name="filter_min_power">Min. effekt</string>
@@ -239,8 +239,8 @@
</plurals>
<string name="data_source_goingelectric_desc">Storartet i tyskspråklige land. Beskrivelser på tysk. Gemenskapsdrevet.</string>
<string name="powered_by_mapbox">tilbudt av Mapbox</string>
<string name="pref_search_provider_info">Data for søk er dyre å hente, spesielt fra Google Maps. Overvei å donere gjennom «Om» → «Doner».</string>
<string name="data_source_openchargemap_desc">Verdensomspennende, med varierende kvalitet. Beskrivelser på engelsk eller det lokale språket. Gemenskapsdrevet og åpen myndighetsdata i noen land (f.eks. Nord-Amerika, Storbritannia, Frankrike, og Norge.)</string>
<string name="pref_search_provider_info"><![CDATA[Data for søk er dyre å hente, spesielt fra Google Maps. Overvei å donere gjennom «Om» → «Doner».]]></string>
<string name="data_source_openchargemap_desc"><![CDATA[Verdensomspennende, med varierende kvalitet. Beskrivelser på engelsk eller det lokale språket. Gemenskapsdrevet og åpen myndighetsdata i noen land (f.eks. Nord-Amerika, Storbritannia, Frankrike, og Norge.)]]></string>
<string name="lets_go">Begynn</string>
<string name="crash_report_text">EVMap krasjet. Send en rapport til utvikleren.</string>
<string name="chargeprice_all_tariffs_selected">alle planer valgt</string>
+9 -9
View File
@@ -2,8 +2,8 @@
<resources>
<string name="data_source_goingelectric_desc">Ideaal in Duitstalige landen. Beschrijvingen in het Duits. Onderhouden door de gebruikersgemeenschap.</string>
<string name="crash_report_text">EVMap is afgebroken. Stuur een crash rapport naar de ontwikkelaar.</string>
<string name="pref_search_provider_info">Gegevens opzoeken is duur, vooral via Google Maps. Overweeg aub om een donatie te doen via “Over” → “Doneer”.</string>
<string name="data_source_openchargemap_desc">Werelddekkend, met variabele kwaliteit. Beschrijving in Engels of lokale taal. Onderhouden door de gebruikers. Ook open overheidswege eens in sommige landen (bv. Noord-Amerika, UK, Frankrijk, Noorwegen).</string>
<string name="pref_search_provider_info"><![CDATA[Gegevens opzoeken is duur, vooral via Google Maps. Overweeg aub om een donatie te doen via “Over” → “Doneer”.]]></string>
<string name="data_source_openchargemap_desc"><![CDATA[Werelddekkend, met variabele kwaliteit. Beschrijving in Engels of lokale taal. Onderhouden door de gebruikers. Ook open overheidswege eens in sommige landen (bv. Noord-Amerika, UK, Frankrijk, Noorwegen).]]></string>
<string name="pref_darkmode_always_off">altijd uit</string>
<string name="chargeprice_select_car_first">Kiest eerst je voertuig model in de instellingen</string>
<string name="chargeprice_no_compatible_connectors">Geen compatibele connectoren aan dit laadstation</string>
@@ -22,16 +22,16 @@
<string name="address">Adres</string>
<string name="operator">Operator</string>
<string name="network">Netwerk</string>
<string name="open_247"><b>24/7 open</b></string>
<string name="closed"><b>Gesloten</b></string>
<string name="open_closesat"><b>Open</b> · Sluit om %s</string>
<string name="closed_opensat"><b>Gesloten</b> · Opent om %s</string>
<string name="open_247"><![CDATA[<b>24/7 open</b>]]></string>
<string name="closed"><![CDATA[<b>Gesloten</b>]]></string>
<string name="open_closesat"><![CDATA[<b>Open</b> · Sluit om %s]]></string>
<string name="closed_opensat"><![CDATA[<b>Gesloten</b> · Opent om %s]]></string>
<string name="closed_unfmt">Gesloten</string>
<string name="holiday">Feestdag</string>
<string name="cost">Kostprijs</string>
<string name="cost_detail"><b>Laden:</b> %1$s · <b>Parkeren:</b> %2$s</string>
<string name="cost_detail_charging"><b>%s laden</b></string>
<string name="cost_detail_parking"><b>%s parkeren</b></string>
<string name="cost_detail"><![CDATA[<b>Laden:</b> %1$s · <b>Parkeren:</b> %2$s]]></string>
<string name="cost_detail_charging"><![CDATA[<b>%s laden</b>]]></string>
<string name="cost_detail_parking"><![CDATA[<b>%s parkeren</b>]]></string>
<string name="charging_free">Gratis</string>
<string name="parking_free">Gratis</string>
<string name="amenities">Voorzieningen</string>
+17 -13
View File
@@ -50,7 +50,7 @@
<string name="fav_remove">Ta bort från favoriter</string>
<string name="pref_navigate_use_maps">Omedelbar navigering</string>
<string name="pref_navigate_use_maps_on">Navigeraknappen startar vägbeskrivning i Google Maps</string>
<string name="pref_navigate_use_maps_off">Navigeraknappen öppnar kartappen med laddstationen</string>
<string name="pref_navigate_use_maps_off">Navigeraknappen öppnar laddarens position i kartappen</string>
<string name="coordinates">Koordinater</string>
<string name="share">Dela</string>
<string name="filter_free">Endast gratis laddare</string>
@@ -82,7 +82,7 @@
<string name="map_type_satellite">Satellit</string>
<string name="map_type_terrain">Terräng</string>
<string name="map_type">Karttyp</string>
<string name="map_details">Kartdetailjer</string>
<string name="map_details">Kartdetaljer</string>
<string name="map_traffic">Trafik</string>
<string name="faq">Vanliga frågor</string>
<string name="menu_filters_active">Aktiva filter</string>
@@ -160,14 +160,14 @@
<string name="filterprofile_name_not_unique">Det finns redan en filterprofil med det namnet</string>
<string name="filterprofiles_empty_state">Du har inga sparade filterprofiler</string>
<string name="welcome_to_evmap">Välkommen till EVMap</string>
<string name="welcome_1">Hitta laddare för elfordon i din närhet</string>
<string name="welcome_2">Varje laddares färg motsvarar dess maximala laddeffekt</string>
<string name="welcome_2_detail">Det här visas också i “Om”Vanliga frågor</string>
<string name="welcome_1">Hitta laddare till elfordon i närheten</string>
<string name="welcome_2">Varje laddares färg motsvarar dess maximala hastighet</string>
<string name="welcome_2_detail">Det här visas också i \"Om\"\"Vanliga frågor\"</string>
<string name="donation_dialog_title">Tack för att du använder EVMap</string>
<string name="donation_dialog_detail">EVMap är fri programvara. Kodbidrag via GitHub tas tacksamt emot. Överväg gärna att donera en valfri summa till utvecklaren för att hjälpa till att täcka driftskostnader.</string>
<string name="donation_dialog_detail">EVMap är fri programvara. Kodbidrag tas tacksamt emot via GitHub. Överväg gärna att donera en valfri summa till utvecklaren för att hjälpa till att täcka driftskostnader.</string>
<string name="chargeprice_donation_dialog_title">Du är en ambitiös prisjämförare!</string>
<string name="chargeprice_donation_dialog_detail">Du använder prisjämförelsefunktionen flitigt. Hjälp gärna till att täcka kostnaderna för denna funktion genom att stödja EVMap med en donation.</string>
<string name="deleted_item">Tog bort “%s”</string>
<string name="deleted_item">Tog bort \"%s\"</string>
<string name="undo">Ångra</string>
<string name="rename">Döp om</string>
<string name="charging_barrierfree">Kan användas utan registrering</string>
@@ -225,7 +225,7 @@
<item quantity="other">%d prisplaner valda</item>
</plurals>
<string name="unknown_operator">Okänd operatör</string>
<string name="data_sources_description">Vänligen välj en datakälla för laddstationer. Det kan ändras senare i inställningarna.</string>
<string name="data_sources_description">Vänligen välj en datakälla för laddstationer. Det kan ändras senare bland inställningarna.</string>
<string name="data_source_goingelectric">GoingElectric.de</string>
<string name="data_source_nobil">NOBIL</string>
<string name="data_source_openstreetmap">OpenStreetMap</string>
@@ -242,7 +242,7 @@
<string name="crash_report_comment_prompt">Du kan skriva en kommentar nedan:</string>
<string name="powered_by_mapbox">tillhandahålls av Mapbox</string>
<string name="pref_search_provider">Sökleverantör</string>
<string name="pref_search_provider_info"><![CDATA[Data för sökningar är dyr att hämta, särskilt från Google Maps. Överväg gärna att donera via OmDonera.]]></string>
<string name="pref_search_provider_info"><![CDATA[Data för sökningar är dyr att hämta, särskilt från Google Maps. Överväg gärna att donera via "Om""Donera".]]></string>
<string name="github_sponsors">GitHub Sponsors</string>
<string name="donate_desc">Stöd EVMaps utveckling med en engångsdonation</string>
<string name="github_sponsors_desc">Stöd EVMap via GitHub Sponsors</string>
@@ -252,7 +252,7 @@
<string name="chargeprice_faq_link">https://ev-map.app/faq/#price-comparison-feature</string>
<string name="referral_link">https://ev-map.app/referrals/</string>
<string name="required">obligatorisk</string>
<string name="edit_filter_profile">Ändra “%s”</string>
<string name="edit_filter_profile">Ändra \"%s\"</string>
<string name="pref_search_delete_recent">Ta bort senaste sökresultaten</string>
<string name="deleted_recent_search_results">Senaste sökresultaten har tagits bort</string>
<string name="settings_data_sources">Datakällor</string>
@@ -349,7 +349,7 @@
<string name="auto_heading">Riktning</string>
<string name="auto_settings">Inställningar</string>
<string name="welcome_android_auto">Android Auto-stöd</string>
<string name="welcome_android_auto_detail">Du kan också använda EVMap i Android Auto om bilen stöder det. Välj då bara EVMap i Android Auto-menyn.</string>
<string name="welcome_android_auto_detail">Du kan också använda EVMap i Android Auto om bilen stöder det. Sök upp och starta EVMap i Android Autos applista.</string>
<string name="sounds_cool">Låter schysst</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap kunde inte avgöra ditt fordons modell.</string>
<string name="auto_chargeprice_vehicle_unknown">Inga valda fordon i appen matchar detta fordon (%1$s %2$s).</string>
@@ -368,7 +368,7 @@
<string name="referral_tesla">Tesla</string>
<string name="generic_connection_error">Kunde inte hämta data</string>
<string name="copied">Kopierat till urklipp</string>
<string name="downloading_chargers_percent">Laddar ner… %.0f%%</string>
<string name="downloading_chargers_percent">Hämtar… %.0f%%</string>
<string name="status_available">Tillgänglig</string>
<string name="status_occupied">Upptagen</string>
<string name="status_charging">Laddar</string>
@@ -385,6 +385,10 @@
<string name="accessibility_employees">Anställda</string>
<string name="accessibility_by_appointment">Efter överenskommelse</string>
<string name="accessibility_residents">Boende</string>
<string name="chargeprice_removal_2025_dialog_detail">Kostnaderna för Chargeprice-data har stigit kraftigt och täcks inte av längre av donationer. Därför kan inte EVMap längre visa denna data direkt i appen. Tillsvidare öppnar detta Chargeprices webbsida. En alternativ lösning är under utveckling, men kommer dröja något och kan introduceras med begränsad funktionalitet. Tack för ditt tålamod och stöd!</string>
<string name="chargeprice_removal_2025_dialog_detail">Kostnaderna för Chargeprice-data har stigit kraftigt och täcks inte längre av donationer. Därför kan inte EVMap fortsätta visa denna data direkt i appen. Tillsvidare öppnar detta Chargeprices webbsida. En alternativ lösning är under utveckling, men kommer dröja något och kan komma att introduceras med begränsad funktionalitet. Tack för ditt tålamod och stöd!</string>
<string name="chargeprice_removal_2025_dialog_title">Ursäkta!</string>
<string name="auto_use_new_map_screen">Ny kartvy (beta)</string>
<string name="welcome_2_title">Full koll på hastigheten</string>
<string name="auto_confirm_delete_profile">Ta bort filterprofilen \"%s\"?</string>
<string name="no_share_app_found">Kunde inte hitta en app att dela med</string>
</resources>
+1 -1
View File
@@ -29,7 +29,7 @@
<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">©20202024 Johan von Forstner and contributors</string>
<string name="copyright_summary">©20202026 Johan von Forstner and contributors</string>
<string name="acra_backend_url" translatable="false">https://acra.muc.vonforst.net/report</string>
<string name="maplibre_attributionsDialogTitle">MapLibre Maps SDK for Android</string>
</resources>
+2
View File
@@ -6,6 +6,7 @@
<string name="no_maps_app_found">Install a navigation app first</string>
<string name="no_browser_app_found">Install a web browser first</string>
<string name="no_email_app_found">Install an email app first</string>
<string name="no_share_app_found">Could not find an app to share with</string>
<string name="address">Address</string>
<string name="operator">Operator</string>
<string name="network">Network</string>
@@ -389,4 +390,5 @@
<string name="chargeprice_removal_2025_dialog_title">Sorry!</string>
<string name="chargeprice_removal_2025_dialog_detail">Costs for Chargeprice data access have risen sharply and cant be covered by donations, so EVMap can no longer show this data directly. For now, this opens the Chargeprice website. An alternative solution is being developed, but itll take time and may start with limited features. Thanks for your patience and support!</string>
<string name="auto_use_new_map_screen">New map screen (beta)</string>
<string name="auto_confirm_delete_profile">Delete filter profile “%s“?</string>
</resources>
@@ -1,79 +0,0 @@
package net.vonforst.evmap.api.availability
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import net.vonforst.evmap.api.goingelectric.GoingElectricApi
import net.vonforst.evmap.model.ChargeLocation
import net.vonforst.evmap.okResponse
import okhttp3.OkHttpClient
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.RecordedRequest
import org.junit.Test
import java.net.HttpURLConnection
class NewMotionAvailabilityDetectorTest {
val api: GoingElectricApi
val webServer = MockWebServer()
val newMotion: NewMotionAvailabilityDetector
init {
webServer.start()
val apikey = ""
val baseurl = webServer.url("/ge/").toString()
api = GoingElectricApi.create(apikey, baseurl)
newMotion = NewMotionAvailabilityDetector(
OkHttpClient.Builder().build(),
webServer.url("/nm/").toString()
)
webServer.dispatcher = object : Dispatcher() {
val notFoundResponse = MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_FOUND)
override fun dispatch(request: RecordedRequest): MockResponse {
val segments = request.requestUrl!!.pathSegments
val urlHead = segments.subList(0, 2).joinToString("/")
when (urlHead) {
"ge/chargepoints" -> {
val id = request.requestUrl!!.queryParameter("ge_id")
return okResponse("/chargers/$id.json")
}
"nm/markers" -> {
val urlTail = segments.subList(2, segments.size).joinToString("/")
val id = when (urlTail) {
"9.56608/9.576080000000001/54.5066/54.516600000000004/22" -> 2105
"9.539283999999999/9.549284/54.471699/54.481699000000006/22" -> 18284
else -> -1
}
return okResponse("/newmotion/$id/markers.json")
}
"nm/locations" -> {
val id = segments.last()
return okResponse("/newmotion/$id.json")
}
else -> return notFoundResponse
}
}
}
}
private fun readResource(s: String) =
NewMotionAvailabilityDetectorTest::class.java.getResource(s)?.readText()
@ExperimentalCoroutinesApi
@Test
fun apiTest() {
for (chargepoint in listOf(2105L, 18284L)) {
val charger = runBlocking { api.getChargepointDetail(chargepoint).body()!! }
.chargelocations!![0].convert("", true) as ChargeLocation
println(charger)
runBlocking {
val result = newMotion.getAvailability(charger)
println(result)
}
}
}
}
+10
View File
@@ -38,6 +38,9 @@ be put into the app in the form of a resource file called `apikeys.xml` under
<string name="nobil_key" translatable="false">
insert your nobil key here
</string>
<string name="evmap_key" translatable="false">
insert your EVMap key here
</string>
</resources>
```
@@ -236,6 +239,13 @@ key and documentation.
If you don't want to test this functionality, simply leave the API key blank.
</details>
### EVMap
EVMap provides APIs to fetch Nobil real-time data.
Contact [EVMap](mailto:evmap@vonforst.net) to get an API key.
Crash reporting
---------------
@@ -0,0 +1,12 @@
Frohe Weihnachten!
Neue Funktionen:
- Neue Echtzeitdatenquelle für Datenquelle Nobil
Verbesserungen:
- Verbesserungen zu Echtzeitdaten von NewMotion
- Automotive: "Über"-Bildschirm nur im geparkten Zustand aufrufbar
Fehler behoben:
- Ablauf von Daten für Nobil korrigiert
- Bearbeiten-Knopf funktionierte nicht für OpenStreetMap
@@ -0,0 +1,8 @@
Verbesserungen:
- Verbesserungen an der Datenquelle Nobil
- Android Auto: Versehentliches Löschen von Filterprofilen vermeiden
Fehler behoben:
- Anzeigefehler nach Schließen der Bildergalerie über Zurück-Button
- Android Auto: Korrekte Sortierung nach Abstand
- Abstürze behoben
@@ -0,0 +1,2 @@
Verbesserungen:
- Verbesserungen bei Echtzeitstatus
@@ -0,0 +1,3 @@
Fehler behoben:
- Android Auto: Scrollposition in der Liste nach Aktualisierung beibehalten
- Tesla Login funktionierte nicht
@@ -0,0 +1,12 @@
Merry Christmas!
New features:
- New realtime data source for charger data source Nobil
Improvements:
- Improved realtime data from NewMotion
- Automotive: About screen only available in parked state
Bugfixes:
- Fixed data expiry for Nobil
- Edit charger button did not work for OpenStreetMap
@@ -0,0 +1,8 @@
Improvements:
- Improvements to Nobil data source
- Android Auto: Avoid accidental deletion of filter profiles
Bugfixes:
- Display error after closing photo gallery using back button
- Android Auto: Fix sorting of charger list by distance
- Fixed crashes
@@ -0,0 +1,2 @@
Improvements:
- Improvements to realtime status
@@ -0,0 +1,3 @@
Bugfixes:
- Android Auto: Keep scroll position after refresh
- Tesla login was not working
@@ -1,4 +1,4 @@
Med EVMap hittar du enkelt laddare för elfordon med hjälp av din Android-telefon. Du får tillgång till data från GoingElectric.de och Open Charge Map som innehåller information om laddstationer över hela världen. För många laddstationer i Europa finns realtidsstatus.
Med EVMap hittar du enkelt laddare till elfordon med hjälp av din Android-telefon. Du får tillgång till data från GoingElectric.de och Open Charge Map som innehåller information om laddstationer över hela världen. För många laddstationer i Europa finns realtidsstatus.
Funktioner:
- Material Design
@@ -1 +1 @@
Hitta laddstationer för elfordon
Hitta laddstationer till elfordon