mirror of
https://github.com/ev-map/EVMap.git
synced 2025-12-25 08:07:46 -05:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b374cda1c | ||
|
|
b82f6f68fb | ||
|
|
8e19399aaa | ||
|
|
e315da926e | ||
|
|
9450230856 | ||
|
|
81d62860e2 | ||
|
|
e825654b9c | ||
|
|
0a4878a129 | ||
|
|
af50a95abd | ||
|
|
fe58551de9 | ||
|
|
abc85c136b | ||
|
|
0ad4691d30 | ||
|
|
d85a64ec77 | ||
|
|
52fefb564a | ||
|
|
d18b2e26b8 | ||
|
|
d5f55366a9 | ||
|
|
2f93e92b57 | ||
|
|
24e5d072d6 | ||
|
|
a9e9055671 | ||
|
|
53ab8dc4e8 | ||
|
|
c0d7d59817 | ||
|
|
42cfdfee1d | ||
|
|
41cb6cf6b0 | ||
|
|
64f50cc5e6 | ||
|
|
c24c03bb32 | ||
|
|
bec25dd4d2 | ||
|
|
4e4c5a0e9a | ||
|
|
17bd7f024e | ||
|
|
fc5e77b01a | ||
|
|
6a114fc2ea | ||
|
|
6e32c6644c | ||
|
|
8fb34ae66f | ||
|
|
ae3489621e | ||
|
|
ff0a110f51 | ||
|
|
24ef4888a8 | ||
|
|
13916b0c8d | ||
|
|
efdd0d6bc5 | ||
|
|
155aca0041 | ||
|
|
6393eadc81 | ||
|
|
4319ece4f3 | ||
|
|
62f2002e5c |
21
README.md
21
README.md
@@ -42,12 +42,23 @@ EVMap uses and put them into the app in the form of a resource file called `apik
|
||||
features and how they can be obtained in our [documentation page](doc/api_keys.md).
|
||||
|
||||
There are three different build flavors, `googleNormal`, `fossNormal` and `googleAutomotive`.
|
||||
- The `foss` variant only uses Mapbox data and should run on most Android devices, even without Google Play Services.
|
||||
- The `foss` variant only uses Mapbox data and should run on most Android devices, even without
|
||||
Google Play Services.
|
||||
- The `google` variants also include access to Google Maps data.
|
||||
- `googleNormal` is intended to run on smartphones and tablets, and also includes the Android Auto app for use
|
||||
on the car display.
|
||||
- `googleAutomotive` variant is intended to be installed directly on car infotainment systems using the
|
||||
Google-flavored Android Automotive OS. It does not provide the usual smartphone UI.
|
||||
- `googleNormal` is intended to run on smartphones and tablets, and also includes the Android
|
||||
Auto app for use on the car display.
|
||||
- `googleAutomotive` variant is intended to be installed directly on car infotainment systems
|
||||
using the Google-flavored Android Automotive OS. It does not provide the usual smartphone UI.
|
||||
|
||||
We also have a special [documentation page](doc/android_auto.md) on how to test the Android Auto
|
||||
app.
|
||||
|
||||
Translations
|
||||
------------
|
||||
|
||||
You can use our [Weblate page](https://hosted.weblate.org/projects/evmap/) to help translate EVMap
|
||||
into new languages.
|
||||
|
||||
<a href="https://hosted.weblate.org/engage/evmap/">
|
||||
<img src="https://hosted.weblate.org/widgets/evmap/-/open-graph.png" width="500" alt="Translation status" />
|
||||
</a>
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||
viewBox="0 0 233.8 368.4" style="enable-background:new 0 0 233.8 368.4;" xml:space="preserve">
|
||||
viewBox="0 0 233.8 368.4" style="enable-background:new 0 0 233.8 368.4;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{display:none;}
|
||||
.st2{display:inline;fill:#802C27;}
|
||||
.st3{fill:#808080;}
|
||||
.st4{display:none;fill:#802C27;}
|
||||
.st1{fill:#808080;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M109.8,0h13.6c33.9,1.9,67.1,18.5,87.7,45.8c13.5,17.2,21,38.6,22.7,60.3v8.1c-0.8,42.1-27.7,76.6-51,109.4
|
||||
c-26.2,37-50.4,77.3-57.1,122.9c-1.8,7.7,0.4,18.5-8.9,22c-2.2-1.7-4.7-3.1-6.2-5.4c-2.7-25.5-9.1-50.7-20-73.9
|
||||
c-12.3-27.1-29.5-51.6-47-75.6C33,199,23,184.2,14.7,168.3c-13-23.8-17.9-51.9-12.5-78.6c4.4-21.1,15.4-40.6,30.6-55.7
|
||||
C53.3,14,81.1,1.8,109.8,0z" />
|
||||
</g>
|
||||
</g>
|
||||
<g class="st1">
|
||||
<path class="st2" d="M107.2,74.1c18.9-4.8,40.4,5.5,47.7,23.7c6.1,14.5,1.9,32.5-9.9,42.9c-12.6,11.5-32.4,14-47.5,6
|
||||
c-13.9-6.8-23-22.6-21.3-38.1C77.6,92,91.1,77.7,107.2,74.1z" />
|
||||
</g>
|
||||
<path class="st0" d="M117,367.4c-0.4-0.3-0.8-0.6-1.2-0.9c-1.6-1.2-3.1-2.3-4.2-3.7c-2.9-26.9-9.6-51.7-20.1-74
|
||||
c-12.4-27.3-30.1-52.4-47.1-75.8c-8.7-12-19.8-27.9-28.8-45.2C2.3,143.6-2.1,115.9,3.2,89.9c4.3-20.4,15-40,30.3-55.2
|
||||
C53.6,15.1,81.5,2.8,109.9,1l13.5,0c34.4,1.9,66.9,18.9,86.9,45.4c12.8,16.3,20.8,37.5,22.5,59.8l0,8
|
||||
c-0.7,38.8-23.7,70.9-45.9,101.9c-1.7,2.3-3.3,4.6-5,6.9c-24.4,34.5-50.3,76.1-57.3,123.3c-0.5,2-0.7,4.3-0.9,6.5
|
||||
C123.3,359,122.8,364.9,117,367.4z" />
|
||||
<path class="st1" d="M123.3,2c34.1,1.9,66.3,18.8,86.2,45c12.6,16.1,20.5,37.1,22.3,59.1l0,8c-0.7,38.5-23.6,70.5-45.7,101.3
|
||||
c-1.7,2.3-3.3,4.6-5,6.9c-24.5,34.6-50.5,76.3-57.4,123.7c-0.5,2.1-0.7,4.4-0.9,6.7c-0.5,5.9-1,11-5.8,13.4
|
||||
c-0.2-0.2-0.5-0.4-0.7-0.5c-1.5-1.1-2.9-2-3.8-3.3c-2.9-26.9-9.7-51.8-20.1-74C80,261,62.3,235.8,45.2,212.4
|
||||
c-8.7-11.9-19.8-27.8-28.8-45.1C3.3,143.3-1,115.9,4.2,90.1c4.2-20.2,14.9-39.6,30-54.7C54.2,16,81.7,3.8,109.9,2H123.3 M123.4,0
|
||||
h-13.6c-28.7,1.8-56.5,14-77,34C17.6,49.1,6.6,68.6,2.2,89.7c-5.4,26.7-0.5,54.8,12.5,78.6C23,184.2,33,199,43.6,213.6
|
||||
c17.5,24,34.7,48.5,47,75.6c10.9,23.2,17.3,48.4,20,73.9c1.5,2.3,4,3.7,6.2,5.4c9.3-3.5,7.1-14.3,8.9-22
|
||||
c6.7-45.6,30.9-85.9,57.1-122.9c23.3-32.8,50.2-67.3,51-109.4v-8.1c-1.7-21.7-9.2-43.1-22.7-60.3C190.5,18.5,157.3,1.9,123.4,0
|
||||
L123.4,0z" />
|
||||
</g>
|
||||
<path class="st3" d="M90.9,57.3v68.2h18.6v55.8l43.4-74.4h-24.8l24.8-49.6H90.9z" />
|
||||
<path class="st4" d="M159,85.3L159,85.3l-20.8-20.9l-5.9,5.9l11.8,11.8c-5.3,2-9,7.1-9,13.1c0,7.7,6.3,14,14,14c2,0,3.9-0.4,5.6-1.2
|
||||
v40.4c0,3.1-2.5,5.6-5.6,5.6s-5.6-2.5-5.6-5.6v-25.2c0-6.2-5-11.2-11.2-11.2h-5.6V72.8c0-6.2-5-11.2-11.2-11.2H81.8
|
||||
c-6.2,0-11.2,5-11.2,11.2v89.7h56.1v-42.1h8.4v28c0,7.7,6.3,14,14,14s14-6.3,14-14V95.2C163.1,91.3,161.6,87.8,159,85.3
|
||||
M149.1,100.8c-3.1,0-5.6-2.5-5.6-5.6c0-3.1,2.5-5.6,5.6-5.6s5.6,2.5,5.6,5.6C154.7,98.3,152.2,100.8,149.1,100.8 M93.1,145.6v-25.2
|
||||
H81.8l22.4-42.1v28h11.2L93.1,145.6z" />
|
||||
<path class="st1"
|
||||
d="M90.9,57.3v68.2h18.6v55.8l43.4-74.4h-24.8l24.8-49.6C152.9,57.3,90.9,57.3,90.9,57.3z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -1,27 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||
viewBox="0 0 233.8 368.4" style="enable-background:new 0 0 233.8 368.4;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#B5B5B5;}
|
||||
.st2{fill:#808080;}
|
||||
.st1{fill:#808080;}
|
||||
.st2{fill:#B5B5B5;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M109.8,0h13.6c33.9,1.9,67.1,18.5,87.7,45.8c13.5,17.2,21,38.6,22.7,60.3v8.1c-0.8,42.1-27.7,76.6-51,109.4
|
||||
c-26.2,37-50.4,77.3-57.1,122.9c-1.8,7.7,0.4,18.5-8.9,22c-2.2-1.7-4.7-3.1-6.2-5.4c-2.7-25.5-9.1-50.7-20-73.9
|
||||
c-12.3-27.1-29.5-51.6-47-75.6C33,199,23,184.2,14.7,168.3c-13-23.8-17.9-51.9-12.5-78.6C6.6,68.6,17.6,49.1,32.8,34
|
||||
C53.3,14,81.1,1.8,109.8,0z" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<polygon class="st1"
|
||||
points="143.2,109.4 123.5,143.2 123.5,181.3 166.9,106.9 144.7,106.9 " />
|
||||
<path class="st1"
|
||||
d="M122.2,101.9h16.7h5.7l22.3-44.6c0,0-10.2,0-22.4,0l-1.1,2.2L122.2,101.9z" />
|
||||
<path class="st2" d="M138.9,57.3c-9.7,0-19.8,0-26.4,0c-2.5,0-5.1,0-7.6,0c-8.2,0-16.1,0-21.4,0c-4.1,0-6.6,0-6.6,0v68.2h18.6v55.8
|
||||
l43.4-74.4h-24.8L138.9,57.3z" />
|
||||
<path class="st0" d="M117,367.4c-0.4-0.3-0.8-0.6-1.2-0.9c-1.6-1.2-3.1-2.3-4.2-3.7c-2.9-26.9-9.6-51.7-20.1-74
|
||||
c-12.4-27.3-30.1-52.4-47.1-75.8c-8.7-12-19.8-27.9-28.8-45.2C2.3,143.6-2.1,115.9,3.2,89.9c4.3-20.4,15-40,30.3-55.2
|
||||
C53.6,15.1,81.5,2.8,109.9,1l13.5,0c34.4,1.9,66.9,18.9,86.9,45.4c12.8,16.3,20.8,37.5,22.5,59.8l0,8
|
||||
c-0.7,38.8-23.7,70.9-45.9,101.9c-1.7,2.3-3.3,4.6-5,6.9c-24.4,34.5-50.3,76.1-57.3,123.3c-0.5,2-0.7,4.3-0.9,6.5
|
||||
C123.3,359,122.8,364.9,117,367.4z" />
|
||||
<path class="st1" d="M123.3,2c34.1,1.9,66.3,18.8,86.2,45c12.6,16.1,20.5,37.1,22.3,59.1l0,8c-0.7,38.5-23.6,70.5-45.7,101.3
|
||||
c-1.7,2.3-3.3,4.6-5,6.9c-24.5,34.6-50.5,76.3-57.4,123.7c-0.5,2.1-0.7,4.4-0.9,6.7c-0.5,5.9-1,11-5.8,13.4
|
||||
c-0.2-0.2-0.5-0.4-0.7-0.5c-1.5-1.1-2.9-2-3.8-3.3c-2.9-26.9-9.7-51.8-20.1-74C80,261,62.3,235.8,45.2,212.4
|
||||
c-8.7-11.9-19.8-27.8-28.8-45.1C3.3,143.3-1,115.9,4.2,90.1c4.2-20.2,14.9-39.6,30-54.7C54.2,16,81.7,3.8,109.9,2H123.3 M123.4,0
|
||||
h-13.6c-28.7,1.8-56.5,14-77,34C17.6,49.1,6.6,68.6,2.2,89.7c-5.4,26.7-0.5,54.8,12.5,78.6C23,184.2,33,199,43.6,213.6
|
||||
c17.5,24,34.7,48.5,47,75.6c10.9,23.2,17.3,48.4,20,73.9c1.5,2.3,4,3.7,6.2,5.4c9.3-3.5,7.1-14.3,8.9-22
|
||||
c6.7-45.6,30.9-85.9,57.1-122.9c23.3-32.8,50.2-67.3,51-109.4v-8.1c-1.7-21.7-9.2-43.1-22.7-60.3C190.5,18.5,157.3,1.9,123.4,0
|
||||
L123.4,0z" />
|
||||
</g>
|
||||
<polygon class="st2" points="143.2,109.4 123.5,143.2 123.5,181.3 166.9,106.9 144.7,106.9 " />
|
||||
<path class="st2" d="M122.2,101.9h16.7h5.7l22.3-44.6c0,0-10.2,0-22.4,0l-1.1,2.2L122.2,101.9z" />
|
||||
<path class="st1" d="M138.9,57.3c-9.7,0-19.8,0-26.4,0c-2.5,0-5.1,0-7.6,0c-8.2,0-16.1,0-21.4,0c-4.1,0-6.6,0-6.6,0v68.2h18.6v55.8
|
||||
l43.4-74.4h-24.8L138.9,57.3z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.0 KiB |
@@ -19,8 +19,8 @@ android {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
// NOTE: always increase versionCode by 2 since automotive flavor uses versionCode + 1
|
||||
versionCode 98
|
||||
versionName "1.3.7"
|
||||
versionCode 104
|
||||
versionName "1.3.9"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -158,15 +158,17 @@ dependencies {
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation 'androidx.browser:browser:1.4.0'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
implementation 'com.github.johan12345:CustomBottomSheetBehavior:f69f532660'
|
||||
implementation 'com.github.johan12345:CustomBottomSheetBehavior:dd0167dbff'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.9.0'
|
||||
implementation 'com.squareup.moshi:moshi-kotlin:1.13.0'
|
||||
implementation 'com.squareup.moshi:moshi-adapters:1.13.0'
|
||||
implementation 'moe.banana:moshi-jsonapi:3.5.0'
|
||||
implementation 'moe.banana:moshi-jsonapi-retrofit-converter:3.5.0'
|
||||
implementation 'com.github.johan12345:jsonapi:50d72e7e55' // patched version for jsonapi-adapters
|
||||
implementation('com.markomilos.jsonapi:jsonapi-retrofit:1.0.1') {
|
||||
exclude group: 'com.markomilos.jsonapi', module: 'jsonapi-adapters'
|
||||
}
|
||||
implementation 'io.coil-kt:coil:1.1.0'
|
||||
implementation 'com.github.johan12345:StfalconImageViewer:5082ebd392'
|
||||
implementation "com.mikepenz:aboutlibraries-core:$about_libs_version"
|
||||
@@ -184,7 +186,7 @@ dependencies {
|
||||
googleAutomotiveImplementation "androidx.car.app:app-automotive:$carAppVersion"
|
||||
|
||||
// AnyMaps
|
||||
def anyMapsVersion = 'f36bb3c126'
|
||||
def anyMapsVersion = 'a9b3dd7d99'
|
||||
implementation "com.github.johan12345.AnyMaps:anymaps-base:$anyMapsVersion"
|
||||
googleImplementation "com.github.johan12345.AnyMaps:anymaps-google:$anyMapsVersion"
|
||||
googleImplementation 'com.google.android.gms:play-services-maps:18.1.0'
|
||||
|
||||
5
app/src/debug/res/values/donottranslate.xml
Normal file
5
app/src/debug/res/values/donottranslate.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="chargeprice_api_url">https://staging-api.chargeprice.app/v1/</string>
|
||||
<string name="chargeprice_key">20c0d68918c9dc96c564784b711a6570</string>
|
||||
</resources>
|
||||
@@ -1,14 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_map_provider_names">
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string-array name="pref_search_provider_names">
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string-array name="pref_search_provider_values" tranlatable="false">
|
||||
<item>mapbox</item>
|
||||
</string-array>
|
||||
<string name="donations_info" formatted="false">Findest du EVMap nützlich? Unterstütze die Weiterentwicklung der App mit einer Spende an den Entwickler.</string>
|
||||
<string name="donate_paypal">Mit PayPal spenden</string>
|
||||
<string name="data_sources_hint">Die Kartendaten für die App stammen von OpenStreetMap (Mapbox).</string>
|
||||
6
app/src/foss/res/values-fr/strings.xml
Normal file
6
app/src/foss/res/values-fr/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="donations_info" formatted="false">Trouvez-vous EVMap utile \? Soutenez son développement en envoyant un don au développeur.</string>
|
||||
<string name="data_sources_hint">Les données cartographiques de l\'application sont fournies par OpenStreetMap (Mapbox).</string>
|
||||
<string name="donate_paypal">Faire un don avec PayPal</string>
|
||||
</resources>
|
||||
6
app/src/foss/res/values-nb-rNO/strings.xml
Normal file
6
app/src/foss/res/values-nb-rNO/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="donate_paypal">Doner med PayPal</string>
|
||||
<string name="data_sources_hint">Kartdata i programmet tilbys av OpenStreetMap (Mapbox).</string>
|
||||
<string name="donations_info" formatted="false">Synes du EVMap er nyttig\? Støtt utviklingen ved å sende en slant til utvikleren.</string>
|
||||
</resources>
|
||||
15
app/src/foss/res/values/arrays.xml
Normal file
15
app/src/foss/res/values/arrays.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_map_provider_names">
|
||||
<item>@string/pref_provider_osm_mapbox</item>
|
||||
</string-array>
|
||||
<string-array name="pref_map_provider_values" translatable="false">
|
||||
<item>mapbox</item>
|
||||
</string-array>
|
||||
<string-array name="pref_search_provider_names">
|
||||
<item>@string/pref_provider_osm_mapbox</item>
|
||||
</string-array>
|
||||
<string-array name="pref_search_provider_values" translatable="false">
|
||||
<item>mapbox</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
6
app/src/foss/res/values/donottranslate.xml
Normal file
6
app/src/foss/res/values/donottranslate.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pref_search_provider_default" translatable="false">mapbox</string>
|
||||
<string name="pref_map_provider_default" translatable="false">mapbox</string>
|
||||
<string name="paypal_link" translatable="false">https://paypal.me/johan98</string>
|
||||
</resources>
|
||||
6
app/src/foss/res/values/strings.xml
Normal file
6
app/src/foss/res/values/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="donations_info" formatted="false">Do you find EVMap useful? Support its development by sending a donation to the developer.</string>
|
||||
<string name="donate_paypal">Donate with PayPal</string>
|
||||
<string name="data_sources_hint">Map data in the app is provided by OpenStreetMap (Mapbox).</string>
|
||||
</resources>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_map_provider_names">
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string-array name="pref_map_provider_values" tranlatable="false">
|
||||
<item>mapbox</item>
|
||||
</string-array>
|
||||
<string-array name="pref_search_provider_names">
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string-array name="pref_search_provider_values" tranlatable="false">
|
||||
<item>mapbox</item>
|
||||
</string-array>
|
||||
<string name="pref_search_provider_default" translatable="false">mapbox</string>
|
||||
<string name="pref_map_provider_default" translatable="false">mapbox</string>
|
||||
<string name="donations_info" formatted="false">Do you find EVMap useful? Support its development by sending a donation to the developer.</string>
|
||||
<string name="donate_paypal">Donate with PayPal</string>
|
||||
<string name="paypal_link" translatable="false">https://paypal.me/johan98</string>
|
||||
<string name="data_sources_hint">Map data in the app is provided by OpenStreetMap (Mapbox).</string>
|
||||
</resources>
|
||||
@@ -5,12 +5,9 @@ import android.annotation.SuppressLint
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.location.Criteria
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresPermission
|
||||
@@ -31,6 +28,9 @@ import androidx.core.location.LocationListenerCompat
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.location.FusionEngine
|
||||
import net.vonforst.evmap.location.LocationEngine
|
||||
import net.vonforst.evmap.location.Priority
|
||||
import net.vonforst.evmap.utils.checkFineLocationPermission
|
||||
|
||||
|
||||
@@ -103,8 +103,8 @@ class EVMapSession(val cas: CarAppService) : Session(), DefaultLifecycleObserver
|
||||
location?.let { value?.updateLocation(it) }
|
||||
}
|
||||
private var location: Location? = null
|
||||
private val locationManager: LocationManager by lazy {
|
||||
carContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
private val locationEngine: LocationEngine by lazy {
|
||||
FusionEngine(carContext)
|
||||
}
|
||||
|
||||
private val hardwareMan: CarHardwareManager by lazy {
|
||||
@@ -179,16 +179,11 @@ class EVMapSession(val cas: CarAppService) : Session(), DefaultLifecycleObserver
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
|
||||
private fun requestPhoneLocationUpdates() {
|
||||
val provider = locationManager.getBestProvider(Criteria().apply {
|
||||
accuracy = Criteria.ACCURACY_FINE
|
||||
}, true) ?: return
|
||||
|
||||
val location = locationManager.getLastKnownLocation(provider)
|
||||
val location = locationEngine.getLastKnownLocation()
|
||||
updateLocation(location)
|
||||
locationManager.requestLocationUpdates(
|
||||
provider,
|
||||
locationEngine.requestLocationUpdates(
|
||||
Priority.HIGH_ACCURACY,
|
||||
1000,
|
||||
1f,
|
||||
phoneLocationListener
|
||||
)
|
||||
}
|
||||
@@ -209,7 +204,7 @@ class EVMapSession(val cas: CarAppService) : Session(), DefaultLifecycleObserver
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
|
||||
private fun removePhoneLocationUpdates() {
|
||||
locationManager.removeUpdates(phoneLocationListener)
|
||||
locationEngine.removeUpdates(phoneLocationListener)
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
|
||||
@@ -15,13 +15,13 @@ import androidx.car.app.model.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import jsonapi.Meta
|
||||
import jsonapi.Relationship
|
||||
import jsonapi.Relationships
|
||||
import jsonapi.ResourceIdentifier
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import moe.banana.jsonapi2.HasMany
|
||||
import moe.banana.jsonapi2.HasOne
|
||||
import moe.banana.jsonapi2.JsonBuffer
|
||||
import moe.banana.jsonapi2.ResourceIdentifier
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.api.chargeprice.*
|
||||
import net.vonforst.evmap.model.ChargeLocation
|
||||
@@ -34,7 +34,10 @@ class ChargepriceScreen(ctx: CarContext, val charger: ChargeLocation) : Screen(c
|
||||
private val prefs = PreferenceDataSource(ctx)
|
||||
private val db = AppDatabase.getInstance(carContext)
|
||||
private val api by lazy {
|
||||
ChargepriceApi.create(carContext.getString(R.string.chargeprice_key))
|
||||
ChargepriceApi.create(
|
||||
carContext.getString(R.string.chargeprice_key),
|
||||
carContext.getString(R.string.chargeprice_api_url)
|
||||
)
|
||||
}
|
||||
private var prices: List<ChargePrice>? = null
|
||||
private var meta: ChargepriceChargepointMeta? = null
|
||||
@@ -94,7 +97,7 @@ class ChargepriceScreen(ctx: CarContext, val charger: ChargeLocation) : Screen(c
|
||||
)
|
||||
.build().intent
|
||||
intent.data =
|
||||
Uri.parse("https://www.chargeprice.app/?poi_id=${charger.id}&poi_source=${getDataAdapter()}")
|
||||
Uri.parse(ChargepriceApi.getPoiUrl(charger))
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
carContext.startActivity(intent)
|
||||
@@ -169,39 +172,44 @@ class ChargepriceScreen(ctx: CarContext, val charger: ChargeLocation) : Screen(c
|
||||
}
|
||||
|
||||
private fun loadPrices(model: Model?) {
|
||||
val dataAdapter = getDataAdapter() ?: return
|
||||
val dataAdapter = ChargepriceApi.getDataAdapter(charger) ?: return
|
||||
val manufacturer = model?.manufacturer?.value
|
||||
val modelName = getVehicleModel(model?.manufacturer?.value, model?.name?.value)
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val car = determineVehicle(manufacturer, modelName)
|
||||
val cpStation = ChargepriceStation.fromEvmap(charger, car.compatibleEvmapConnectors)
|
||||
val result = api.getChargePrices(ChargepriceRequest().apply {
|
||||
this.dataAdapter = dataAdapter
|
||||
station = cpStation
|
||||
vehicle = HasOne(car)
|
||||
tariffs = if (!prefs.chargepriceMyTariffsAll) {
|
||||
val myTariffs = prefs.chargepriceMyTariffs ?: emptySet()
|
||||
HasMany<ChargepriceTariff>(*myTariffs.map {
|
||||
ResourceIdentifier(
|
||||
"tariff",
|
||||
it
|
||||
val result = api.getChargePrices(
|
||||
ChargepriceRequest(
|
||||
dataAdapter = dataAdapter,
|
||||
station = cpStation,
|
||||
vehicle = car,
|
||||
options = ChargepriceOptions(
|
||||
batteryRange = batteryRange.map { it.toDouble() },
|
||||
providerCustomerTariffs = prefs.chargepriceShowProviderCustomerTariffs,
|
||||
maxMonthlyFees = if (prefs.chargepriceNoBaseFee) 0.0 else null,
|
||||
currency = prefs.chargepriceCurrency,
|
||||
allowUnbalancedLoad = prefs.chargepriceAllowUnbalancedLoad
|
||||
),
|
||||
relationships = if (!prefs.chargepriceMyTariffsAll) {
|
||||
val myTariffs = prefs.chargepriceMyTariffs ?: emptySet()
|
||||
Relationships(
|
||||
"tariffs" to Relationship.ToMany(
|
||||
myTariffs.map {
|
||||
ResourceIdentifier(
|
||||
"tariff",
|
||||
id = it
|
||||
)
|
||||
},
|
||||
meta = Meta.from(
|
||||
ChargepriceRequestTariffMeta(ChargepriceInclude.ALWAYS),
|
||||
ChargepriceApi.moshi
|
||||
)
|
||||
)
|
||||
)
|
||||
}.toTypedArray()).apply {
|
||||
meta = JsonBuffer.create(
|
||||
ChargepriceApi.moshi.adapter(ChargepriceRequestTariffMeta::class.java),
|
||||
ChargepriceRequestTariffMeta(ChargepriceInclude.ALWAYS)
|
||||
)
|
||||
}
|
||||
} else null
|
||||
options = ChargepriceOptions(
|
||||
batteryRange = batteryRange.map { it.toDouble() },
|
||||
providerCustomerTariffs = prefs.chargepriceShowProviderCustomerTariffs,
|
||||
maxMonthlyFees = if (prefs.chargepriceNoBaseFee) 0.0 else null,
|
||||
currency = prefs.chargepriceCurrency,
|
||||
allowUnbalancedLoad = prefs.chargepriceAllowUnbalancedLoad
|
||||
)
|
||||
}, ChargepriceApi.getChargepriceLanguage())
|
||||
} else null
|
||||
), ChargepriceApi.getChargepriceLanguage()
|
||||
)
|
||||
|
||||
val myTariffs = prefs.chargepriceMyTariffs
|
||||
|
||||
@@ -215,14 +223,16 @@ class ChargepriceScreen(ctx: CarContext, val charger: ChargeLocation) : Screen(c
|
||||
invalidate()
|
||||
return@launch
|
||||
}
|
||||
meta =
|
||||
(result.meta.get<ChargepriceMeta>(ChargepriceApi.moshi.adapter(ChargepriceMeta::class.java)) as ChargepriceMeta).chargePoints.filterIndexed { i, cp ->
|
||||
charger.chargepointsMerged[i].type in car.compatibleEvmapConnectors
|
||||
}.maxByOrNull {
|
||||
it.power
|
||||
}
|
||||
|
||||
prices = result.map { cp ->
|
||||
val metaMapped =
|
||||
result.meta!!.map(ChargepriceMeta::class.java, ChargepriceApi.moshi)!!
|
||||
meta = metaMapped.chargePoints.filterIndexed { i, cp ->
|
||||
charger.chargepointsMerged[i].type in car.compatibleEvmapConnectors
|
||||
}.maxByOrNull {
|
||||
it.power
|
||||
}
|
||||
|
||||
prices = result.data!!.map { cp ->
|
||||
val filteredPrices =
|
||||
cp.chargepointPrices.filter {
|
||||
it.plug == chargepoint.plug && it.power == chargepoint.power
|
||||
@@ -230,15 +240,15 @@ class ChargepriceScreen(ctx: CarContext, val charger: ChargeLocation) : Screen(c
|
||||
if (filteredPrices.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
cp.clone().apply {
|
||||
cp.copy(
|
||||
chargepointPrices = filteredPrices
|
||||
}
|
||||
)
|
||||
}
|
||||
}.filterNotNull()
|
||||
.sortedBy { it.chargepointPrices.first().price }
|
||||
.sortedByDescending {
|
||||
prefs.chargepriceMyTariffsAll ||
|
||||
myTariffs != null && it.tariff?.get()?.id in myTariffs
|
||||
myTariffs != null && it.tariffId in myTariffs
|
||||
}
|
||||
invalidate()
|
||||
} catch (e: IOException) {
|
||||
@@ -316,10 +326,4 @@ class ChargepriceScreen(ctx: CarContext, val charger: ChargeLocation) : Screen(c
|
||||
}
|
||||
return vehicles[0]
|
||||
}
|
||||
|
||||
private fun getDataAdapter(): String? = when (charger.dataSource) {
|
||||
"goingelectric" -> ChargepriceApi.DATA_SOURCE_GOINGELECTRIC
|
||||
"openchargemap" -> ChargepriceApi.DATA_SOURCE_OPENCHARGEMAP
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -373,10 +373,8 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
|
||||
|
||||
// Reloading chargers in onStart does not seem to count towards content limit.
|
||||
// So let's do this so the user gets fresh chargers when re-entering the app.
|
||||
chargers = null
|
||||
availabilities.clear()
|
||||
invalidate()
|
||||
filtersWithValue.observe(this) {
|
||||
filtersWithValue.observe(this@MapScreen) {
|
||||
loadChargers()
|
||||
}
|
||||
}
|
||||
@@ -398,6 +396,12 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
|
||||
}
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
// Reloading chargers in onStart does not seem to count towards content limit.
|
||||
// So let's do this so the user gets fresh chargers when re-entering the app.
|
||||
// Deleting the data already in onStop makes sure that we show a loading screen directly
|
||||
// (i.e. onGetTemplate is not called while the old data is still there)
|
||||
chargers = null
|
||||
availabilities.clear()
|
||||
removeListeners()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.vonforst.evmap.auto
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.car.app.CarContext
|
||||
import androidx.car.app.CarToast
|
||||
import androidx.car.app.Screen
|
||||
@@ -107,6 +108,7 @@ class DataSettingsScreen(ctx: CarContext) : Screen(ctx) {
|
||||
screenManager.push(
|
||||
ChooseDataSourceScreen(
|
||||
carContext,
|
||||
R.string.pref_data_source,
|
||||
dataSourceNames,
|
||||
dataSourceValues,
|
||||
prefs.dataSource,
|
||||
@@ -127,6 +129,7 @@ class DataSettingsScreen(ctx: CarContext) : Screen(ctx) {
|
||||
screenManager.push(
|
||||
ChooseDataSourceScreen(
|
||||
carContext,
|
||||
R.string.pref_search_provider,
|
||||
searchProviderNames,
|
||||
searchProviderValues,
|
||||
prefs.searchProvider
|
||||
@@ -155,6 +158,7 @@ class DataSettingsScreen(ctx: CarContext) : Screen(ctx) {
|
||||
|
||||
class ChooseDataSourceScreen(
|
||||
ctx: CarContext,
|
||||
@StringRes val title: Int,
|
||||
val names: Array<String>,
|
||||
val values: Array<String>,
|
||||
val currentValue: String,
|
||||
@@ -165,7 +169,7 @@ class ChooseDataSourceScreen(
|
||||
|
||||
override fun onGetTemplate(): Template {
|
||||
return ListTemplate.Builder().apply {
|
||||
setTitle(carContext.getString(R.string.pref_data_source))
|
||||
setTitle(carContext.getString(title))
|
||||
setHeaderAction(Action.BACK)
|
||||
setSingleList(ItemList.Builder().apply {
|
||||
for (i in names.indices) {
|
||||
@@ -218,7 +222,10 @@ class ChargepriceSettingsScreen(ctx: CarContext) : Screen(ctx) {
|
||||
R.plurals.chargeprice_some_tariffs_selected,
|
||||
n,
|
||||
n
|
||||
) + "\n" + carContext.getString(R.string.pref_my_tariffs_summary)
|
||||
) + "\n" + carContext.resources.getQuantityString(
|
||||
R.plurals.pref_my_tariffs_summary,
|
||||
n
|
||||
)
|
||||
}
|
||||
)
|
||||
}.build())
|
||||
@@ -283,7 +290,10 @@ class ChargepriceSettingsScreen(ctx: CarContext) : Screen(ctx) {
|
||||
|
||||
class SelectVehiclesScreen(ctx: CarContext) : MultiSelectSearchScreen<ChargepriceCar>(ctx) {
|
||||
private val prefs = PreferenceDataSource(carContext)
|
||||
private var api = ChargepriceApi.create(carContext.getString(R.string.chargeprice_key))
|
||||
private var api = ChargepriceApi.create(
|
||||
carContext.getString(R.string.chargeprice_key),
|
||||
carContext.getString(R.string.chargeprice_api_url)
|
||||
)
|
||||
override val isMultiSelect = true
|
||||
override val shouldShowSelectAll = false
|
||||
|
||||
@@ -308,7 +318,10 @@ class SelectVehiclesScreen(ctx: CarContext) : MultiSelectSearchScreen<Chargepric
|
||||
|
||||
class SelectTariffsScreen(ctx: CarContext) : MultiSelectSearchScreen<ChargepriceTariff>(ctx) {
|
||||
private val prefs = PreferenceDataSource(carContext)
|
||||
private var api = ChargepriceApi.create(carContext.getString(R.string.chargeprice_key))
|
||||
private var api = ChargepriceApi.create(
|
||||
carContext.getString(R.string.chargeprice_key),
|
||||
carContext.getString(R.string.chargeprice_api_url)
|
||||
)
|
||||
override val isMultiSelect = true
|
||||
override val shouldShowSelectAll = true
|
||||
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_map_provider_names">
|
||||
<item>Google Maps</item>
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string-array name="pref_search_provider_names">
|
||||
<item>Google Maps</item>
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string name="donations_info" formatted="false">Findest du EVMap nützlich? Unterstütze die Weiterentwicklung der App mit einer Spende an den Entwickler.\n\nGoogle zieht von der Spende 15% Gebühren ab.</string>
|
||||
<string name="auto_location_service">EVMap läuft unter Android Auto und nutzt dafür deinen Standort.</string>
|
||||
<string name="auto_no_chargers_found">Keine Ladestationen in der Nähe gefunden</string>
|
||||
37
app/src/google/res/values-fr/strings.xml
Normal file
37
app/src/google/res/values-fr/strings.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="donations_info" formatted="false">Trouvez-vous EVMap utile \? Soutenez son développement en envoyant un don au développeur.
|
||||
\n
|
||||
\nGoogle prend 15% sur chaque don.</string>
|
||||
<string name="auto_location_service">EVMap fonctionne sur Android Auto et utilise votre position.</string>
|
||||
<string name="open_in_app">Ouvrir dans l\'application</string>
|
||||
<string name="opened_on_phone">Ouvert sur le téléphone</string>
|
||||
<string name="auto_location_permission_needed">Pour exécuter EVMap sur Android Auto, vous devez autoriser l\'accès à votre emplacement.</string>
|
||||
<string name="grant_on_phone">Grant au téléphone</string>
|
||||
<string name="auto_prices">Prix</string>
|
||||
<string name="auto_vehicle_data">Données sur le véhicule</string>
|
||||
<string name="auto_range">Autonomie</string>
|
||||
<string name="auto_speed">Vitesse</string>
|
||||
<string name="welcome_android_auto">Prise en charge d’Android Auto</string>
|
||||
<string name="sounds_cool">ça a l\'air cool</string>
|
||||
<string name="auto_chargeprice_vehicle_unknown">Aucun des véhicules sélectionnés dans l\'application ne correspond à ce véhicule (%1$s %2$s).</string>
|
||||
<string name="auto_chargeprice_vehicle_ambiguous">Plusieurs véhicules sélectionnés dans l\'application correspondent à ce véhicule (%1$s %2$s).</string>
|
||||
<string name="selecting_all">tous les éléments sélectionnés</string>
|
||||
<string name="data_sources_hint">Dans les paramètres, vous pouvez également choisir entre Google Maps et OpenStreetMap (Mapbox) pour les données cartographiques.</string>
|
||||
<string name="auto_chargeprice_vehicle_unavailable">EVMap n\'a pas pu déterminer le modèle de votre véhicule.</string>
|
||||
<string name="auto_no_chargers_found">Aucun chargeur à proximité n\'a été trouvé</string>
|
||||
<string name="auto_no_favorites_found">Pas de favoris trouvés</string>
|
||||
<string name="auto_charging_level">Niveau de charge</string>
|
||||
<string name="auto_chargers_closeby">Chargeurs à proximité</string>
|
||||
<string name="auto_chargers_near_location">Près de %s</string>
|
||||
<string name="auto_fault_report_date">⚠️ Rapport d\'anomalie (%s)</string>
|
||||
<string name="auto_no_data">Indisponible</string>
|
||||
<string name="auto_settings">Paramètres</string>
|
||||
<string name="selecting_none">désélectionner tous les éléments</string>
|
||||
<string name="auto_vehicle_data_permission_needed">Pour cette fonction, EVMap doit avoir accès aux données de votre véhicule.</string>
|
||||
<string name="auto_heading">Direction</string>
|
||||
<string name="auto_favorites">Favoris</string>
|
||||
<string name="auto_no_refresh_possible">D\'autres mises à jour ne sont pas possibles. Veuillez revenir en arrière et redémarrer.</string>
|
||||
<string name="settings_android_auto_chargeprice_range">Plage de charge pour la comparaison des prix</string>
|
||||
<string name="welcome_android_auto_detail">Vous pouvez également utiliser EVMap à partir d\'Android Auto sur les voitures prises en charge. Il suffit de sélectionner l\'application EVMap dans le menu Android Auto.</string>
|
||||
</resources>
|
||||
37
app/src/google/res/values-nb-rNO/strings.xml
Normal file
37
app/src/google/res/values-nb-rNO/strings.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="donations_info" formatted="false">Synes du EVMap er nyttig\? Støtt utviklingen ved å sende penger til utvikleren.
|
||||
\n
|
||||
\nGoogle tar 15% av alle donasjoner.</string>
|
||||
<string name="auto_favorites">Favoritter</string>
|
||||
<string name="auto_charging_level">Ladingsnivå</string>
|
||||
<string name="auto_chargeprice_vehicle_unavailable">EVMap kunne ikke fastsette kjøretøymodellen.</string>
|
||||
<string name="selecting_none">fravalgte alle elementer</string>
|
||||
<string name="grant_on_phone">Innvilg på mobilenheten</string>
|
||||
<string name="auto_chargers_closeby">Ladere i nærheten</string>
|
||||
<string name="auto_prices">Pris</string>
|
||||
<string name="auto_no_chargers_found">Ingen ladere i nærheten</string>
|
||||
<string name="auto_no_favorites_found">Fant ikke noen favoritter</string>
|
||||
<string name="open_in_app">Åpne i programmet</string>
|
||||
<string name="auto_location_service">EVMap kjører på Android Auto og bruker posisjonen din.</string>
|
||||
<string name="auto_heading">Fartsretning</string>
|
||||
<string name="opened_on_phone">Åpnet på mobilenheten</string>
|
||||
<string name="auto_location_permission_needed">Innvilg posisjonstilgang for å bruke EVMap på Android Auto.</string>
|
||||
<string name="auto_chargers_near_location">Nær %s</string>
|
||||
<string name="auto_fault_report_date">⚠️ Feilrapport (%s)</string>
|
||||
<string name="auto_vehicle_data">Kjøretøydata</string>
|
||||
<string name="auto_no_data">Utilgjengelig</string>
|
||||
<string name="auto_speed">Hastighet</string>
|
||||
<string name="auto_settings">Innstillinger</string>
|
||||
<string name="auto_chargeprice_vehicle_unknown">Ingen av kjøretøyene valgt i programmet samsvarer med dette kjøretøyet (%1$s %2$s).</string>
|
||||
<string name="welcome_android_auto">Android Auto-støtte</string>
|
||||
<string name="auto_chargeprice_vehicle_ambiguous">Flere kjøretøy valgt i programmet samsvarer med dette kjøretøyet (%1$s %2$s).</string>
|
||||
<string name="auto_vehicle_data_permission_needed">EvMap trenger tilgang til kjøretøydata for å bruke denne funksjonen.</string>
|
||||
<string name="auto_no_refresh_possible">Videre oppdateringer er ikke mulig. Gå tilbake og start på ny.</string>
|
||||
<string name="auto_range">Rekkevidde</string>
|
||||
<string name="welcome_android_auto_detail">Du kan også bruke EVMap inne i Android Auto på bilder som støtter dette ved å velge det i Android Auto-menyen.</string>
|
||||
<string name="settings_android_auto_chargeprice_range">Prissammenligning for laderekkevidde fordelt på pris</string>
|
||||
<string name="data_sources_hint">I innstillingene kan du også bytte mellom Google Maps og OpenStreetMap (Mapbox) for kartdata.</string>
|
||||
<string name="selecting_all">valgte alle elementene</string>
|
||||
<string name="sounds_cool">den er grei</string>
|
||||
</resources>
|
||||
19
app/src/google/res/values/arrays.xml
Normal file
19
app/src/google/res/values/arrays.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_map_provider_names">
|
||||
<item>@string/pref_provider_google_maps</item>
|
||||
<item>@string/pref_provider_osm_mapbox</item>
|
||||
</string-array>
|
||||
<string-array name="pref_map_provider_values" translatable="false">
|
||||
<item>google</item>
|
||||
<item>mapbox</item>
|
||||
</string-array>
|
||||
<string-array name="pref_search_provider_names">
|
||||
<item>@string/pref_provider_google_maps</item>
|
||||
<item>@string/pref_provider_osm_mapbox</item>
|
||||
</string-array>
|
||||
<string-array name="pref_search_provider_values" translatable="false">
|
||||
<item>google</item>
|
||||
<item>mapbox</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -3,5 +3,5 @@
|
||||
<color name="gauge_active">#00e676</color>
|
||||
<color name="gauge_middle">#087f23</color>
|
||||
<color name="gauge_inactive">#9e9e9e</color>
|
||||
<color name="charger_100kw_dark">#fdd835</color>
|
||||
<color name="charger_100kw_dark">#FBC02D</color>
|
||||
</resources>
|
||||
5
app/src/google/res/values/donottranslate.xml
Normal file
5
app/src/google/res/values/donottranslate.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pref_map_provider_default" translatable="false">google</string>
|
||||
<string name="pref_search_provider_default" translatable="false">mapbox</string>
|
||||
</resources>
|
||||
@@ -1,23 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_map_provider_names">
|
||||
<item>Google Maps</item>
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string-array name="pref_map_provider_values" tranlatable="false">
|
||||
<item>google</item>
|
||||
<item>mapbox</item>
|
||||
</string-array>
|
||||
<string-array name="pref_search_provider_names">
|
||||
<item>Google Maps</item>
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string-array name="pref_search_provider_values" tranlatable="false">
|
||||
<item>google</item>
|
||||
<item>mapbox</item>
|
||||
</string-array>
|
||||
<string name="pref_map_provider_default" translatable="false">google</string>
|
||||
<string name="pref_search_provider_default" translatable="false">mapbox</string>
|
||||
<string name="donations_info" formatted="false">Do you find EVMap useful? Support its development by sending a donation to the developer.\n\nGoogle takes 15% off every donation.</string>
|
||||
<string name="auto_location_service">EVMap is running on Android Auto and using your location.</string>
|
||||
<string name="auto_no_chargers_found">No nearby chargers found</string>
|
||||
@@ -45,7 +27,7 @@
|
||||
<string name="sounds_cool">sounds cool</string>
|
||||
<string name="auto_chargeprice_vehicle_unavailable">EVMap could not determine your vehicle model.</string>
|
||||
<string name="auto_chargeprice_vehicle_unknown">None of the vehicles selected in the app matches this vehicle (%1$s %2$s).</string>
|
||||
<string name="auto_chargeprice_vehicle_ambiguous">Mehrere der in der App ausgewählten Fahrzeuge passen zu diesem Fahrzeug (%1$s %2$s).</string>
|
||||
<string name="auto_chargeprice_vehicle_ambiguous">Multiple vehicles selected in the app match this vehicle (%1$s %2$s).</string>
|
||||
<string name="settings_android_auto_chargeprice_range">Charging range for price comparison</string>
|
||||
<string name="data_sources_hint">In the settings you can also switch between Google Maps and OpenStreetMap (Mapbox) for the map data.</string>
|
||||
<string name="selecting_all">selected all items</string>
|
||||
5
app/src/googleAutomotive/res/values-fr/strings.xml
Normal file
5
app/src/googleAutomotive/res/values-fr/strings.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="grant_on_phone">Autoriser</string>
|
||||
<string name="auto_location_permission_needed">Pour exécuter EVMap sur Android Auto, vous devez autoriser l\'accès à votre emplacement.</string>
|
||||
</resources>
|
||||
5
app/src/googleAutomotive/res/values-nb-rNO/strings.xml
Normal file
5
app/src/googleAutomotive/res/values-nb-rNO/strings.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="auto_location_permission_needed">Du må du innvilge posisjonstilgang for å kjøre EVMap i bilen din.</string>
|
||||
<string name="grant_on_phone">Tillat</string>
|
||||
</resources>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="auto_location_permission_needed">To run EVMap on your car, you need to grant access to your location.</string>
|
||||
<string name="grant_on_phone">Allow</string>
|
||||
<string name="auto_location_permission_needed">To run EVMap on your car, you need to grant access to your location.</string>
|
||||
</resources>
|
||||
@@ -86,7 +86,7 @@ class MapsActivity : AppCompatActivity(),
|
||||
ViewCompat.setOnApplyWindowInsetsListener(navView) { v, insets ->
|
||||
val header = navView.getHeaderView(0)
|
||||
header.setPadding(0, insets.getInsets(WindowInsetsCompat.Type.statusBars()).top, 0, 0)
|
||||
WindowInsetsCompat.CONSUMED
|
||||
insets
|
||||
}
|
||||
|
||||
prefs = PreferenceDataSource(this)
|
||||
|
||||
@@ -3,14 +3,16 @@ package net.vonforst.evmap.api.chargeprice
|
||||
import android.content.Context
|
||||
import com.facebook.stetho.okhttp3.StethoInterceptor
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import moe.banana.jsonapi2.ArrayDocument
|
||||
import moe.banana.jsonapi2.JsonApiConverterFactory
|
||||
import moe.banana.jsonapi2.ResourceAdapterFactory
|
||||
import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory
|
||||
import jsonapi.Document
|
||||
import jsonapi.JsonApiFactory
|
||||
import jsonapi.retrofit.DocumentConverterFactory
|
||||
import net.vonforst.evmap.BuildConfig
|
||||
import net.vonforst.evmap.model.ChargeLocation
|
||||
import okhttp3.Cache
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
@@ -20,34 +22,45 @@ import java.util.*
|
||||
interface ChargepriceApi {
|
||||
@POST("charge_prices")
|
||||
suspend fun getChargePrices(
|
||||
@Body request: ChargepriceRequest,
|
||||
@Body @jsonapi.retrofit.Document request: ChargepriceRequest,
|
||||
@Header("Accept-Language") language: String
|
||||
): ArrayDocument<ChargePrice>
|
||||
): Document<List<ChargePrice>>
|
||||
|
||||
@GET("vehicles")
|
||||
suspend fun getVehicles(): ArrayDocument<ChargepriceCar>
|
||||
@jsonapi.retrofit.Document
|
||||
suspend fun getVehicles(): List<ChargepriceCar>
|
||||
|
||||
@GET("tariffs")
|
||||
suspend fun getTariffs(): ArrayDocument<ChargepriceTariff>
|
||||
@jsonapi.retrofit.Document
|
||||
suspend fun getTariffs(): List<ChargepriceTariff>
|
||||
|
||||
@POST("user_feedback")
|
||||
suspend fun userFeedback(@Body @jsonapi.retrofit.Document feedback: ChargepriceUserFeedback)
|
||||
|
||||
companion object {
|
||||
private val cacheSize = 1L * 1024 * 1024 // 1MB
|
||||
val supportedLanguages = setOf("de", "en", "fr", "nl")
|
||||
|
||||
val DATA_SOURCE_GOINGELECTRIC = "going_electric"
|
||||
val DATA_SOURCE_OPENCHARGEMAP = "open_charge_map"
|
||||
private val DATA_SOURCE_GOINGELECTRIC = "going_electric"
|
||||
private val DATA_SOURCE_OPENCHARGEMAP = "open_charge_map"
|
||||
|
||||
private val jsonApiAdapterFactory = ResourceAdapterFactory.builder()
|
||||
.add(ChargepriceRequest::class.java)
|
||||
.add(ChargepriceTariff::class.java)
|
||||
.add(ChargepriceBrand::class.java)
|
||||
.add(ChargePrice::class.java)
|
||||
.add(ChargepriceCar::class.java)
|
||||
private val jsonApiAdapterFactory = JsonApiFactory.Builder()
|
||||
.addType(ChargepriceRequest::class.java)
|
||||
.addType(ChargepriceTariff::class.java)
|
||||
.addType(ChargepriceBrand::class.java)
|
||||
.addType(ChargePrice::class.java)
|
||||
.addType(ChargepriceCar::class.java)
|
||||
.build()
|
||||
val moshi = Moshi.Builder()
|
||||
.add(jsonApiAdapterFactory)
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.add(
|
||||
PolymorphicJsonAdapterFactory.of(ChargepriceUserFeedback::class.java, "type")
|
||||
.withSubtype(ChargepriceMissingPriceFeedback::class.java, "missing_price")
|
||||
.withSubtype(ChargepriceWrongPriceFeedback::class.java, "wrong_price")
|
||||
.withSubtype(ChargepriceMissingVehicleFeedback::class.java, "missing_vehicle")
|
||||
)
|
||||
.build()
|
||||
|
||||
fun create(
|
||||
apikey: String,
|
||||
baseurl: String = "https://api.chargeprice.app/v1/",
|
||||
@@ -73,7 +86,8 @@ interface ChargepriceApi {
|
||||
|
||||
val retrofit = Retrofit.Builder()
|
||||
.baseUrl(baseurl)
|
||||
.addConverterFactory(JsonApiConverterFactory.create(moshi))
|
||||
.addConverterFactory(DocumentConverterFactory.create())
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.client(client)
|
||||
.build()
|
||||
return retrofit.create(ChargepriceApi::class.java)
|
||||
@@ -89,6 +103,15 @@ interface ChargepriceApi {
|
||||
}
|
||||
}
|
||||
|
||||
fun getPoiUrl(charger: ChargeLocation) =
|
||||
"https://www.chargeprice.app/?poi_id=${charger.id}&poi_source=${getDataAdapter(charger)}"
|
||||
|
||||
fun getDataAdapter(charger: ChargeLocation) = when (charger.dataSource) {
|
||||
"goingelectric" -> DATA_SOURCE_GOINGELECTRIC
|
||||
"openchargemap" -> DATA_SOURCE_OPENCHARGEMAP
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isCountrySupported(country: String, dataSource: String): Boolean = when (dataSource) {
|
||||
// list of countries updated 2021/08/24
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package net.vonforst.evmap.api.chargeprice
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import android.util.Patterns
|
||||
import com.squareup.moshi.Json
|
||||
import moe.banana.jsonapi2.HasMany
|
||||
import moe.banana.jsonapi2.HasOne
|
||||
|
||||
import moe.banana.jsonapi2.JsonApi
|
||||
import moe.banana.jsonapi2.Resource
|
||||
import com.squareup.moshi.JsonClass
|
||||
import jsonapi.*
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.adapter.Equatable
|
||||
import net.vonforst.evmap.api.equivalentPlugTypes
|
||||
@@ -17,16 +17,21 @@ import kotlin.math.ceil
|
||||
import kotlin.math.floor
|
||||
|
||||
|
||||
@JsonApi(type = "charge_price_request")
|
||||
class ChargepriceRequest : Resource() {
|
||||
@field:Json(name = "data_adapter")
|
||||
lateinit var dataAdapter: String
|
||||
lateinit var station: ChargepriceStation
|
||||
lateinit var options: ChargepriceOptions
|
||||
var tariffs: HasMany<ChargepriceTariff>? = null
|
||||
var vehicle: HasOne<ChargepriceCar>? = null
|
||||
}
|
||||
@Resource("charge_price_request")
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargepriceRequest(
|
||||
@Json(name = "data_adapter")
|
||||
val dataAdapter: String,
|
||||
val station: ChargepriceStation,
|
||||
val options: ChargepriceOptions,
|
||||
@ToMany("tariffs")
|
||||
val tariffs: List<ChargepriceTariff>? = null,
|
||||
@ToOne("vehicle")
|
||||
val vehicle: ChargepriceCar? = null,
|
||||
@RelationshipsObject var relationships: Relationships? = null
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargepriceStation(
|
||||
val longitude: Double,
|
||||
val latitude: Double,
|
||||
@@ -56,11 +61,13 @@ data class ChargepriceStation(
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargepriceChargepoint(
|
||||
val power: Double,
|
||||
val plug: String
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargepriceOptions(
|
||||
@Json(name = "max_monthly_fees") val maxMonthlyFees: Double? = null,
|
||||
val energy: Double? = null,
|
||||
@@ -73,142 +80,107 @@ data class ChargepriceOptions(
|
||||
@Json(name = "provider_customer_tariffs") val providerCustomerTariffs: Boolean? = null
|
||||
)
|
||||
|
||||
@JsonApi(type = "tariff")
|
||||
class ChargepriceTariff() : Resource() {
|
||||
lateinit var provider: String
|
||||
lateinit var name: String
|
||||
@field:Json(name = "direct_payment")
|
||||
var directPayment: Boolean = false
|
||||
@field:Json(name = "provider_customer_tariff")
|
||||
var providerCustomerTariff: Boolean = false
|
||||
@field:Json(name = "supported_cuntries")
|
||||
lateinit var supportedCountries: Set<String>
|
||||
@field:Json(name = "charge_card_id")
|
||||
lateinit var chargeCardId: String // GE charge card ID
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
other as ChargepriceTariff
|
||||
|
||||
if (provider != other.provider) return false
|
||||
if (name != other.name) return false
|
||||
if (directPayment != other.directPayment) return false
|
||||
if (providerCustomerTariff != other.providerCustomerTariff) return false
|
||||
if (supportedCountries != other.supportedCountries) return false
|
||||
if (chargeCardId != other.chargeCardId) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + provider.hashCode()
|
||||
result = 31 * result + name.hashCode()
|
||||
result = 31 * result + directPayment.hashCode()
|
||||
result = 31 * result + providerCustomerTariff.hashCode()
|
||||
result = 31 * result + supportedCountries.hashCode()
|
||||
result = 31 * result + chargeCardId.hashCode()
|
||||
return result
|
||||
}
|
||||
@Resource("tariff")
|
||||
@Parcelize
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargepriceTariff(
|
||||
@Id val id_: String?,
|
||||
val provider: String,
|
||||
val name: String,
|
||||
@Json(name = "direct_payment")
|
||||
val directPayment: Boolean = false,
|
||||
@Json(name = "provider_customer_tariff")
|
||||
val providerCustomerTariff: Boolean = false,
|
||||
@Json(name = "supported_countries")
|
||||
val supportedCountries: Set<String>,
|
||||
@Json(name = "charge_card_id")
|
||||
val chargeCardId: String?, // GE charge card ID
|
||||
) : Parcelable {
|
||||
val id: String
|
||||
get() = id_!!
|
||||
}
|
||||
|
||||
@JsonApi(type = "car")
|
||||
class ChargepriceCar : Resource(), Equatable {
|
||||
lateinit var name: String
|
||||
lateinit var brand: String
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Resource("car")
|
||||
@Parcelize
|
||||
data class ChargepriceCar(
|
||||
@Id val id_: String?,
|
||||
val name: String,
|
||||
val brand: String,
|
||||
|
||||
@field:Json(name = "dc_charge_ports")
|
||||
lateinit var dcChargePorts: List<String>
|
||||
lateinit var manufacturer: HasOne<ChargepriceBrand>
|
||||
@Json(name = "dc_charge_ports")
|
||||
val dcChargePorts: List<String>
|
||||
) : Equatable, Parcelable {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
other as ChargepriceCar
|
||||
|
||||
if (name != other.name) return false
|
||||
if (brand != other.brand) return false
|
||||
if (dcChargePorts != other.dcChargePorts) return false
|
||||
if (manufacturer != other.manufacturer) return false
|
||||
|
||||
return true
|
||||
companion object {
|
||||
private val acConnectors = listOf(
|
||||
Chargepoint.CEE_BLAU,
|
||||
Chargepoint.CEE_ROT,
|
||||
Chargepoint.SCHUKO,
|
||||
Chargepoint.TYPE_1,
|
||||
Chargepoint.TYPE_2_UNKNOWN,
|
||||
Chargepoint.TYPE_2_SOCKET,
|
||||
Chargepoint.TYPE_2_PLUG
|
||||
)
|
||||
private val plugMapping = mapOf(
|
||||
"ccs" to Chargepoint.CCS_UNKNOWN,
|
||||
"tesla_suc" to Chargepoint.SUPERCHARGER,
|
||||
"tesla_ccs" to Chargepoint.CCS_UNKNOWN,
|
||||
"chademo" to Chargepoint.CHADEMO
|
||||
)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + name.hashCode()
|
||||
result = 31 * result + brand.hashCode()
|
||||
result = 31 * result + dcChargePorts.hashCode()
|
||||
result = 31 * result + manufacturer.hashCode()
|
||||
return result
|
||||
}
|
||||
val id: String
|
||||
get() = id_!!
|
||||
|
||||
private val acConnectors = listOf(
|
||||
Chargepoint.CEE_BLAU,
|
||||
Chargepoint.CEE_ROT,
|
||||
Chargepoint.SCHUKO,
|
||||
Chargepoint.TYPE_1,
|
||||
Chargepoint.TYPE_2_UNKNOWN,
|
||||
Chargepoint.TYPE_2_SOCKET,
|
||||
Chargepoint.TYPE_2_PLUG
|
||||
)
|
||||
private val plugMapping = mapOf(
|
||||
"ccs" to Chargepoint.CCS_UNKNOWN,
|
||||
"tesla_suc" to Chargepoint.SUPERCHARGER,
|
||||
"tesla_ccs" to Chargepoint.CCS_UNKNOWN,
|
||||
"chademo" to Chargepoint.CHADEMO
|
||||
)
|
||||
val compatibleEvmapConnectors: List<String>
|
||||
get() = dcChargePorts.map {
|
||||
plugMapping[it]
|
||||
}.filterNotNull().plus(acConnectors)
|
||||
}
|
||||
|
||||
@JsonApi(type = "brand")
|
||||
class ChargepriceBrand : Resource()
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Resource("brand")
|
||||
@Parcelize
|
||||
data class ChargepriceBrand(
|
||||
@Id val id: String?
|
||||
) : Parcelable
|
||||
|
||||
@JsonApi(type = "charge_price")
|
||||
class ChargePrice : Resource(), Equatable, Cloneable {
|
||||
lateinit var provider: String
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Resource("charge_price")
|
||||
@Parcelize
|
||||
data class ChargePrice(
|
||||
val provider: String,
|
||||
@Json(name = "tariff_name")
|
||||
val tariffName: String,
|
||||
val url: String,
|
||||
@Json(name = "monthly_min_sales")
|
||||
val monthlyMinSales: Double = 0.0,
|
||||
@Json(name = "total_monthly_fee")
|
||||
val totalMonthlyFee: Double = 0.0,
|
||||
@Json(name = "flat_rate")
|
||||
val flatRate: Boolean = false,
|
||||
|
||||
@field:Json(name = "tariff_name")
|
||||
lateinit var tariffName: String
|
||||
lateinit var url: String
|
||||
@Json(name = "direct_payment")
|
||||
val directPayment: Boolean = false,
|
||||
|
||||
@field:Json(name = "monthly_min_sales")
|
||||
var monthlyMinSales: Double = 0.0
|
||||
@Json(name = "provider_customer_tariff")
|
||||
val providerCustomerTariff: Boolean = false,
|
||||
val currency: String,
|
||||
|
||||
@field:Json(name = "total_monthly_fee")
|
||||
var totalMonthlyFee: Double = 0.0
|
||||
@Json(name = "start_time")
|
||||
val startTime: Int = 0,
|
||||
val tags: List<ChargepriceTag>,
|
||||
|
||||
@field:Json(name = "flat_rate")
|
||||
var flatRate: Boolean = false
|
||||
|
||||
@field:Json(name = "direct_payment")
|
||||
var directPayment: Boolean = false
|
||||
|
||||
@field:Json(name = "provider_customer_tariff")
|
||||
var providerCustomerTariff: Boolean = false
|
||||
lateinit var currency: String
|
||||
|
||||
@field:Json(name = "start_time")
|
||||
var startTime: Int = 0
|
||||
lateinit var tags: List<ChargepriceTag>
|
||||
|
||||
@field:Json(name = "charge_point_prices")
|
||||
lateinit var chargepointPrices: List<ChargepointPrice>
|
||||
|
||||
@field:Json(name = "branding")
|
||||
var branding: ChargepriceBranding? = null
|
||||
|
||||
var tariff: HasOne<ChargepriceTariff>? = null
|
||||
@Json(name = "charge_point_prices")
|
||||
val chargepointPrices: List<ChargepointPrice>,
|
||||
|
||||
@Json(name = "branding")
|
||||
val branding: ChargepriceBranding? = null,
|
||||
|
||||
@ToOne("tariff")
|
||||
val tariffId: String?
|
||||
) : Equatable, Cloneable, Parcelable {
|
||||
fun formatMonthlyFees(ctx: Context): String {
|
||||
return listOfNotNull(
|
||||
if (totalMonthlyFee > 0) {
|
||||
@@ -219,69 +191,10 @@ class ChargePrice : Resource(), Equatable, Cloneable {
|
||||
} else null
|
||||
).joinToString(", ")
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
other as ChargePrice
|
||||
|
||||
if (provider != other.provider) return false
|
||||
if (tariffName != other.tariffName) return false
|
||||
if (url != other.url) return false
|
||||
if (monthlyMinSales != other.monthlyMinSales) return false
|
||||
if (totalMonthlyFee != other.totalMonthlyFee) return false
|
||||
if (flatRate != other.flatRate) return false
|
||||
if (directPayment != other.directPayment) return false
|
||||
if (providerCustomerTariff != other.providerCustomerTariff) return false
|
||||
if (currency != other.currency) return false
|
||||
if (startTime != other.startTime) return false
|
||||
if (tags != other.tags) return false
|
||||
if (chargepointPrices != other.chargepointPrices) return false
|
||||
if (branding != other.branding) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = super.hashCode()
|
||||
result = 31 * result + provider.hashCode()
|
||||
result = 31 * result + tariffName.hashCode()
|
||||
result = 31 * result + url.hashCode()
|
||||
result = 31 * result + monthlyMinSales.hashCode()
|
||||
result = 31 * result + totalMonthlyFee.hashCode()
|
||||
result = 31 * result + flatRate.hashCode()
|
||||
result = 31 * result + directPayment.hashCode()
|
||||
result = 31 * result + providerCustomerTariff.hashCode()
|
||||
result = 31 * result + currency.hashCode()
|
||||
result = 31 * result + startTime
|
||||
result = 31 * result + tags.hashCode()
|
||||
result = 31 * result + chargepointPrices.hashCode()
|
||||
result = 31 * result + branding.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
public override fun clone(): ChargePrice {
|
||||
return ChargePrice().apply {
|
||||
chargepointPrices = this@ChargePrice.chargepointPrices
|
||||
currency = this@ChargePrice.currency
|
||||
directPayment = this@ChargePrice.directPayment
|
||||
flatRate = this@ChargePrice.flatRate
|
||||
monthlyMinSales = this@ChargePrice.monthlyMinSales
|
||||
provider = this@ChargePrice.provider
|
||||
providerCustomerTariff = this@ChargePrice.providerCustomerTariff
|
||||
startTime = this@ChargePrice.startTime
|
||||
tags = this@ChargePrice.tags
|
||||
tariffName = this@ChargePrice.tariffName
|
||||
totalMonthlyFee = this@ChargePrice.totalMonthlyFee
|
||||
url = this@ChargePrice.url
|
||||
tariff = this@ChargePrice.tariff
|
||||
branding = this@ChargePrice.branding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class ChargepointPrice(
|
||||
val power: Double,
|
||||
val plug: String,
|
||||
@@ -289,7 +202,7 @@ data class ChargepointPrice(
|
||||
@Json(name = "price_distribution") val priceDistribution: PriceDistribution,
|
||||
@Json(name = "blocking_fee_start") val blockingFeeStart: Int?,
|
||||
@Json(name = "no_price_reason") var noPriceReason: String?
|
||||
) {
|
||||
) : Parcelable {
|
||||
fun formatDistribution(ctx: Context): String {
|
||||
fun percent(value: Double): String {
|
||||
return ctx.getString(R.string.percent_format, value * 100) + "\u00a0"
|
||||
@@ -332,19 +245,28 @@ data class ChargepointPrice(
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class ChargepriceBranding(
|
||||
@Json(name = "background_color") val backgroundColor: String,
|
||||
@Json(name = "text_color") val textColor: String,
|
||||
@Json(name = "logo_url") val logoUrl: String
|
||||
)
|
||||
) : Parcelable
|
||||
|
||||
data class PriceDistribution(val kwh: Double?, val session: Double?, val minute: Double?) {
|
||||
val isOnlyKwh =
|
||||
kwh != null && kwh > 0 && (session == null || session == 0.0) && (minute == null || minute == 0.0)
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class PriceDistribution(val kwh: Double?, val session: Double?, val minute: Double?) :
|
||||
Parcelable {
|
||||
val isOnlyKwh
|
||||
get() = kwh != null && kwh > 0 && (session == null || session == 0.0) && (minute == null || minute == 0.0)
|
||||
}
|
||||
|
||||
data class ChargepriceTag(val kind: String, val text: String, val url: String?) : Equatable
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class ChargepriceTag(val kind: String, val text: String, val url: String?) : Equatable,
|
||||
Parcelable
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargepriceMeta(
|
||||
@Json(name = "charge_points") val chargePoints: List<ChargepriceChargepointMeta>
|
||||
)
|
||||
@@ -358,13 +280,97 @@ enum class ChargepriceInclude {
|
||||
EXCLUSIVE
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class ChargepriceRequestTariffMeta(
|
||||
val include: ChargepriceInclude
|
||||
)
|
||||
) : Parcelable
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargepriceChargepointMeta(
|
||||
val power: Double,
|
||||
val plug: String,
|
||||
val energy: Double,
|
||||
val duration: Double
|
||||
)
|
||||
)
|
||||
|
||||
@Resource("user_feedback")
|
||||
sealed class ChargepriceUserFeedback(
|
||||
val notes: String,
|
||||
val email: String,
|
||||
val context: String,
|
||||
val language: String
|
||||
) {
|
||||
init {
|
||||
if (email.isBlank() || email.length > 100 || !Patterns.EMAIL_ADDRESS.matcher(email)
|
||||
.matches()
|
||||
) {
|
||||
throw IllegalArgumentException("invalid email")
|
||||
}
|
||||
if (!ChargepriceApi.supportedLanguages.contains(language)) {
|
||||
throw IllegalArgumentException("invalid language")
|
||||
}
|
||||
if (context.length > 500) throw IllegalArgumentException("invalid context")
|
||||
if (notes.length > 1000) throw IllegalArgumentException("invalid notes")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Resource(type = "missing_price")
|
||||
class ChargepriceMissingPriceFeedback(
|
||||
val tariff: String,
|
||||
val cpo: String,
|
||||
val price: String,
|
||||
@Json(name = "poi_link") val poiLink: String,
|
||||
notes: String,
|
||||
email: String,
|
||||
context: String,
|
||||
language: String
|
||||
) : ChargepriceUserFeedback(notes, email, context, language) {
|
||||
init {
|
||||
if (tariff.isBlank() || tariff.length > 100) throw IllegalArgumentException("invalid tariff")
|
||||
if (cpo.length > 200) throw IllegalArgumentException("invalid cpo")
|
||||
if (price.isBlank() || price.length > 100) throw IllegalArgumentException("invalid price")
|
||||
if (poiLink.isBlank() || poiLink.length > 200) throw IllegalArgumentException("invalid poiLink")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Resource(type = "wrong_price")
|
||||
class ChargepriceWrongPriceFeedback(
|
||||
val tariff: String,
|
||||
val cpo: String,
|
||||
@Json(name = "displayed_price") val displayedPrice: String,
|
||||
@Json(name = "actual_price") val actualPrice: String,
|
||||
@Json(name = "poi_link") val poiLink: String,
|
||||
notes: String,
|
||||
email: String,
|
||||
context: String,
|
||||
language: String,
|
||||
) : ChargepriceUserFeedback(notes, email, context, language) {
|
||||
init {
|
||||
if (tariff.length > 100) throw IllegalArgumentException("invalid tariff")
|
||||
if (cpo.length > 200) throw IllegalArgumentException("invalid cpo")
|
||||
if (displayedPrice.length > 100) throw IllegalArgumentException("invalid displayedPrice")
|
||||
if (actualPrice.length > 100) throw IllegalArgumentException("invalid actualPrice")
|
||||
if (poiLink.length > 200) throw IllegalArgumentException("invalid poiLink")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Resource(type = "missing_vehicle")
|
||||
class ChargepriceMissingVehicleFeedback(
|
||||
val brand: String,
|
||||
val model: String,
|
||||
notes: String,
|
||||
email: String,
|
||||
context: String,
|
||||
language: String,
|
||||
) : ChargepriceUserFeedback(notes, email, context, language) {
|
||||
init {
|
||||
if (brand.length > 100) throw IllegalArgumentException("invalid brand")
|
||||
if (model.length > 100) throw IllegalArgumentException("invalid model")
|
||||
}
|
||||
}
|
||||
@@ -467,17 +467,17 @@ class GoingElectricApiWrapper(
|
||||
sp.getString(R.string.filter_networks), "networks",
|
||||
networkMap, manyChoices = true
|
||||
),
|
||||
MultipleChoiceFilter(
|
||||
sp.getString(R.string.categories), "categories",
|
||||
categoryMap,
|
||||
manyChoices = true
|
||||
),
|
||||
BooleanFilter(sp.getString(R.string.filter_exclude_faults), "exclude_faults"),
|
||||
BooleanFilter(sp.getString(R.string.filter_barrierfree), "barrierfree"),
|
||||
MultipleChoiceFilter(
|
||||
sp.getString(R.string.filter_chargecards), "chargecards",
|
||||
chargecardMap, manyChoices = true
|
||||
),
|
||||
BooleanFilter(sp.getString(R.string.filter_exclude_faults), "exclude_faults")
|
||||
MultipleChoiceFilter(
|
||||
sp.getString(R.string.categories), "categories",
|
||||
categoryMap,
|
||||
manyChoices = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
@@ -17,6 +16,7 @@ import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.transition.MaterialContainerTransform
|
||||
import net.vonforst.evmap.MapsActivity
|
||||
@@ -24,6 +24,7 @@ import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.adapter.ChargepriceAdapter
|
||||
import net.vonforst.evmap.adapter.CheckableChargepriceCarAdapter
|
||||
import net.vonforst.evmap.adapter.CheckableConnectorAdapter
|
||||
import net.vonforst.evmap.api.chargeprice.ChargepriceApi
|
||||
import net.vonforst.evmap.api.chargeprice.ChargepriceCar
|
||||
import net.vonforst.evmap.api.equivalentPlugTypes
|
||||
import net.vonforst.evmap.databinding.FragmentChargepriceBinding
|
||||
@@ -31,7 +32,7 @@ import net.vonforst.evmap.model.Chargepoint
|
||||
import net.vonforst.evmap.storage.PreferenceDataSource
|
||||
import net.vonforst.evmap.viewmodel.ChargepriceViewModel
|
||||
import net.vonforst.evmap.viewmodel.Status
|
||||
import net.vonforst.evmap.viewmodel.viewModelFactory
|
||||
import net.vonforst.evmap.viewmodel.savedStateViewModelFactory
|
||||
import java.text.NumberFormat
|
||||
|
||||
class ChargepriceFragment : Fragment() {
|
||||
@@ -39,10 +40,12 @@ class ChargepriceFragment : Fragment() {
|
||||
private var connectionErrorSnackbar: Snackbar? = null
|
||||
|
||||
private val vm: ChargepriceViewModel by viewModels(factoryProducer = {
|
||||
viewModelFactory {
|
||||
savedStateViewModelFactory { state ->
|
||||
ChargepriceViewModel(
|
||||
requireActivity().application,
|
||||
getString(R.string.chargeprice_key)
|
||||
getString(R.string.chargeprice_key),
|
||||
getString(R.string.chargeprice_api_url),
|
||||
state
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -60,8 +63,13 @@ class ChargepriceFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
vm.reloadPrefs()
|
||||
}
|
||||
|
||||
private fun showDonationDialog() {
|
||||
AlertDialog.Builder(requireContext())
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.chargeprice_donation_dialog_title)
|
||||
.setMessage(R.string.chargeprice_donation_dialog_detail)
|
||||
.setNegativeButton(R.string.ok) { di, _ ->
|
||||
@@ -103,9 +111,7 @@ class ChargepriceFragment : Fragment() {
|
||||
|
||||
val fragmentArgs: ChargepriceFragmentArgs by navArgs()
|
||||
val charger = fragmentArgs.charger
|
||||
val dataSource = fragmentArgs.dataSource
|
||||
vm.charger.value = charger
|
||||
vm.dataSource.value = dataSource
|
||||
if (vm.chargepoint.value == null) {
|
||||
vm.chargepoint.value = charger.chargepointsMerged.get(0)
|
||||
}
|
||||
@@ -170,7 +176,7 @@ class ChargepriceFragment : Fragment() {
|
||||
}
|
||||
|
||||
binding.imgChargepriceLogo.setOnClickListener {
|
||||
(requireActivity() as MapsActivity).openUrl("https://www.chargeprice.app/?poi_id=${charger.id}&poi_source=${dataSource}")
|
||||
(requireActivity() as MapsActivity).openUrl(ChargepriceApi.getPoiUrl(charger))
|
||||
}
|
||||
|
||||
binding.btnSettings.setOnClickListener {
|
||||
|
||||
@@ -4,13 +4,12 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import net.vonforst.evmap.databinding.DialogDataSourceSelectBinding
|
||||
import net.vonforst.evmap.model.FILTERS_DISABLED
|
||||
import net.vonforst.evmap.storage.PreferenceDataSource
|
||||
import java.util.*
|
||||
import net.vonforst.evmap.ui.MaterialDialogFragment
|
||||
|
||||
class DataSourceSelectDialog : AppCompatDialogFragment() {
|
||||
class DataSourceSelectDialog : MaterialDialogFragment() {
|
||||
private lateinit var binding: DialogDataSourceSelectBinding
|
||||
var okListener: ((String) -> Unit)? = null
|
||||
|
||||
@@ -41,16 +40,12 @@ class DataSourceSelectDialog : AppCompatDialogFragment() {
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
// dialog with 95% screen height
|
||||
dialog?.window?.setLayout(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
(resources.displayMetrics.heightPixels * 0.95).toInt()
|
||||
)
|
||||
setFullSize()
|
||||
}
|
||||
|
||||
private lateinit var prefs: PreferenceDataSource
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
override fun initView(view: View, savedInstanceState: Bundle?) {
|
||||
val args = requireArguments()
|
||||
binding.btnCancel.visibility =
|
||||
if (args.getBoolean("cancel_enabled")) View.VISIBLE else View.GONE
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package net.vonforst.evmap.fragment
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.location.Criteria
|
||||
import android.location.LocationManager
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
@@ -29,6 +26,8 @@ import net.vonforst.evmap.adapter.DataBindingAdapter
|
||||
import net.vonforst.evmap.adapter.FavoritesAdapter
|
||||
import net.vonforst.evmap.databinding.FragmentFavoritesBinding
|
||||
import net.vonforst.evmap.databinding.ItemFavoriteBinding
|
||||
import net.vonforst.evmap.location.FusionEngine
|
||||
import net.vonforst.evmap.location.LocationEngine
|
||||
import net.vonforst.evmap.model.Favorite
|
||||
import net.vonforst.evmap.model.FavoriteWithDetail
|
||||
import net.vonforst.evmap.utils.checkAnyLocationPermission
|
||||
@@ -37,7 +36,7 @@ import net.vonforst.evmap.viewmodel.viewModelFactory
|
||||
|
||||
class FavoritesFragment : Fragment() {
|
||||
private lateinit var binding: FragmentFavoritesBinding
|
||||
private lateinit var locationManager: LocationManager
|
||||
private lateinit var locationEngine: LocationEngine
|
||||
private var toDelete: Favorite? = null
|
||||
private var deleteSnackbar: Snackbar? = null
|
||||
private lateinit var adapter: FavoritesAdapter
|
||||
@@ -54,8 +53,7 @@ class FavoritesFragment : Fragment() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
locationManager =
|
||||
requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
locationEngine = FusionEngine(requireContext())
|
||||
|
||||
enterTransition = MaterialFadeThrough()
|
||||
exitTransition = MaterialFadeThrough()
|
||||
@@ -122,11 +120,7 @@ class FavoritesFragment : Fragment() {
|
||||
super.onStart()
|
||||
|
||||
if (requireContext().checkAnyLocationPermission()) {
|
||||
val provider = locationManager.getBestProvider(Criteria().apply {
|
||||
accuracy = Criteria.ACCURACY_FINE
|
||||
}, true) ?: return
|
||||
|
||||
val location = locationManager.getLastKnownLocation(provider)
|
||||
val location = locationEngine.getLastKnownLocation()
|
||||
location?.let {
|
||||
vm.location.value = LatLng(it.latitude, it.longitude)
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.location.Criteria
|
||||
import android.location.Geocoder
|
||||
import android.location.LocationManager
|
||||
import android.os.Bundle
|
||||
import android.text.method.KeyListener
|
||||
import android.view.*
|
||||
@@ -20,7 +18,6 @@ import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.RequiresPermission
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.location.LocationListenerCompat
|
||||
@@ -52,6 +49,7 @@ import com.car2go.maps.model.BitmapDescriptor
|
||||
import com.car2go.maps.model.LatLng
|
||||
import com.car2go.maps.model.Marker
|
||||
import com.car2go.maps.model.MarkerOptions
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.transition.MaterialArcMotion
|
||||
import com.google.android.material.transition.MaterialContainerTransform
|
||||
@@ -75,11 +73,13 @@ import net.vonforst.evmap.adapter.DetailsAdapter
|
||||
import net.vonforst.evmap.adapter.GalleryAdapter
|
||||
import net.vonforst.evmap.adapter.PlaceAutocompleteAdapter
|
||||
import net.vonforst.evmap.api.goingelectric.GoingElectricApiWrapper
|
||||
import net.vonforst.evmap.api.openchargemap.OpenChargeMapApiWrapper
|
||||
import net.vonforst.evmap.autocomplete.ApiUnavailableException
|
||||
import net.vonforst.evmap.autocomplete.PlaceWithBounds
|
||||
import net.vonforst.evmap.bold
|
||||
import net.vonforst.evmap.databinding.FragmentMapBinding
|
||||
import net.vonforst.evmap.location.FusionEngine
|
||||
import net.vonforst.evmap.location.LocationEngine
|
||||
import net.vonforst.evmap.location.Priority
|
||||
import net.vonforst.evmap.model.*
|
||||
import net.vonforst.evmap.storage.PreferenceDataSource
|
||||
import net.vonforst.evmap.ui.*
|
||||
@@ -101,7 +101,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
private val galleryVm: GalleryViewModel by activityViewModels()
|
||||
private var mapFragment: MapFragment? = null
|
||||
private var map: AnyMap? = null
|
||||
private lateinit var locationManager: LocationManager
|
||||
private lateinit var locationEngine: LocationEngine
|
||||
private var requestingLocationUpdates = false
|
||||
private lateinit var bottomSheetBehavior: BottomSheetBehaviorGoogleMapsLike<View>
|
||||
private lateinit var detailAppBarBehavior: MergedAppBarLayoutBehavior
|
||||
@@ -146,8 +146,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
|
||||
prefs = PreferenceDataSource(requireContext())
|
||||
|
||||
locationManager =
|
||||
requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
locationEngine = FusionEngine(requireContext())
|
||||
clusterIconGenerator = ClusterIconGenerator(requireContext())
|
||||
|
||||
enterTransition = MaterialFadeThrough()
|
||||
@@ -363,16 +362,11 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
}
|
||||
binding.detailView.btnChargeprice.setOnClickListener {
|
||||
val charger = vm.charger.value?.data ?: return@setOnClickListener
|
||||
val dataSource = when (vm.apiType) {
|
||||
GoingElectricApiWrapper::class.java -> "going_electric"
|
||||
OpenChargeMapApiWrapper::class.java -> "open_charge_map"
|
||||
else -> throw IllegalArgumentException("unsupported data source")
|
||||
}
|
||||
val extras =
|
||||
FragmentNavigatorExtras(binding.detailView.btnChargeprice to getString(R.string.shared_element_chargeprice))
|
||||
findNavController().navigate(
|
||||
R.id.action_map_to_chargepriceFragment,
|
||||
ChargepriceFragmentArgs(charger, dataSource).toBundle(),
|
||||
ChargepriceFragmentArgs(charger).toBundle(),
|
||||
null, extras
|
||||
)
|
||||
}
|
||||
@@ -605,35 +599,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
updateFavoriteToggle()
|
||||
})
|
||||
vm.searchResult.observe(viewLifecycleOwner, Observer { place ->
|
||||
val map = this.map ?: return@Observer
|
||||
searchResultMarker?.remove()
|
||||
searchResultMarker = null
|
||||
|
||||
if (place != null) {
|
||||
// disable location following when search result is shown
|
||||
vm.myLocationEnabled.value = false
|
||||
if (place.viewport != null) {
|
||||
map.animateCamera(map.cameraUpdateFactory.newLatLngBounds(place.viewport, 0))
|
||||
} else {
|
||||
map.animateCamera(map.cameraUpdateFactory.newLatLngZoom(place.latLng, 12f))
|
||||
}
|
||||
|
||||
if (searchResultIcon == null) {
|
||||
searchResultIcon =
|
||||
map.bitmapDescriptorFactory.fromResource(R.drawable.ic_map_marker)
|
||||
}
|
||||
searchResultMarker = map.addMarker(
|
||||
MarkerOptions()
|
||||
.z(placeSearchZ)
|
||||
.position(place.latLng)
|
||||
.icon(searchResultIcon)
|
||||
.anchor(0.5f, 1f)
|
||||
)
|
||||
} else {
|
||||
binding.search.setText("")
|
||||
}
|
||||
|
||||
updateBackPressedCallback()
|
||||
displaySearchResult(place, moveCamera = true)
|
||||
})
|
||||
vm.layersMenuOpen.observe(viewLifecycleOwner, Observer { open ->
|
||||
binding.fabLayers.visibility = if (open) View.INVISIBLE else View.VISIBLE
|
||||
@@ -650,6 +616,40 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
updateBackPressedCallback()
|
||||
}
|
||||
|
||||
private fun displaySearchResult(place: PlaceWithBounds?, moveCamera: Boolean) {
|
||||
val map = this.map ?: return
|
||||
searchResultMarker?.remove()
|
||||
searchResultMarker = null
|
||||
|
||||
if (place != null) {
|
||||
// disable location following when search result is shown
|
||||
if (moveCamera) {
|
||||
vm.myLocationEnabled.value = false
|
||||
if (place.viewport != null) {
|
||||
map.animateCamera(map.cameraUpdateFactory.newLatLngBounds(place.viewport, 0))
|
||||
} else {
|
||||
map.animateCamera(map.cameraUpdateFactory.newLatLngZoom(place.latLng, 12f))
|
||||
}
|
||||
}
|
||||
|
||||
if (searchResultIcon == null) {
|
||||
searchResultIcon =
|
||||
map.bitmapDescriptorFactory.fromResource(R.drawable.ic_map_marker)
|
||||
}
|
||||
searchResultMarker = map.addMarker(
|
||||
MarkerOptions()
|
||||
.z(placeSearchZ)
|
||||
.position(place.latLng)
|
||||
.icon(searchResultIcon)
|
||||
.anchor(0.5f, 1f)
|
||||
)
|
||||
} else {
|
||||
binding.search.setText("")
|
||||
}
|
||||
|
||||
updateBackPressedCallback()
|
||||
}
|
||||
|
||||
private fun updateBackPressedCallback() {
|
||||
backPressedCallback.isEnabled =
|
||||
vm.bottomSheetState.value != null && vm.bottomSheetState.value != STATE_HIDDEN
|
||||
@@ -738,6 +738,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
}
|
||||
}
|
||||
.withStartPosition(position)
|
||||
.withHiddenStatusBar(false)
|
||||
.show()
|
||||
|
||||
}
|
||||
@@ -810,7 +811,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
it.name
|
||||
}
|
||||
}
|
||||
AlertDialog.Builder(activity)
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.charge_cards)
|
||||
.setItems(names.toTypedArray()) { _, i ->
|
||||
val card = data[i]
|
||||
@@ -1008,7 +1009,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
|
||||
if (vm.searchResult.value != null) {
|
||||
// show search result (after configuration change)
|
||||
vm.searchResult.postValue(vm.searchResult.value)
|
||||
displaySearchResult(vm.searchResult.value, moveCamera = !positionSet)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1026,9 +1027,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
|
||||
@RequiresPermission(anyOf = [ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION])
|
||||
private fun moveToLastLocation(map: AnyMap, animate: Boolean) {
|
||||
val provider = getLocationProvider() ?: return
|
||||
|
||||
val location = locationManager.getLastKnownLocation(provider)
|
||||
val location = locationEngine.getLastKnownLocation()
|
||||
if (location != null) {
|
||||
val latLng = LatLng(location.latitude, location.longitude)
|
||||
vm.location.value = latLng
|
||||
@@ -1041,10 +1040,6 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLocationProvider() = locationManager.getBestProvider(Criteria().apply {
|
||||
accuracy = Criteria.ACCURACY_FINE
|
||||
}, true)
|
||||
|
||||
@Synchronized
|
||||
private fun updateMap(chargepoints: List<ChargepointListItem>) {
|
||||
val map = this.map ?: return
|
||||
@@ -1264,6 +1259,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
}
|
||||
})
|
||||
})
|
||||
popup.setTouchModal(false)
|
||||
popup.show()
|
||||
}
|
||||
|
||||
@@ -1298,12 +1294,9 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
|
||||
@RequiresPermission(anyOf = [ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION])
|
||||
private fun requestLocationUpdates() {
|
||||
val provider = getLocationProvider() ?: return
|
||||
|
||||
locationManager.requestLocationUpdates(
|
||||
provider,
|
||||
locationEngine.requestLocationUpdates(
|
||||
Priority.HIGH_ACCURACY,
|
||||
5000,
|
||||
1f,
|
||||
locationListener
|
||||
)
|
||||
requestingLocationUpdates = true
|
||||
@@ -1312,7 +1305,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun removeLocationUpdates() {
|
||||
if (context?.checkAnyLocationPermission() == true) {
|
||||
locationManager.removeUpdates(locationListener)
|
||||
locationEngine.removeUpdates(locationListener)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,20 +4,16 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.adapter.DataBindingAdapter
|
||||
import net.vonforst.evmap.adapter.Equatable
|
||||
import net.vonforst.evmap.databinding.DialogMultiSelectBinding
|
||||
import net.vonforst.evmap.ui.MaterialDialogFragment
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class MultiSelectDialog : AppCompatDialogFragment() {
|
||||
class MultiSelectDialog : MaterialDialogFragment() {
|
||||
companion object {
|
||||
fun getInstance(
|
||||
title: String,
|
||||
@@ -54,19 +50,10 @@ class MultiSelectDialog : AppCompatDialogFragment() {
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
val density = resources.displayMetrics.density
|
||||
val width = resources.displayMetrics.widthPixels
|
||||
val maxWidth = (500 * density).roundToInt()
|
||||
|
||||
// dialog with 95% screen height
|
||||
dialog?.window?.setLayout(
|
||||
if (width < maxWidth) WindowManager.LayoutParams.MATCH_PARENT else maxWidth,
|
||||
(resources.displayMetrics.heightPixels * 0.95).toInt()
|
||||
)
|
||||
setFullSize(maxWidthDp = 500)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
override fun initView(view: View, savedInstanceState: Bundle?) {
|
||||
val args = requireArguments()
|
||||
val data = args.getSerializable("data") as HashMap<String, String>
|
||||
val selected = args.getSerializable("selected") as HashSet<String>
|
||||
|
||||
@@ -16,7 +16,8 @@ class ChargepriceSettingsFragment : BaseSettingsFragment() {
|
||||
viewModelFactory {
|
||||
SettingsViewModel(
|
||||
requireActivity().application,
|
||||
getString(R.string.chargeprice_key)
|
||||
getString(R.string.chargeprice_key),
|
||||
getString(R.string.chargeprice_api_url)
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -69,7 +70,8 @@ class ChargepriceSettingsFragment : BaseSettingsFragment() {
|
||||
R.plurals.chargeprice_some_tariffs_selected,
|
||||
n,
|
||||
n
|
||||
) + "\n" + getString(R.string.pref_my_tariffs_summary)
|
||||
) + "\n" + requireContext().resources
|
||||
.getQuantityString(R.plurals.pref_my_tariffs_summary, n)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ class DataSettingsFragment : BaseSettingsFragment() {
|
||||
viewModelFactory {
|
||||
SettingsViewModel(
|
||||
requireActivity().application,
|
||||
getString(R.string.chargeprice_key)
|
||||
getString(R.string.chargeprice_key),
|
||||
getString(R.string.chargeprice_api_url)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -4,15 +4,13 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.databinding.DialogOpensourceDonationsBinding
|
||||
import net.vonforst.evmap.storage.PreferenceDataSource
|
||||
import kotlin.math.roundToInt
|
||||
import net.vonforst.evmap.ui.MaterialDialogFragment
|
||||
|
||||
class OpensourceDonationsDialogFramgent : AppCompatDialogFragment() {
|
||||
class OpensourceDonationsDialogFragment : MaterialDialogFragment() {
|
||||
private lateinit var binding: DialogOpensourceDonationsBinding
|
||||
|
||||
override fun onCreateView(
|
||||
@@ -24,9 +22,7 @@ class OpensourceDonationsDialogFramgent : AppCompatDialogFragment() {
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
override fun initView(view: View, savedInstanceState: Bundle?) {
|
||||
val prefs = PreferenceDataSource(requireContext())
|
||||
binding.btnOk.setOnClickListener {
|
||||
prefs.opensourceDonationsDialogShown = true
|
||||
@@ -44,14 +40,5 @@ class OpensourceDonationsDialogFramgent : AppCompatDialogFragment() {
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
val density = resources.displayMetrics.density
|
||||
val width = resources.displayMetrics.widthPixels
|
||||
val maxWidth = (500 * density).roundToInt()
|
||||
|
||||
dialog?.window?.setLayout(
|
||||
if (width < maxWidth) WindowManager.LayoutParams.MATCH_PARENT else maxWidth,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
}
|
||||
}
|
||||
271
app/src/main/java/net/vonforst/evmap/location/FusionEngine.kt
Normal file
271
app/src/main/java/net/vonforst/evmap/location/FusionEngine.kt
Normal file
@@ -0,0 +1,271 @@
|
||||
package net.vonforst.evmap.location
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import android.os.Build
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.RequiresPermission
|
||||
import androidx.core.location.LocationListenerCompat
|
||||
|
||||
/**
|
||||
* Location engine that fuses GPS and network locations.
|
||||
*
|
||||
* Simplified version of
|
||||
* https://github.com/lostzen/lost/blob/master/lost/src/main/java/com/mapzen/android/lost/internal/FusionEngine.java
|
||||
*/
|
||||
class FusionEngine(context: Context) : LocationEngine(context),
|
||||
LocationListenerCompat {
|
||||
|
||||
/**
|
||||
* Location updates more than 60 seconds old are considered stale.
|
||||
*/
|
||||
private val RECENT_UPDATE_THRESHOLD_IN_MILLIS = (60 * 1000).toLong()
|
||||
private val RECENT_UPDATE_THRESHOLD_IN_NANOS = RECENT_UPDATE_THRESHOLD_IN_MILLIS * 1000000
|
||||
private val TAG = FusionEngine::class.java.simpleName
|
||||
|
||||
private val locationManager =
|
||||
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
private var gpsLocation: Location? = null
|
||||
private var networkLocation: Location? = null
|
||||
|
||||
private val supportsSystemFusedProvider: Boolean
|
||||
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && locationManager.allProviders.contains(
|
||||
LocationManager.FUSED_PROVIDER
|
||||
)
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
override fun getLastKnownLocation(): Location? {
|
||||
if (supportsSystemFusedProvider) {
|
||||
try {
|
||||
return locationManager.getLastKnownLocation(LocationManager.FUSED_PROVIDER)
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "Permissions not granted for fused provider", e)
|
||||
}
|
||||
}
|
||||
|
||||
val minTime = SystemClock.elapsedRealtimeNanos() - RECENT_UPDATE_THRESHOLD_IN_NANOS
|
||||
var bestLocation: Location? = null
|
||||
var bestAccuracy = Float.MAX_VALUE
|
||||
var bestTime = Long.MIN_VALUE
|
||||
for (provider in locationManager.allProviders) {
|
||||
try {
|
||||
val location = locationManager.getLastKnownLocation(provider)
|
||||
if (location != null) {
|
||||
val accuracy = location.accuracy
|
||||
val time = location.elapsedRealtimeNanos
|
||||
if (time > minTime && accuracy < bestAccuracy) {
|
||||
bestLocation = location
|
||||
bestAccuracy = accuracy
|
||||
bestTime = time
|
||||
} else if (time < minTime && bestAccuracy == Float.MAX_VALUE && time > bestTime) {
|
||||
bestLocation = location
|
||||
bestTime = time
|
||||
}
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "Permissions not granted for provider: $provider", e)
|
||||
}
|
||||
}
|
||||
return bestLocation
|
||||
}
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
override fun enable() {
|
||||
var networkInterval = Long.MAX_VALUE
|
||||
var gpsInterval = Long.MAX_VALUE
|
||||
var passiveInterval = Long.MAX_VALUE
|
||||
for ((priority, interval) in requests) {
|
||||
when (priority) {
|
||||
Priority.HIGH_ACCURACY -> {
|
||||
if (interval < gpsInterval) {
|
||||
gpsInterval = interval
|
||||
}
|
||||
if (interval < networkInterval) {
|
||||
networkInterval = interval
|
||||
}
|
||||
}
|
||||
Priority.BALANCED_POWER_ACCURACY, Priority.LOW_POWER -> if (interval < networkInterval) {
|
||||
networkInterval = interval
|
||||
}
|
||||
Priority.NO_POWER -> if (interval < passiveInterval) {
|
||||
passiveInterval = interval
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (supportsSystemFusedProvider && gpsInterval < Long.MAX_VALUE) {
|
||||
try {
|
||||
enableFused(gpsInterval)
|
||||
checkLastKnownFused()
|
||||
return
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "Permissions not granted for fused provider", e)
|
||||
}
|
||||
}
|
||||
|
||||
var checkGps = false
|
||||
if (gpsInterval < Long.MAX_VALUE) {
|
||||
enableGps(gpsInterval)
|
||||
checkGps = true
|
||||
}
|
||||
if (networkInterval < Long.MAX_VALUE) {
|
||||
enableNetwork(networkInterval)
|
||||
if (checkGps) {
|
||||
val lastGps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
|
||||
val lastNetwork =
|
||||
locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
|
||||
if (lastGps != null && lastNetwork != null) {
|
||||
val useGps = lastGps.isBetterThan(lastNetwork)
|
||||
if (useGps) {
|
||||
checkLastKnownGps()
|
||||
} else {
|
||||
checkLastKnownNetwork()
|
||||
}
|
||||
} else if (lastGps != null) {
|
||||
checkLastKnownGps()
|
||||
} else {
|
||||
checkLastKnownNetwork()
|
||||
}
|
||||
} else {
|
||||
checkLastKnownNetwork()
|
||||
}
|
||||
}
|
||||
if (passiveInterval < Long.MAX_VALUE) {
|
||||
enablePassive(passiveInterval)
|
||||
checkLastKnownPassive()
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
override fun disable() {
|
||||
locationManager.removeUpdates(this)
|
||||
}
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
private fun enableGps(interval: Long) {
|
||||
try {
|
||||
locationManager.requestLocationUpdates(
|
||||
LocationManager.GPS_PROVIDER,
|
||||
interval,
|
||||
0f,
|
||||
this,
|
||||
looper
|
||||
)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.e(TAG, "Unable to register for GPS updates.", e)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
private fun enableNetwork(interval: Long) {
|
||||
try {
|
||||
locationManager.requestLocationUpdates(
|
||||
LocationManager.NETWORK_PROVIDER,
|
||||
interval,
|
||||
0f,
|
||||
this,
|
||||
looper
|
||||
)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.e(TAG, "Unable to register for network updates.", e)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
private fun enablePassive(interval: Long) {
|
||||
try {
|
||||
locationManager.requestLocationUpdates(
|
||||
LocationManager.PASSIVE_PROVIDER,
|
||||
interval,
|
||||
0f,
|
||||
this,
|
||||
looper
|
||||
)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.e(TAG, "Unable to register for passive updates.", e)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
private fun enableFused(interval: Long) {
|
||||
try {
|
||||
locationManager.requestLocationUpdates(
|
||||
LocationManager.FUSED_PROVIDER,
|
||||
interval,
|
||||
0f,
|
||||
this,
|
||||
looper
|
||||
)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.e(TAG, "Unable to register for passive updates.", e)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
private fun checkLastKnownGps() {
|
||||
checkLastKnownAndNotify(LocationManager.GPS_PROVIDER)
|
||||
}
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
private fun checkLastKnownNetwork() {
|
||||
checkLastKnownAndNotify(LocationManager.NETWORK_PROVIDER)
|
||||
}
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
private fun checkLastKnownPassive() {
|
||||
checkLastKnownAndNotify(LocationManager.PASSIVE_PROVIDER)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
private fun checkLastKnownFused() {
|
||||
checkLastKnownAndNotify(LocationManager.FUSED_PROVIDER)
|
||||
}
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
private fun checkLastKnownAndNotify(provider: String) {
|
||||
val location = locationManager.getLastKnownLocation(provider)
|
||||
location?.let { onLocationChanged(it) }
|
||||
}
|
||||
|
||||
override fun onLocationChanged(location: Location) {
|
||||
if (LocationManager.FUSED_PROVIDER == location.provider) {
|
||||
requests.forEach { it.listener.onLocationChanged(location) }
|
||||
} else if (LocationManager.GPS_PROVIDER == location.provider) {
|
||||
gpsLocation = location
|
||||
if (gpsLocation.isBetterThan(networkLocation)) {
|
||||
requests.forEach { it.listener.onLocationChanged(location) }
|
||||
}
|
||||
} else if (LocationManager.NETWORK_PROVIDER == location.provider) {
|
||||
networkLocation = location
|
||||
if (networkLocation.isBetterThan(gpsLocation)) {
|
||||
requests.forEach { it.listener.onLocationChanged(location) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Location?.isBetterThan(other: Location?): Boolean {
|
||||
if (this == null) {
|
||||
return false
|
||||
}
|
||||
if (other == null) {
|
||||
return true
|
||||
}
|
||||
if (this.elapsedRealtimeNanos
|
||||
> other.elapsedRealtimeNanos + RECENT_UPDATE_THRESHOLD_IN_NANOS
|
||||
) {
|
||||
return true
|
||||
}
|
||||
if (!this.hasAccuracy()) {
|
||||
return false
|
||||
}
|
||||
return if (!other.hasAccuracy()) {
|
||||
true
|
||||
} else this.accuracy < other.accuracy
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package net.vonforst.evmap.location
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.os.Looper
|
||||
import androidx.annotation.RequiresPermission
|
||||
|
||||
/**
|
||||
* Base class for [com.mapzen.android.lost.internal.FusionEngine].
|
||||
*/
|
||||
abstract class LocationEngine(protected val context: Context) {
|
||||
protected val requests: MutableList<LocationRequest> = mutableListOf()
|
||||
|
||||
/**
|
||||
* Return most best recent location available.
|
||||
*/
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
abstract fun getLastKnownLocation(): Location?
|
||||
|
||||
/**
|
||||
* Enables the engine on receiving a valid location request.
|
||||
*
|
||||
* @param request Valid location request to enable.
|
||||
*/
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
fun requestLocationUpdates(priority: Priority, intervalMs: Long, listener: LocationListener) {
|
||||
requests.add(LocationRequest(priority, intervalMs, listener))
|
||||
enable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the engine when no requests remain, otherwise updates the engine's configuration.
|
||||
*
|
||||
* @param requests Valid location request to enable.
|
||||
*/
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
fun removeUpdates(listener: LocationListener) {
|
||||
this.requests.removeIf { it.listener == listener }
|
||||
disable()
|
||||
if (this.requests.isNotEmpty()) enable()
|
||||
}
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
fun removeAllRequests() {
|
||||
requests.clear()
|
||||
disable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass should perform all operations required to enable the engine. (ex. Register for
|
||||
* location updates.)
|
||||
*/
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
protected abstract fun enable()
|
||||
|
||||
/**
|
||||
* Subclass should perform all operations required to disable the engine. (ex. Remove location
|
||||
* updates.)
|
||||
*/
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
|
||||
protected abstract fun disable()
|
||||
protected val looper: Looper
|
||||
get() = context.mainLooper
|
||||
|
||||
interface Callback {
|
||||
fun reportLocation(location: Location)
|
||||
}
|
||||
}
|
||||
|
||||
data class LocationRequest(
|
||||
val priority: Priority,
|
||||
val intervalMs: Long,
|
||||
val listener: LocationListener
|
||||
)
|
||||
|
||||
enum class Priority {
|
||||
HIGH_ACCURACY,
|
||||
BALANCED_POWER_ACCURACY,
|
||||
LOW_POWER,
|
||||
NO_POWER
|
||||
}
|
||||
@@ -167,9 +167,9 @@ data class Cost(
|
||||
fun getStatusText(ctx: Context, emoji: Boolean = false): CharSequence {
|
||||
if (freecharging != null && freeparking != null) {
|
||||
val charging =
|
||||
if (freecharging) ctx.getString(R.string.free) else ctx.getString(R.string.paid)
|
||||
if (freecharging) ctx.getString(R.string.charging_free) else ctx.getString(R.string.charging_paid)
|
||||
val parking =
|
||||
if (freeparking) ctx.getString(R.string.free) else ctx.getString(R.string.paid)
|
||||
if (freeparking) ctx.getString(R.string.parking_free) else ctx.getString(R.string.parking_paid)
|
||||
return if (emoji) {
|
||||
"⚡ $charging · \uD83C\uDD7F️ $parking"
|
||||
} else {
|
||||
@@ -177,7 +177,7 @@ data class Cost(
|
||||
}
|
||||
} else if (freecharging != null) {
|
||||
val charging =
|
||||
if (freecharging) ctx.getString(R.string.free) else ctx.getString(R.string.paid)
|
||||
if (freecharging) ctx.getString(R.string.charging_free) else ctx.getString(R.string.charging_paid)
|
||||
return if (emoji) {
|
||||
"⚡ $charging"
|
||||
} else {
|
||||
@@ -185,7 +185,7 @@ data class Cost(
|
||||
}
|
||||
} else if (freeparking != null) {
|
||||
val parking =
|
||||
if (freeparking) ctx.getString(R.string.free) else ctx.getString(R.string.paid)
|
||||
if (freeparking) ctx.getString(R.string.parking_free) else ctx.getString(R.string.parking_paid)
|
||||
return if (emoji) {
|
||||
"\uD83C\uDD7F $parking"
|
||||
} else {
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
package net.vonforst.evmap.ui
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.view.Gravity
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.EditText
|
||||
import android.widget.FrameLayout
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private fun dialogEditText(ctx: Context): Pair<View, EditText> {
|
||||
val container = FrameLayout(ctx)
|
||||
@@ -24,30 +30,19 @@ private fun dialogEditText(ctx: Context): Pair<View, EditText> {
|
||||
|
||||
fun showEditTextDialog(
|
||||
ctx: Context,
|
||||
customize: (AlertDialog.Builder, EditText) -> Unit
|
||||
customize: (MaterialAlertDialogBuilder, EditText) -> Unit
|
||||
): AlertDialog {
|
||||
val (container, input) = dialogEditText(ctx)
|
||||
val dialogBuilder = AlertDialog.Builder(ctx)
|
||||
val dialogBuilder = MaterialAlertDialogBuilder(ctx)
|
||||
.setView(container)
|
||||
|
||||
customize(dialogBuilder, input)
|
||||
|
||||
val dialog = dialogBuilder.show()
|
||||
|
||||
|
||||
// move dialog to top
|
||||
val attrs = dialog.window?.attributes?.apply {
|
||||
gravity = Gravity.TOP
|
||||
}
|
||||
dialog.window?.attributes = attrs
|
||||
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
|
||||
// focus and show keyboard
|
||||
input.requestFocus()
|
||||
input.postDelayed({
|
||||
val imm =
|
||||
ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.showSoftInput(input, InputMethodManager.SHOW_IMPLICIT)
|
||||
}, 100)
|
||||
input.setOnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
val text = input.text
|
||||
@@ -60,4 +55,57 @@ fun showEditTextDialog(
|
||||
false
|
||||
}
|
||||
return dialog
|
||||
}
|
||||
|
||||
/**
|
||||
* DialogFragment that uses Material styling.
|
||||
* This needs a bit of a workaround, see also
|
||||
* https://github.com/material-components/material-components-android/issues/540 and
|
||||
* https://dev.to/bhullnatik/how-to-use-material-dialogs-with-dialogfragment-28i1
|
||||
*/
|
||||
abstract class MaterialDialogFragment : AppCompatDialogFragment() {
|
||||
|
||||
private lateinit var dialogView: View
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = MaterialAlertDialogBuilder(requireContext(), theme).apply {
|
||||
dialogView =
|
||||
onCreateView(LayoutInflater.from(requireContext()), null, savedInstanceState)!!
|
||||
|
||||
setView(dialogView)
|
||||
}.create()
|
||||
initView(dialogView, savedInstanceState)
|
||||
return dialog
|
||||
}
|
||||
|
||||
abstract fun initView(view: View, savedInstanceState: Bundle?)
|
||||
|
||||
override fun getView(): View {
|
||||
return dialogView
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
// make sure that custom view fills whole dialog height
|
||||
(view.parent as View).layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
(view.parent.parent as View).layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
(view.parent.parent.parent as View).layoutParams.height =
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the dialog fill the whole width & height of the screen, with an optional maximum
|
||||
* width in dp. Call this during onStart.
|
||||
*/
|
||||
fun setFullSize(maxWidthDp: Int? = null) {
|
||||
val width = resources.displayMetrics.widthPixels
|
||||
val maxWidth = if (maxWidthDp != null) {
|
||||
val density = resources.displayMetrics.density
|
||||
(maxWidthDp * density).roundToInt()
|
||||
} else null
|
||||
|
||||
dialog?.window?.setLayout(
|
||||
if (maxWidth == null || width < maxWidth) WindowManager.LayoutParams.MATCH_PARENT else maxWidth,
|
||||
WindowManager.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package net.vonforst.evmap.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.appcompat.view.menu.MenuPopupHelper
|
||||
import androidx.appcompat.widget.MenuPopupWindow
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
|
||||
/**
|
||||
* Reflection workaround to make setTouchModal accessible for
|
||||
*/
|
||||
@SuppressLint("RestrictedApi")
|
||||
fun PopupMenu.setTouchModal(modal: Boolean) {
|
||||
try {
|
||||
val mPopup = javaClass.getDeclaredField("mPopup").let { field ->
|
||||
field.isAccessible = true
|
||||
field.get(this)
|
||||
} as MenuPopupHelper
|
||||
val mPopup2 = mPopup.javaClass.getDeclaredMethod("getPopup").let { method ->
|
||||
method.isAccessible = true
|
||||
method.invoke(mPopup)
|
||||
}
|
||||
val mPopup3 = mPopup2.javaClass.getDeclaredField("mPopup").let { field ->
|
||||
field.isAccessible = true
|
||||
field.get(mPopup2)
|
||||
} as MenuPopupWindow
|
||||
mPopup3.setTouchModal(modal)
|
||||
} catch (e: NoSuchFieldException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: NoSuchMethodException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,12 @@ class LocaleContextWrapper(base: Context?) : ContextWrapper(base) {
|
||||
}
|
||||
} else {
|
||||
// set selected locale
|
||||
val locale = Locale(language)
|
||||
val locale = if (language.contains("-")) {
|
||||
val split = language.split("-")
|
||||
Locale(split[0], split[1])
|
||||
} else {
|
||||
Locale(language)
|
||||
}
|
||||
Locale.setDefault(locale)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
appConfig.setLocale(locale)
|
||||
|
||||
@@ -2,12 +2,12 @@ package net.vonforst.evmap.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.*
|
||||
import jsonapi.Meta
|
||||
import jsonapi.Relationship
|
||||
import jsonapi.Relationships
|
||||
import jsonapi.ResourceIdentifier
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.banana.jsonapi2.HasMany
|
||||
import moe.banana.jsonapi2.HasOne
|
||||
import moe.banana.jsonapi2.JsonBuffer
|
||||
import moe.banana.jsonapi2.ResourceIdentifier
|
||||
import net.vonforst.evmap.api.chargeprice.*
|
||||
import net.vonforst.evmap.api.equivalentPlugTypes
|
||||
import net.vonforst.evmap.model.ChargeLocation
|
||||
@@ -16,30 +16,48 @@ import net.vonforst.evmap.storage.PreferenceDataSource
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class ChargepriceViewModel(application: Application, chargepriceApiKey: String) :
|
||||
class ChargepriceViewModel(
|
||||
application: Application,
|
||||
chargepriceApiKey: String,
|
||||
chargepriceApiUrl: String,
|
||||
private val state: SavedStateHandle
|
||||
) :
|
||||
AndroidViewModel(application) {
|
||||
private var api = ChargepriceApi.create(chargepriceApiKey)
|
||||
private var api = ChargepriceApi.create(chargepriceApiKey, chargepriceApiUrl)
|
||||
private var prefs = PreferenceDataSource(application)
|
||||
|
||||
val charger: MutableLiveData<ChargeLocation> by lazy {
|
||||
MutableLiveData<ChargeLocation>()
|
||||
}
|
||||
|
||||
val dataSource: MutableLiveData<String> by lazy {
|
||||
MutableLiveData<String>()
|
||||
state.getLiveData("charger")
|
||||
}
|
||||
|
||||
val chargepoint: MutableLiveData<Chargepoint> by lazy {
|
||||
MutableLiveData<Chargepoint>()
|
||||
state.getLiveData("chargepoint")
|
||||
}
|
||||
|
||||
val vehicles: MutableLiveData<Resource<List<ChargepriceCar>>> by lazy {
|
||||
MutableLiveData<Resource<List<ChargepriceCar>>>().apply {
|
||||
if (prefs.chargepriceMyVehicles.isEmpty()) {
|
||||
value = Resource.success(emptyList())
|
||||
} else {
|
||||
value = Resource.loading(null)
|
||||
loadVehicles()
|
||||
private val vehicleIds: MutableLiveData<Set<String>> by lazy {
|
||||
MutableLiveData<Set<String>>().apply {
|
||||
value = prefs.chargepriceMyVehicles
|
||||
}
|
||||
}
|
||||
|
||||
val vehicles: LiveData<Resource<List<ChargepriceCar>>> by lazy {
|
||||
MediatorLiveData<Resource<List<ChargepriceCar>>>().apply {
|
||||
addSource(vehicleIds.distinctUntilChanged()) { vehicleIds ->
|
||||
if (vehicleIds.isEmpty()) {
|
||||
value = Resource.success(emptyList())
|
||||
} else {
|
||||
value = Resource.loading(null)
|
||||
viewModelScope.launch {
|
||||
value = try {
|
||||
val result = api.getVehicles()
|
||||
Resource.success(result.filter {
|
||||
it.id in vehicleIds
|
||||
})
|
||||
} catch (e: IOException) {
|
||||
Resource.error(e.message, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
observeForever {
|
||||
vehicle.value = it.data?.firstOrNull()
|
||||
@@ -48,7 +66,7 @@ class ChargepriceViewModel(application: Application, chargepriceApiKey: String)
|
||||
}
|
||||
|
||||
val vehicle: MutableLiveData<ChargepriceCar> by lazy {
|
||||
MutableLiveData<ChargepriceCar>()
|
||||
state.getLiveData("vehicle")
|
||||
}
|
||||
|
||||
val vehicleCompatibleConnectors: LiveData<List<String>> by lazy {
|
||||
@@ -94,21 +112,24 @@ class ChargepriceViewModel(application: Application, chargepriceApiKey: String)
|
||||
}
|
||||
}
|
||||
|
||||
val chargePrices: MediatorLiveData<Resource<List<ChargePrice>>> by lazy {
|
||||
val chargePrices: MutableLiveData<Resource<List<ChargePrice>>> by lazy {
|
||||
MediatorLiveData<Resource<List<ChargePrice>>>().apply {
|
||||
value = Resource.loading(null)
|
||||
value = state["chargePrices"] ?: Resource.loading(null)
|
||||
listOf(
|
||||
charger,
|
||||
dataSource,
|
||||
batteryRange,
|
||||
batteryRangeSliderDragging,
|
||||
vehicleCompatibleConnectors,
|
||||
myTariffs, myTariffsAll
|
||||
).forEach {
|
||||
addSource(it) {
|
||||
addSource(it.distinctUntilChanged()) {
|
||||
if (!batteryRangeSliderDragging.value!!) loadPrices()
|
||||
}
|
||||
}
|
||||
observeForever {
|
||||
// persist data in case fragment gets recreated
|
||||
state["chargePrices"] = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,15 +161,15 @@ class ChargepriceViewModel(application: Application, chargepriceApiKey: String)
|
||||
if (filteredPrices.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
cp.clone().apply {
|
||||
cp.copy(
|
||||
chargepointPrices = filteredPrices
|
||||
}
|
||||
)
|
||||
}
|
||||
}.filterNotNull()
|
||||
.sortedBy { it.chargepointPrices.first().price }
|
||||
.sortedByDescending {
|
||||
prefs.chargepriceMyTariffsAll ||
|
||||
myTariffs != null && it.tariff?.get()?.id in myTariffs
|
||||
myTariffs != null && it.tariffId in myTariffs
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -157,6 +178,10 @@ class ChargepriceViewModel(application: Application, chargepriceApiKey: String)
|
||||
}
|
||||
}
|
||||
|
||||
fun reloadPrefs() {
|
||||
vehicleIds.value = prefs.chargepriceMyVehicles
|
||||
}
|
||||
|
||||
private fun getChargepricePlugType(chargepoint: Chargepoint): String {
|
||||
val index = charger.value!!.chargepointsMerged.indexOf(chargepoint)
|
||||
val type = charger.value!!.chargepriceData!!.plugTypes?.get(index) ?: chargepoint.type
|
||||
@@ -210,10 +235,9 @@ class ChargepriceViewModel(application: Application, chargepriceApiKey: String)
|
||||
val charger = charger.value
|
||||
val car = vehicle.value
|
||||
val compatibleConnectors = vehicleCompatibleConnectors.value
|
||||
val dataSource = dataSource.value
|
||||
val myTariffs = myTariffs.value
|
||||
val myTariffsAll = myTariffsAll.value
|
||||
if (charger == null || car == null || compatibleConnectors == null || dataSource == null || myTariffsAll == null || myTariffsAll == false && myTariffs == null) {
|
||||
if (charger == null || car == null || compatibleConnectors == null || myTariffsAll == null || myTariffsAll == false && myTariffs == null) {
|
||||
chargePrices.value = Resource.error(null, null)
|
||||
return
|
||||
}
|
||||
@@ -223,34 +247,39 @@ class ChargepriceViewModel(application: Application, chargepriceApiKey: String)
|
||||
loadPricesJob?.cancel()
|
||||
loadPricesJob = viewModelScope.launch {
|
||||
try {
|
||||
val result = api.getChargePrices(ChargepriceRequest().apply {
|
||||
dataAdapter = dataSource
|
||||
station = cpStation
|
||||
vehicle = HasOne(car)
|
||||
tariffs = if (!myTariffsAll) {
|
||||
HasMany<ChargepriceTariff>(*myTariffs!!.map {
|
||||
ResourceIdentifier(
|
||||
"tariff",
|
||||
it
|
||||
val result = api.getChargePrices(
|
||||
ChargepriceRequest(
|
||||
dataAdapter = ChargepriceApi.getDataAdapter(charger),
|
||||
station = cpStation,
|
||||
vehicle = car,
|
||||
options = ChargepriceOptions(
|
||||
batteryRange = batteryRange.value!!.map { it.toDouble() },
|
||||
providerCustomerTariffs = prefs.chargepriceShowProviderCustomerTariffs,
|
||||
maxMonthlyFees = if (prefs.chargepriceNoBaseFee) 0.0 else null,
|
||||
currency = prefs.chargepriceCurrency,
|
||||
allowUnbalancedLoad = prefs.chargepriceAllowUnbalancedLoad
|
||||
),
|
||||
relationships = if (!myTariffsAll) {
|
||||
Relationships(
|
||||
"tariffs" to Relationship.ToMany(
|
||||
(myTariffs ?: emptySet()).map {
|
||||
ResourceIdentifier(
|
||||
"tariff",
|
||||
id = it
|
||||
)
|
||||
},
|
||||
meta = Meta.from(
|
||||
ChargepriceRequestTariffMeta(ChargepriceInclude.ALWAYS),
|
||||
ChargepriceApi.moshi
|
||||
)
|
||||
)
|
||||
)
|
||||
}.toTypedArray()).apply {
|
||||
meta = JsonBuffer.create(
|
||||
ChargepriceApi.moshi.adapter(ChargepriceRequestTariffMeta::class.java),
|
||||
ChargepriceRequestTariffMeta(ChargepriceInclude.ALWAYS)
|
||||
)
|
||||
}
|
||||
} else null
|
||||
options = ChargepriceOptions(
|
||||
batteryRange = batteryRange.value!!.map { it.toDouble() },
|
||||
providerCustomerTariffs = prefs.chargepriceShowProviderCustomerTariffs,
|
||||
maxMonthlyFees = if (prefs.chargepriceNoBaseFee) 0.0 else null,
|
||||
currency = prefs.chargepriceCurrency,
|
||||
allowUnbalancedLoad = prefs.chargepriceAllowUnbalancedLoad
|
||||
)
|
||||
}, ChargepriceApi.getChargepriceLanguage())
|
||||
val meta =
|
||||
result.meta.get<ChargepriceMeta>(ChargepriceApi.moshi.adapter(ChargepriceMeta::class.java)) as ChargepriceMeta
|
||||
chargePrices.value = Resource.success(result)
|
||||
} else null
|
||||
), ChargepriceApi.getChargepriceLanguage()
|
||||
)
|
||||
|
||||
val meta = result.meta!!.map(ChargepriceMeta::class.java, ChargepriceApi.moshi)!!
|
||||
chargePrices.value = Resource.success(result.data)
|
||||
chargePriceMeta.value = Resource.success(meta)
|
||||
} catch (e: IOException) {
|
||||
chargePrices.value = Resource.error(e.message, null)
|
||||
@@ -261,17 +290,4 @@ class ChargepriceViewModel(application: Application, chargepriceApiKey: String)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadVehicles() {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val result = api.getVehicles()
|
||||
vehicles.value = Resource.success(result.filter {
|
||||
it.id in prefs.chargepriceMyVehicles
|
||||
})
|
||||
} catch (e: IOException) {
|
||||
vehicles.value = Resource.error(e.message, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,10 @@ import androidx.lifecycle.*
|
||||
import com.car2go.maps.AnyMap
|
||||
import com.car2go.maps.model.LatLng
|
||||
import com.car2go.maps.model.LatLngBounds
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import net.vonforst.evmap.api.ChargepointApi
|
||||
import net.vonforst.evmap.api.availability.ChargeLocationStatus
|
||||
@@ -277,7 +279,11 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
|
||||
}
|
||||
|
||||
suspend fun copyFiltersToCustom() {
|
||||
filterStatus.value?.let { db.filterValueDao().copyFiltersToCustom(it, prefs.dataSource) }
|
||||
filterStatus.value?.let {
|
||||
withContext(Dispatchers.IO) {
|
||||
db.filterValueDao().copyFiltersToCustom(it, prefs.dataSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setMapType(type: AnyMap.Type) {
|
||||
|
||||
@@ -11,9 +11,13 @@ import net.vonforst.evmap.api.chargeprice.ChargepriceTariff
|
||||
import net.vonforst.evmap.storage.AppDatabase
|
||||
import java.io.IOException
|
||||
|
||||
class SettingsViewModel(application: Application, chargepriceApiKey: String) :
|
||||
class SettingsViewModel(
|
||||
application: Application,
|
||||
chargepriceApiKey: String,
|
||||
chargepriceApiUrl: String
|
||||
) :
|
||||
AndroidViewModel(application) {
|
||||
private var api = ChargepriceApi.create(chargepriceApiKey)
|
||||
private var api = ChargepriceApi.create(chargepriceApiKey, chargepriceApiUrl)
|
||||
private var db = AppDatabase.getInstance(application)
|
||||
|
||||
val vehicles: MutableLiveData<Resource<List<ChargepriceCar>>> by lazy {
|
||||
|
||||
@@ -19,6 +19,16 @@ inline fun <VM : ViewModel> viewModelFactory(crossinline f: () -> VM) =
|
||||
override fun <T : ViewModel> create(aClass: Class<T>): T = f() as T
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <VM : ViewModel> savedStateViewModelFactory(crossinline f: (SavedStateHandle) -> VM) =
|
||||
object : AbstractSavedStateViewModelFactory() {
|
||||
override fun <T : ViewModel> create(
|
||||
key: String,
|
||||
modelClass: Class<T>,
|
||||
handle: SavedStateHandle
|
||||
) = f(handle) as T
|
||||
}
|
||||
|
||||
enum class Status {
|
||||
SUCCESS,
|
||||
ERROR,
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<vector android:height="44.11976dp"
|
||||
android:viewportHeight="368.4"
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="233.8dp"
|
||||
android:height="368.4dp"
|
||||
android:viewportWidth="233.8"
|
||||
android:width="28dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M109.8,0h13.6c33.9,1.9 67.1,18.5 87.7,45.8c13.5,17.2 21,38.6 22.7,60.3v8.1c-0.8,42.1 -27.7,76.6 -51,109.4c-26.2,37 -50.4,77.3 -57.1,122.9c-1.8,7.7 0.4,18.5 -8.9,22c-2.2,-1.7 -4.7,-3.1 -6.2,-5.4c-2.7,-25.5 -9.1,-50.7 -20,-73.9c-12.3,-27.1 -29.5,-51.6 -47,-75.6C33,199 23,184.2 14.7,168.3c-13,-23.8 -17.9,-51.9 -12.5,-78.6c4.4,-21.1 15.4,-40.6 30.6,-55.7C53.3,14 81.1,1.8 109.8,0z" />
|
||||
<path
|
||||
android:fillColor="#808080"
|
||||
android:pathData="M90.9,57.3v68.2h18.6v55.8l43.4,-74.4h-24.8l24.8,-49.6H90.9z" />
|
||||
android:viewportHeight="368.4">
|
||||
<path
|
||||
android:pathData="M117,367.4c-0.4,-0.3 -0.8,-0.6 -1.2,-0.9c-1.6,-1.2 -3.1,-2.3 -4.2,-3.7c-2.9,-26.9 -9.6,-51.7 -20.1,-74c-12.4,-27.3 -30.1,-52.4 -47.1,-75.8c-8.7,-12 -19.8,-27.9 -28.8,-45.2C2.3,143.6 -2.1,115.9 3.2,89.9c4.3,-20.4 15,-40 30.3,-55.2C53.6,15.1 81.5,2.8 109.9,1l13.5,0c34.4,1.9 66.9,18.9 86.9,45.4c12.8,16.3 20.8,37.5 22.5,59.8l0,8c-0.7,38.8 -23.7,70.9 -45.9,101.9c-1.7,2.3 -3.3,4.6 -5,6.9c-24.4,34.5 -50.3,76.1 -57.3,123.3c-0.5,2 -0.7,4.3 -0.9,6.5C123.3,359 122.8,364.9 117,367.4z"
|
||||
android:fillColor="#FFFFFF" />
|
||||
<path
|
||||
android:pathData="M123.3,2c34.1,1.9 66.3,18.8 86.2,45c12.6,16.1 20.5,37.1 22.3,59.1l0,8c-0.7,38.5 -23.6,70.5 -45.7,101.3c-1.7,2.3 -3.3,4.6 -5,6.9c-24.5,34.6 -50.5,76.3 -57.4,123.7c-0.5,2.1 -0.7,4.4 -0.9,6.7c-0.5,5.9 -1,11 -5.8,13.4c-0.2,-0.2 -0.5,-0.4 -0.7,-0.5c-1.5,-1.1 -2.9,-2 -3.8,-3.3c-2.9,-26.9 -9.7,-51.8 -20.1,-74C80,261 62.3,235.8 45.2,212.4c-8.7,-11.9 -19.8,-27.8 -28.8,-45.1C3.3,143.3 -1,115.9 4.2,90.1c4.2,-20.2 14.9,-39.6 30,-54.7C54.2,16 81.7,3.8 109.9,2H123.3M123.4,0h-13.6c-28.7,1.8 -56.5,14 -77,34C17.6,49.1 6.6,68.6 2.2,89.7c-5.4,26.7 -0.5,54.8 12.5,78.6C23,184.2 33,199 43.6,213.6c17.5,24 34.7,48.5 47,75.6c10.9,23.2 17.3,48.4 20,73.9c1.5,2.3 4,3.7 6.2,5.4c9.3,-3.5 7.1,-14.3 8.9,-22c6.7,-45.6 30.9,-85.9 57.1,-122.9c23.3,-32.8 50.2,-67.3 51,-109.4v-8.1c-1.7,-21.7 -9.2,-43.1 -22.7,-60.3C190.5,18.5 157.3,1.9 123.4,0L123.4,0z"
|
||||
android:fillColor="#808080" />
|
||||
<path
|
||||
android:pathData="M90.9,57.3v68.2h18.6v55.8l43.4,-74.4h-24.8l24.8,-49.6C152.9,57.3 90.9,57.3 90.9,57.3z"
|
||||
android:fillColor="#808080" />
|
||||
</vector>
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
android:height="16dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path
|
||||
android:fillColor="#dddddd"
|
||||
android:pathData="M12,12m-8.5,0a8.5,8.5 0,1 1,17 0a8.5,8.5 0,1 1,-17 0" />
|
||||
android:fillColor="#808080"
|
||||
android:pathData="M12,12m-9,0a9,9 0,1 1,18 0a9,9 0,1 1,-18 0" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,12m-7.5,0a7.5,7.5 0,1 1,15 0a7.5,7.5 0,1 1,-15 0" />
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
<path
|
||||
android:fillColor="#B5B5B5"
|
||||
android:pathData="M122.2,101.9h16.7h5.7l22.3,-44.6c0,0 -10.2,0 -22.4,0l-1.1,2.2L122.2,101.9z" />
|
||||
<path
|
||||
android:pathData="M123.3,2c34.1,1.9 66.3,18.8 86.2,45c12.6,16.1 20.5,37.1 22.3,59.1l0,8c-0.7,38.5 -23.6,70.5 -45.7,101.3c-1.7,2.3 -3.3,4.6 -5,6.9c-24.5,34.6 -50.5,76.3 -57.4,123.7c-0.5,2.1 -0.7,4.4 -0.9,6.7c-0.5,5.9 -1,11 -5.8,13.4c-0.2,-0.2 -0.5,-0.4 -0.7,-0.5c-1.5,-1.1 -2.9,-2 -3.8,-3.3c-2.9,-26.9 -9.7,-51.8 -20.1,-74C80,261 62.3,235.8 45.2,212.4c-8.7,-11.9 -19.8,-27.8 -28.8,-45.1C3.3,143.3 -1,115.9 4.2,90.1c4.2,-20.2 14.9,-39.6 30,-54.7C54.2,16 81.7,3.8 109.9,2H123.3M123.4,0h-13.6c-28.7,1.8 -56.5,14 -77,34C17.6,49.1 6.6,68.6 2.2,89.7c-5.4,26.7 -0.5,54.8 12.5,78.6C23,184.2 33,199 43.6,213.6c17.5,24 34.7,48.5 47,75.6c10.9,23.2 17.3,48.4 20,73.9c1.5,2.3 4,3.7 6.2,5.4c9.3,-3.5 7.1,-14.3 8.9,-22c6.7,-45.6 30.9,-85.9 57.1,-122.9c23.3,-32.8 50.2,-67.3 51,-109.4v-8.1c-1.7,-21.7 -9.2,-43.1 -22.7,-60.3C190.5,18.5 157.3,1.9 123.4,0L123.4,0z"
|
||||
android:fillColor="#808080" />
|
||||
<path
|
||||
android:fillColor="#808080"
|
||||
android:pathData="M138.9,57.3c-9.7,0 -19.8,0 -26.4,0c-2.5,0 -5.1,0 -7.6,0c-8.2,0 -16.1,0 -21.4,0c-4.1,0 -6.6,0 -6.6,0v68.2h18.6v55.8l43.4,-74.4h-24.8L138.9,57.3z" />
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/topPanel"
|
||||
android:id="@+id/topPane"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="88dp"
|
||||
android:background="@color/colorPrimary"
|
||||
@@ -46,7 +46,7 @@
|
||||
android:textAppearance="@style/TextAppearance.Material3.HeadlineSmall"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/topPanel" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/topPane" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/welcomeText1"
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:background="@{BindingAdaptersKt.tariffBackground(context,!myTariffsAll && myTariffs.contains(item.tariff.get().id), item.branding.backgroundColor)}">
|
||||
android:background="@{BindingAdaptersKt.tariffBackground(context,!myTariffsAll && myTariffs.contains(item.tariffId), item.branding.backgroundColor)}">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtTariff"
|
||||
|
||||
@@ -130,9 +130,6 @@
|
||||
<argument
|
||||
android:name="charger"
|
||||
app:argType="net.vonforst.evmap.model.ChargeLocation" />
|
||||
<argument
|
||||
android:name="dataSource"
|
||||
app:argType="string" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/donate"
|
||||
@@ -141,7 +138,7 @@
|
||||
tools:layout="@layout/fragment_donate" />
|
||||
<dialog
|
||||
android:id="@+id/opensource_donations"
|
||||
android:name="net.vonforst.evmap.fragment.updatedialogs.OpensourceDonationsDialogFramgent"
|
||||
android:name="net.vonforst.evmap.fragment.updatedialogs.OpensourceDonationsDialogFragment"
|
||||
android:label="@string/donation_dialog_title"
|
||||
tools:layout="@layout/dialog_opensource_donations">
|
||||
<action
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_language_names">
|
||||
<item>Gerätesprache verwenden</item>
|
||||
<item>Englisch</item>
|
||||
<item>Deutsch</item>
|
||||
</string-array>
|
||||
<string-array name="pref_darkmode_names">
|
||||
<item>Geräteeinstellung verwenden</item>
|
||||
<item>immer an</item>
|
||||
<item>immer aus</item>
|
||||
</string-array>
|
||||
<string-array name="pref_data_source_names">
|
||||
<item>GoingElectric.de</item>
|
||||
<item>Open Charge Map</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -19,8 +19,10 @@
|
||||
<string name="cost_detail"><![CDATA[<b>Laden:</b> %1$s · <b>Parken:</b> %2$s]]></string>
|
||||
<string name="cost_detail_charging"><![CDATA[<b>%s laden</b>]]></string>
|
||||
<string name="cost_detail_parking"><![CDATA[<b>%s parken</b>]]></string>
|
||||
<string name="free">Kostenlos</string>
|
||||
<string name="paid">Kostenpflichtig</string>
|
||||
<string name="charging_free">Kostenlos</string>
|
||||
<string name="charging_paid">Kostenpflichtig</string>
|
||||
<string name="parking_free">Kostenlos</string>
|
||||
<string name="parking_paid">Kostenpflichtig</string>
|
||||
<string name="amenities">Ladeweile</string>
|
||||
<string name="general_info">Allgemeine Hinweise</string>
|
||||
<string name="realtime_data_unavailable">Echtzeitstatus nicht verfügbar</string>
|
||||
@@ -204,24 +206,13 @@
|
||||
<string name="pref_chargeprice_currency">Währung</string>
|
||||
<string name="pref_my_tariffs">Meine Tarife</string>
|
||||
<string name="chargeprice_all_tariffs_selected">alle Tarife ausgewählt</string>
|
||||
<string name="pref_my_tariffs_summary">(werden im Preisvergleich hervorgehoben)</string>
|
||||
<plurals name="pref_my_tariffs_summary">
|
||||
<item quantity="one">(wird im Preisvergleich hervorgehoben)</item>
|
||||
<item quantity="other">(werden im Preisvergleich hervorgehoben)</item>
|
||||
</plurals>
|
||||
<string name="license">Lizenz</string>
|
||||
<string name="settings_charger_data">Ladesäulen</string>
|
||||
<string name="pref_data_source">Datenquelle</string>
|
||||
<string-array name="pref_chargeprice_currency_names">
|
||||
<item>Schweizer Franken (CHF)</item>
|
||||
<item>Tschechische Krone (CZK)</item>
|
||||
<item>Dänische Krone (DKK)</item>
|
||||
<item>Euro (EUR)</item>
|
||||
<item>Britisches Pfund (GBP)</item>
|
||||
<item>Kroatische Kuna (HRK)</item>
|
||||
<item>Ungarischer Forint (HUF)</item>
|
||||
<item>Isländische Krone (ISK)</item>
|
||||
<item>Norwegische Krone (NOK)</item>
|
||||
<item>Polnischer Złoty (PLN)</item>
|
||||
<item>Schwedische Krone (SEK)</item>
|
||||
<item>US-Dollar (USD)</item>
|
||||
</string-array>
|
||||
<plurals name="chargeprice_some_tariffs_selected">
|
||||
<item quantity="one">%d Tarif ausgewählt</item>
|
||||
<item quantity="other">%d Tarife ausgewählt</item>
|
||||
@@ -262,4 +253,22 @@
|
||||
<string name="pref_map_rotate_gestures_off">Karte bleibt fest nach Norden ausgerichtet</string>
|
||||
<string name="refresh_live_data">Echtzeitstatus aktualisieren</string>
|
||||
<string name="autocomplete_connection_error">Vorschläge konnten nicht geladen werden</string>
|
||||
<string name="pref_language_device_default">Gerätesprache verwenden</string>
|
||||
<string name="pref_darkmode_device_default">Geräteeinstellung verwenden</string>
|
||||
<string name="pref_darkmode_always_on">immer an</string>
|
||||
<string name="pref_darkmode_always_off">immer aus</string>
|
||||
<string name="pref_chargeprice_currency_chf">Schweizer Franken (CHF)</string>
|
||||
<string name="pref_chargeprice_currency_czk">Tschechische Krone (CZK)</string>
|
||||
<string name="pref_chargeprice_currency_dkk">Dänische Krone (DKK)</string>
|
||||
<string name="pref_chargeprice_currency_eur">Euro (EUR)</string>
|
||||
<string name="pref_chargeprice_currency_gbp">Britisches Pfund (GBP)</string>
|
||||
<string name="pref_chargeprice_currency_hrk">Kroatische Kuna (HRK)</string>
|
||||
<string name="pref_chargeprice_currency_huf">Ungarischer Forint (HUF)</string>
|
||||
<string name="pref_chargeprice_currency_isk">Isländische Krone (ISK)</string>
|
||||
<string name="pref_chargeprice_currency_nok">Norwegische Krone (NOK)</string>
|
||||
<string name="pref_chargeprice_currency_pln">Polnischer Złoty (PLN)</string>
|
||||
<string name="pref_chargeprice_currency_sek">Schwedische Krone (SEK)</string>
|
||||
<string name="pref_chargeprice_currency_usd">US-Dollar (USD)</string>
|
||||
<string name="pref_provider_google_maps">Google Maps</string>
|
||||
<string name="pref_provider_osm_mapbox">OpenStreetMap (Mapbox)</string>
|
||||
</resources>
|
||||
|
||||
274
app/src/main/res/values-fr/strings.xml
Normal file
274
app/src/main/res/values-fr/strings.xml
Normal file
@@ -0,0 +1,274 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- tools:ignore="MissingQuantity" is temporary until Weblate 4.14 is released -->
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingQuantity">
|
||||
<string name="app_name">EVMap</string>
|
||||
<string name="title_activity_maps">EVMap</string>
|
||||
<string name="connectors">Connecteurs</string>
|
||||
<string name="no_maps_app_found">Aucune application de navigation trouvée</string>
|
||||
<string name="no_browser_app_found">Aucun navigateur web trouvé</string>
|
||||
<string name="address">Adresse</string>
|
||||
<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="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="holiday">Jour férié</string>
|
||||
<string name="cost_detail"><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>
|
||||
<string name="menu_filter">Filtre</string>
|
||||
<string name="not_implemented">pas encore mis en œuvre</string>
|
||||
<string name="about">À propos d\'EVMap</string>
|
||||
<string name="github_link_title">Code source</string>
|
||||
<string name="settings_ui">Interface utilisateur</string>
|
||||
<string name="privacy">Politique de confidentialité</string>
|
||||
<string name="fav_add">Ajouter aux favoris</string>
|
||||
<string name="pref_navigate_use_maps">Démarrer la navigation immédiatement</string>
|
||||
<string name="coordinates">Coordonnées</string>
|
||||
<string name="pref_navigate_use_maps_on">Le bouton de navigation lance immédiatement la navigation Google Maps</string>
|
||||
<string name="share">Partager</string>
|
||||
<string name="plug_chademo">CHAdeMO</string>
|
||||
<string name="plug_supercharger">Superchargeur Tesla</string>
|
||||
<string name="show_less">moins…</string>
|
||||
<string name="favorites_empty_state">Si vous ajoutez des chargeurs à vos favoris, ils apparaîtront ici.</string>
|
||||
<string name="donate">Faire un don</string>
|
||||
<string name="map_type_satellite">Satellite</string>
|
||||
<string name="map_type_terrain">Terrain</string>
|
||||
<string name="map_type">Type de carte</string>
|
||||
<string name="map_details">Détails de la carte</string>
|
||||
<string name="map_traffic">Trafic</string>
|
||||
<string name="faq">FAQ</string>
|
||||
<string name="faq_desc">Foire aux questions</string>
|
||||
<string name="menu_filters_active">Filtres actifs</string>
|
||||
<string name="filters_activated">Filtres activés</string>
|
||||
<string name="filters_deactivated">Filtres désactivés</string>
|
||||
<string name="menu_manage_filter_profiles">Gérer les profils de filtrage</string>
|
||||
<string name="edit">modifier</string>
|
||||
<string name="pref_language">Langue</string>
|
||||
<string name="pref_language_summary">Changer la langue de l\'application</string>
|
||||
<string name="connection_error">Impossible de charger les stations de recharge</string>
|
||||
<string name="retry">Réessayer</string>
|
||||
<string name="filter_open_247">Disponible 24h/24 et 7j/7</string>
|
||||
<string name="filter_barrierfree">Utilisable sans enregistrement</string>
|
||||
<string name="filter_exclude_faults">Exclure les chargeurs avec des défauts signalés</string>
|
||||
<string name="charge_cards">Méthodes de paiement</string>
|
||||
<string name="goingelectric_forum">Fil de discussion du forum sur GoingElectric.de</string>
|
||||
<string name="edit_at_datasource">modifier à %s</string>
|
||||
<string name="categories">Catégories</string>
|
||||
<string name="category_car_dealership">Concessionnaire automobile</string>
|
||||
<string name="category_public_authorities">Pouvoirs publics</string>
|
||||
<string name="category_church">Église</string>
|
||||
<string name="category_hospital">Hôpital</string>
|
||||
<string name="category_museum">Musée</string>
|
||||
<string name="category_parking_multi">Parking à étages</string>
|
||||
<string name="category_parking">Parking</string>
|
||||
<string name="category_private_charger">Chargeur privé</string>
|
||||
<string name="category_rest_area">Aire de repos</string>
|
||||
<string name="category_parking_underground">Parking souterrain</string>
|
||||
<string name="category_zoo">Zoo</string>
|
||||
<string name="menu_apply">Appliquer les filtres</string>
|
||||
<string name="save_as_profile">Enregistrer en tant que profil</string>
|
||||
<string name="welcome_1">Trouvez des chargeurs de véhicules électriques autour de vous.</string>
|
||||
<string name="welcome_2">La couleur d\'un chargeur sur la carte vous indique sa puissance de charge maximale.</string>
|
||||
<string name="welcome_2_detail">(Vous pouvez vérifier à nouveau les couleurs sous \"À propos d\'EVMap → FAQ\" dans le menu)</string>
|
||||
<string name="donation_dialog_title">Merci d\'utiliser EVMap !</string>
|
||||
<string name="chargeprice_donation_dialog_title">Vous êtes un vrai chasseur de bonnes affaires !</string>
|
||||
<string name="deleted_filterprofile">\"%s\" supprimé</string>
|
||||
<string name="undo">Annuler</string>
|
||||
<string name="rename">Renommer</string>
|
||||
<string name="verified">vérifié</string>
|
||||
<plurals name="charge_cards_compatible_num">
|
||||
<item quantity="one">%d mode de paiement compatible</item>
|
||||
<item quantity="other">%d modes de paiement compatibles</item>
|
||||
</plurals>
|
||||
<string name="verified_desc">Chargeur vérifié par un membre de la communauté %s - ne fonctionne pas forcément en ce moment.</string>
|
||||
<string name="percent_format">%.0f%%</string>
|
||||
<string name="chargeprice_session_fee">frais de session</string>
|
||||
<string name="chargeprice_per_kwh">par kWh</string>
|
||||
<string name="chargeprice_per_minute">par min</string>
|
||||
<string name="chargeprice_blocking_fee">Frais de blocage >%s</string>
|
||||
<string name="chargeprice_no_tariffs_found">Chargeprice.app n\'a trouvé aucun tarif de recharge compatible avec ce chargeur.</string>
|
||||
<string name="pref_chargeprice_show_provider_customer_tariffs">Afficher les tarifs exclusifs aux clients</string>
|
||||
<string name="chargeprice_battery_range">Charge de %1$.0f%% à %2$.0f%%</string>
|
||||
<string name="chargeprice_battery_range_from">Charge de</string>
|
||||
<string name="chargeprice_stats">(%1$.0f kWh, approx. %2$s, ⌀ %3$.0f kW)</string>
|
||||
<string name="chargeprice_vehicle">Véhicule</string>
|
||||
<string name="close">fermer</string>
|
||||
<string name="chargeprice_title">Prix</string>
|
||||
<string name="pref_chargeprice_currency">Devise</string>
|
||||
<string name="data_source_goingelectric">GoingElectric.de</string>
|
||||
<string name="chargeprice_all_tariffs_selected">tous les tarifs sélectionnés</string>
|
||||
<string name="pref_data_source">Source des données</string>
|
||||
<plurals name="chargeprice_some_tariffs_selected">
|
||||
<item quantity="one">%d tarif sélectionné</item>
|
||||
<item quantity="other">%d tarifs sélectionnés</item>
|
||||
</plurals>
|
||||
<string name="data_source_openchargemap">Open Charge Map</string>
|
||||
<string name="next">suivant</string>
|
||||
<string name="get_started">Commencez</string>
|
||||
<string name="crash_report_comment_prompt">Vous pouvez ajouter un commentaire ci-dessous :</string>
|
||||
<string name="github_sponsors">GitHub Sponsors</string>
|
||||
<string name="donate_desc">Soutenir le développement d\'EVMap par un don unique</string>
|
||||
<string name="github_sponsors_desc">Soutenir EVMap sur GitHub Sponsors</string>
|
||||
<string name="unnamed_filter_profile">Profil de filtrage sans nom</string>
|
||||
<string name="privacy_link">https://evmap.vonforst.net/en/privacy.html</string>
|
||||
<string name="required">requis</string>
|
||||
<string name="edit_filter_profile">Modifier \"%s\"</string>
|
||||
<string name="pref_search_delete_recent">Supprimer les résultats de recherche récents</string>
|
||||
<string name="settings_android_auto">Android Auto</string>
|
||||
<string name="pref_chargeprice_allow_unbalanced_load">Permettre une charge déséquilibrée</string>
|
||||
<string name="pref_map_rotate_gestures_enabled">Activer la rotation de la carte</string>
|
||||
<string name="pref_map_rotate_gestures_off">La carte reste orientée vers le nord</string>
|
||||
<string name="refresh_live_data">rafraîchir le statut en temps réel</string>
|
||||
<string name="pref_language_device_default">Utiliser la langue de l\'appareil</string>
|
||||
<string name="pref_darkmode_device_default">Utiliser le réglage de l\'appareil</string>
|
||||
<string name="pref_darkmode_always_on">toujours allumé</string>
|
||||
<string name="pref_darkmode_always_off">toujours éteint</string>
|
||||
<string name="pref_chargeprice_currency_czk">Couronne tchèque (CZK)</string>
|
||||
<string name="pref_chargeprice_currency_dkk">Couronne danoise (DKK)</string>
|
||||
<string name="pref_chargeprice_currency_eur">Euro (EUR)</string>
|
||||
<string name="show_more">plus…</string>
|
||||
<string name="fav_remove">Retirer des favoris</string>
|
||||
<string name="amenities">Commodités</string>
|
||||
<string name="search">Recherche</string>
|
||||
<string name="menu_map">Carte</string>
|
||||
<string name="settings">Paramètres</string>
|
||||
<string name="copyright">Copyright</string>
|
||||
<string name="general_info">Informations générales</string>
|
||||
<string name="realtime_data_loading">Vérification du statut en temps réel…</string>
|
||||
<string name="plug_ccs">CCS</string>
|
||||
<string name="donation_successful">Merci ! ❤️</string>
|
||||
<string name="donation_failed">Quelque chose s\'est mal passé. 😕</string>
|
||||
<string name="category_supermarket">Supermarché</string>
|
||||
<string name="version">Version</string>
|
||||
<string name="oss_licenses">Licences Open Source</string>
|
||||
<string name="realtime_data_source">Source du statut en temps réel (bêta) : %s</string>
|
||||
<string name="plug_type_2">Type 2</string>
|
||||
<string name="plug_type_3">Type 3a</string>
|
||||
<string name="plug_cee_rot">CEE Rouge</string>
|
||||
<string name="all">tous</string>
|
||||
<string name="fault_report_date">Rapport d\'anomalie (dernière mise à jour : %s)</string>
|
||||
<string name="menu_report_new_charger">Signaler un nouveau chargeur</string>
|
||||
<string name="filter_connectors">Connecteurs</string>
|
||||
<string name="copyright_summary">©2020-2022 Johan von Forstner</string>
|
||||
<string name="other">Autre</string>
|
||||
<string name="pref_navigate_use_maps_off">Le bouton de navigation lance l’application de cartes avec l’emplacement du chargeur</string>
|
||||
<string name="settings_map">Carte</string>
|
||||
<string name="fault_report">Rapport d\'anomalie</string>
|
||||
<string name="filter_free">Uniquement des chargeurs gratuits</string>
|
||||
<string name="filter_min_power">Puissance minimale</string>
|
||||
<string name="filter_free_parking">Uniquement les chargeurs avec un parking gratuit</string>
|
||||
<string name="filter_min_connectors">Nombre minimal de connecteurs</string>
|
||||
<string name="plug_type_1">Type 1</string>
|
||||
<string name="plug_schuko">Schuko</string>
|
||||
<string name="plug_cee_blau">CEE Bleu</string>
|
||||
<string name="plug_roadster_hpc">Tesla Roadster (2008) HPC</string>
|
||||
<string name="none">aucun</string>
|
||||
<string name="map_type_normal">Défaut</string>
|
||||
<string name="filter_networks">Réseaux</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="pref_darkmode">Mode sombre</string>
|
||||
<string name="menu_edit_filters">Modifier les filtres</string>
|
||||
<string name="go_to_chargeprice">Comparer les prix</string>
|
||||
<string name="filter_chargecards">Méthodes de paiement</string>
|
||||
<string name="all_selected">Tous sélectionnés</string>
|
||||
<string name="number_selected">%d sélectionné</string>
|
||||
<string name="cancel">Annuler</string>
|
||||
<string name="filter_operators">Opérateurs</string>
|
||||
<string name="chargeprice_donation_dialog_detail">Il semble que vous appréciez beaucoup la fonction de comparaison des prix. Pour accéder aux données de tarification, le développeur d\'EVMap doit payer une redevance mensuelle au fournisseur de données Chargeprice.app. Par conséquent, veuillez envisager de soutenir EVMap par un don.</string>
|
||||
<string name="pref_darkmode_summary">Définir lorsque le mode sombre est activé</string>
|
||||
<string name="and_n_others">et %d autres</string>
|
||||
<string name="contact">Contact</string>
|
||||
<string name="pref_map_provider">Fournisseur de cartes</string>
|
||||
<string name="twitter">Twitter</string>
|
||||
<string name="category_petrol_station">Station-service</string>
|
||||
<string name="edit_on_goingelectric_info">Si seule une page vide s\'affiche ici, veuillez d\'abord vous connecter à GoingElectric.de.</string>
|
||||
<string name="settings_chargeprice">Comparaison des prix</string>
|
||||
<string name="category_service_on_motorway">Aire de service (sur autoroute)</string>
|
||||
<string name="category_railway_station">Gare ferroviaire</string>
|
||||
<string name="category_camping">Camping</string>
|
||||
<string name="category_airport">Aéroport</string>
|
||||
<string name="category_amusement_park">Parc d\'attractions</string>
|
||||
<string name="category_hotel">Hôtel</string>
|
||||
<string name="category_restaurant">Restaurant</string>
|
||||
<string name="filter_favorites">Favoris</string>
|
||||
<string name="reorder">réorganiser</string>
|
||||
<string name="delete">Supprimer</string>
|
||||
<string name="save_profile_enter_name">Saisissez le nom du profil de filtrage :</string>
|
||||
<string name="donation_dialog_detail">EVMap est un logiciel libre et open source que je développe pendant mon temps libre. Les contributions de codage sur GitHub sont très appréciées. Cependant, en raison de la popularité croissante de l\'application, je dois également couvrir certains coûts de fonctionnement, par exemple pour l\'accès aux sources de données. Par conséquent, veuillez envisager de soutenir l\'application par un don ou via les sponsors GitHub.</string>
|
||||
<string name="charging_barrierfree">Utilisable sans enregistrement</string>
|
||||
<string name="chargeprice_battery_range_to">à</string>
|
||||
<string name="category_service_off_motorway">Aire de service (hors autoroute)</string>
|
||||
<string name="category_shopping_mall">Centre commercial</string>
|
||||
<string name="category_cinema">Cinéma</string>
|
||||
<string name="category_swimming_pool">Piscine</string>
|
||||
<string name="menu_save_profile">Enregistrer en tant que profil</string>
|
||||
<string name="no_filters">Aucun filtre</string>
|
||||
<string name="category_holiday_home">Maison de vacances</string>
|
||||
<string name="category_caravan_site">Emplacement pour caravanes</string>
|
||||
<string name="filter_custom">Filtre modifié</string>
|
||||
<string name="filterprofiles_empty_state">Vous n\'avez pas encore enregistré de profils de filtrage.</string>
|
||||
<string name="welcome_to_evmap">Bienvenue sur EVMap</string>
|
||||
<string name="chargeprice_provider_customer_tariff">Uniquement pour les clients du fournisseur</string>
|
||||
<string name="powered_by_chargeprice">alimenté par Chargeprice</string>
|
||||
<string name="pref_my_vehicle">Mes véhicules</string>
|
||||
<string name="pref_my_tariffs">Mes tarifs de recharge</string>
|
||||
<string name="license">Licence</string>
|
||||
<string name="autocomplete_connection_error">Les suggestions n\'ont pas pu être chargées</string>
|
||||
<string name="chargeprice_select_connector">Choisir le connecteur</string>
|
||||
<string name="chargeprice_select_car_first">Veuillez d\'abord sélectionner le modèle de votre voiture dans les paramètres.</string>
|
||||
<string name="pref_chargeprice_show_provider_customer_tariffs_summary">Certains fournisseurs offrent des tarifs moins chers exclusivement à leurs clients (par exemple, électricité domestique, gaz)</string>
|
||||
<string name="pref_chargeprice_no_base_fee">Afficher uniquement les tarifs sans frais mensuels</string>
|
||||
<string name="chargeprice_no_compatible_connectors">Aucun des connecteurs de cette station de charge n\'est compatible avec votre véhicule.</string>
|
||||
<string name="chargeprice_connection_error">Impossible de charger les prix</string>
|
||||
<string name="pref_search_provider">Fournisseur de recherche de lieux</string>
|
||||
<plurals name="pref_my_tariffs_summary">
|
||||
<item quantity="one" tools:ignore="ImpliedQuantity">(sera mis en évidence dans la comparaison des prix)</item>
|
||||
<item quantity="other">(seront mis en évidence dans la comparaison des prix)</item>
|
||||
</plurals>
|
||||
<string name="deleted_recent_search_results">Les résultats de recherche récents ont été supprimés</string>
|
||||
<string name="pref_chargeprice_currency_gbp">Livre sterling (GBP)</string>
|
||||
<string name="pref_chargeprice_currency_isk">Couronne islandaise (ISK)</string>
|
||||
<string name="pref_chargeprice_currency_nok">Couronne norvégienne (NOK)</string>
|
||||
<string name="settings_charger_data">Stations de recharge</string>
|
||||
<string name="got_it">J\'ai compris</string>
|
||||
<string name="powered_by_mapbox">propulsé par Mapbox</string>
|
||||
<string name="lets_go">Allons-y</string>
|
||||
<string name="crash_report_text">Désolé, il semble que EVMap ait 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">Très bonne couverture en Allemagne, en Autriche et en Suisse et dans de nombreux pays voisins. 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="faq_link">https://evmap.vonforst.net/en/faq.html</string>
|
||||
<string name="chargeprice_faq_link">https://evmap.vonforst.net/en/chargeprice_faq.html</string>
|
||||
<string name="settings_data_sources">Sources de données</string>
|
||||
<string name="data_sources_description">EVMap supporte plusieurs sources de données pour les stations de recharge. Veuillez sélectionner celle que vous souhaitez utiliser. Vous pourrez toujours 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. Si vous utilisez souvent cette fonctionnalité, veuillez envisager de faire un don via \"À propos d’EVMap -> Faire un don\".</string>
|
||||
<string name="pref_chargeprice_currency_hrk">Kuna croate (HRK)</string>
|
||||
<string name="help">Aide</string>
|
||||
<string name="pref_chargeprice_allow_unbalanced_load_summary">Autoriser la charge avec >4,5 kW aux stations AC pour les voitures avec chargeur monophasé</string>
|
||||
<string name="pref_chargeprice_currency_huf">Forint hongrois (HUF)</string>
|
||||
<string name="pref_chargeprice_currency_pln">Złoty polonais (PLN)</string>
|
||||
<string name="pref_map_rotate_gestures_on">La carte peut être pivotée avec un geste à deux doigts</string>
|
||||
<string name="pref_chargeprice_currency_chf">Franc suisse (CHF)</string>
|
||||
<string name="pref_chargeprice_currency_usd">Dollar américain (USD)</string>
|
||||
<string name="pref_chargeprice_currency_sek">Couronne suédoise (SEK)</string>
|
||||
<string name="cost_detail_charging"><b>recharge %s</b></string>
|
||||
<string name="cost_detail_parking"><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>
|
||||
<string name="charge_price_kwh_format">%1$.2f %2$s/kWh</string>
|
||||
<string name="chargeprice_base_fee">Frais fixes : %1$.2f %2$s/mois</string>
|
||||
<string name="chargeprice_min_spend">Dépenses minimales : %1$.2f %2$s/mois</string>
|
||||
<string name="welcome_2_title">Visualisez la puissance</string>
|
||||
<string name="pref_provider_google_maps">Google Maps</string>
|
||||
<string name="parking_free">Gratuit</string>
|
||||
<string name="parking_paid">Payant</string>
|
||||
<string name="pref_provider_osm_mapbox">OpenStreetMap (Mapbox)</string>
|
||||
<string name="charging_paid">Payante</string>
|
||||
<string name="charging_free">Gratuite</string>
|
||||
</resources>
|
||||
275
app/src/main/res/values-nb-rNO/strings.xml
Normal file
275
app/src/main/res/values-nb-rNO/strings.xml
Normal file
@@ -0,0 +1,275 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">EVMap</string>
|
||||
<string name="no_maps_app_found">Fant ingen navigeringsprogrammer</string>
|
||||
<string name="closed"><b>Stengt</b></string>
|
||||
<string name="open_closesat"><b>Åpen</b> · Stenger %s</string>
|
||||
<string name="holiday">Ferie</string>
|
||||
<string name="cost">Kostnad</string>
|
||||
<string name="general_info">Generell info</string>
|
||||
<string name="menu_filter">Filter</string>
|
||||
<string name="about">Om EVMap</string>
|
||||
<string name="version">Versjon</string>
|
||||
<string name="settings">Innstillinger</string>
|
||||
<string name="settings_map">Kart</string>
|
||||
<string name="fav_add">Legg til som favoritt</string>
|
||||
<string name="fav_remove">Fjern fra favoritter</string>
|
||||
<string name="share">Del</string>
|
||||
<string name="filter_free">Kun gratisladere</string>
|
||||
<string name="faq">O-S-S</string>
|
||||
<string name="faq_desc">Ofte stilte spørsmål</string>
|
||||
<string name="menu_edit_filters">Rediger filtre</string>
|
||||
<string name="edit">rediger</string>
|
||||
<string name="cancel">Avbryt</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="pref_language">Språk</string>
|
||||
<string name="pref_language_summary">Endre programspråket</string>
|
||||
<string name="and_n_others">og %d andre</string>
|
||||
<string name="pref_map_provider">Karttilbyder</string>
|
||||
<string name="twitter">Twitter</string>
|
||||
<string name="contact">Kontakt</string>
|
||||
<string name="categories">Kategorier</string>
|
||||
<string name="category_airport">Flyplass</string>
|
||||
<string name="category_hotel">Hotell</string>
|
||||
<string name="category_church">Kirke</string>
|
||||
<string name="filter_favorites">Favoritter</string>
|
||||
<string name="delete">Slett</string>
|
||||
<string name="save_as_profile">Lagre som profil</string>
|
||||
<string name="donation_dialog_title">Takk for at du bruker EVMap!</string>
|
||||
<string name="license">Lisens</string>
|
||||
<string name="pref_data_source">Datakilder</string>
|
||||
<string name="required">påkrevd</string>
|
||||
<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="settings_ui">Brukergrensesnitt</string>
|
||||
<string name="title_activity_maps">EVMap</string>
|
||||
<string name="no_browser_app_found">Fant ingen nettlesere</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="menu_map">Kart</string>
|
||||
<string name="category_petrol_station">Bensinstasjon</string>
|
||||
<string name="closed_opensat"><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>
|
||||
<string name="menu_manage_filter_profiles">Håndter filterprofiler</string>
|
||||
<string name="search">Søk</string>
|
||||
<string name="not_implemented">ikke implementert enda</string>
|
||||
<string name="github_link_title">Kildekode</string>
|
||||
<string name="oss_licenses">Frie lisenser</string>
|
||||
<string name="copyright">Opphavsrett</string>
|
||||
<string name="coordinates">Koordinater</string>
|
||||
<string name="fault_report">Feilrapport</string>
|
||||
<string name="privacy">Personvernsmerknad</string>
|
||||
<string name="pref_navigate_use_maps">Start navigasjon umiddelbart</string>
|
||||
<string name="charge_cards">Betalingsmetoder</string>
|
||||
<string name="go_to_chargeprice">Sammenlign priser</string>
|
||||
<string name="filter_networks">Nettverk</string>
|
||||
<string name="filter_chargecards">Betalingsmetoder</string>
|
||||
<string name="category_hospital">Sykehus</string>
|
||||
<string name="menu_save_profile">Lagre som profil</string>
|
||||
<string name="pref_chargeprice_currency">Valuta</string>
|
||||
<string name="next">neste</string>
|
||||
<string name="github_sponsors">GitHub-sponsorer</string>
|
||||
<string name="menu_report_new_charger">Rapporter ny lader</string>
|
||||
<string name="category_private_charger">Privat lader</string>
|
||||
<string name="category_restaurant">Restaurant</string>
|
||||
<string name="category_museum">Museum</string>
|
||||
<string name="category_swimming_pool">Svømmebasseng</string>
|
||||
<string name="unnamed_filter_profile">Filterprofil uten navn</string>
|
||||
<string name="get_started">Begynn</string>
|
||||
<string name="got_it">Skjønner</string>
|
||||
<string name="settings_data_sources">Datakilder</string>
|
||||
<string name="pref_search_delete_recent">Slett nylige søkeresultater</string>
|
||||
<string name="pref_chargeprice_currency_eur">Euro (EUR)</string>
|
||||
<string name="pref_chargeprice_currency_nok">Norske kroner (NOK)</string>
|
||||
<string name="pref_chargeprice_currency_gbp">Britiske pund (GBP)</string>
|
||||
<string name="pref_chargeprice_currency_sek">Svenske kroner (SEK)</string>
|
||||
<string name="realtime_data_loading">Sjekker sanntidsstatus …</string>
|
||||
<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="copyright_summary">©2020–2022 Johan von Forstner</string>
|
||||
<string name="pref_navigate_use_maps_on">Navigasjonsnkappen starter Google Maps-navigasjon umiddelbart</string>
|
||||
<string name="filter_free_parking">Kun ladere med gratis parkering</string>
|
||||
<string name="filter_min_power">Minimumseffekt</string>
|
||||
<string name="plug_type_1">Type 1</string>
|
||||
<string name="plug_chademo">CHAdeMO</string>
|
||||
<string name="plug_schuko">Schuko</string>
|
||||
<string name="plug_supercharger">Tesla Supercharger</string>
|
||||
<string name="plug_type_2">Type 2</string>
|
||||
<string name="plug_type_3">Type 3A</string>
|
||||
<string name="all">alle</string>
|
||||
<string name="none">ingen</string>
|
||||
<string name="show_less">færre …</string>
|
||||
<string name="map_type_satellite">Satellitt</string>
|
||||
<string name="map_type_terrain">Terreng</string>
|
||||
<string name="map_type">Karttype</string>
|
||||
<string name="map_details">Kartdetaljer</string>
|
||||
<string name="map_traffic">Trafikk</string>
|
||||
<string name="favorites_empty_state">Ladere lagret som favoritter vises her.</string>
|
||||
<string name="plug_cee_rot">CEE rød</string>
|
||||
<string name="plug_roadster_hpc">Tesla Roadster (2008) HPC</string>
|
||||
<string name="menu_filters_active">Aktive filtre</string>
|
||||
<string name="fault_report_date">Feilrapport (siste oppdatering: %s)</string>
|
||||
<string name="all_selected">Alle valgt</string>
|
||||
<string name="number_selected">%d valgt</string>
|
||||
<string name="pref_darkmode">Mørk drakt</string>
|
||||
<string name="connection_error">Kunne ikke laste inn ladestasjoner</string>
|
||||
<string name="filter_barrierfree">Kan brukes uten registrering</string>
|
||||
<string name="goingelectric_forum">Forumtråd på GoingElectric.de</string>
|
||||
<string name="category_car_dealership">Bilforhandlere</string>
|
||||
<string name="category_railway_station">Togstasjon</string>
|
||||
<string name="category_public_authorities">Offentlige myndigheter</string>
|
||||
<string name="category_amusement_park">Fornøyelsespark</string>
|
||||
<string name="category_cinema">Kino</string>
|
||||
<string name="category_parking_multi">Parkeringshus</string>
|
||||
<string name="edit_at_datasource">rediger på %s</string>
|
||||
<string name="category_camping">Campingplass</string>
|
||||
<string name="category_service_on_motorway">Rasteplass (på motorvei)</string>
|
||||
<string name="category_shopping_mall">Kjøpesenter</string>
|
||||
<string name="category_holiday_home">Feriehjem</string>
|
||||
<string name="category_parking">Parkeringsplass</string>
|
||||
<string name="category_rest_area">Rasteplass</string>
|
||||
<string name="category_supermarket">Supermarked</string>
|
||||
<string name="menu_apply">Bruk filtre</string>
|
||||
<string name="no_filters">Ingen filtre</string>
|
||||
<string name="category_zoo">Dyrehage</string>
|
||||
<string name="category_caravan_site">Campingplass</string>
|
||||
<string name="category_parking_underground">Parkeringsgarasje under bakken</string>
|
||||
<string name="reorder">endre rekkefølge</string>
|
||||
<string name="save_profile_enter_name">Skriv inn navnet på filterprofilen:</string>
|
||||
<string name="filterprofiles_empty_state">Du har ikke lagret noen filterprofiler.</string>
|
||||
<string name="chargeprice_donation_dialog_title">Du er en sann gjerrigknark.</string>
|
||||
<string name="deleted_filterprofile">Slettet «%s»</string>
|
||||
<string name="charging_barrierfree">Kan brukes uten registrering</string>
|
||||
<string name="welcome_1">Finn kjøretøyladere der du er.</string>
|
||||
<string name="welcome_2">Maksimal ladeeffekt er angitt ved forskjellige farger på respektive ladere i kartet.</string>
|
||||
<string name="welcome_2_detail">(Du kan sjekke fargene igjen i «Om EVMap → O-S-S» i menyen)</string>
|
||||
<string name="verified_desc">Lader bekreftet av et medlem av %s-gemenskapen. Dette betyr ikke at den virker nå.</string>
|
||||
<string name="charge_price_format">%2$s%1$.2f</string>
|
||||
<string name="charge_price_average_format">⌀ %2$s%1$.2f/kWt</string>
|
||||
<string name="charge_price_kwh_format">%2$s%1$.2f/kWt</string>
|
||||
<string name="chargeprice_per_kwh">per kWt</string>
|
||||
<string name="percent_format">%.0f%%</string>
|
||||
<string name="chargeprice_per_minute">per min</string>
|
||||
<string name="chargeprice_min_spend">Minimumskostnad: %2$s%1$.2f/måned</string>
|
||||
<string name="settings_chargeprice">Prissammenligning</string>
|
||||
<string name="pref_my_vehicle">Mine kjøretøy</string>
|
||||
<string name="chargeprice_battery_range_to">til</string>
|
||||
<string name="chargeprice_stats">(%1$.0f kWt, omtrentlig. %2$s, ⌀ %3$.0f kW)</string>
|
||||
<string name="chargeprice_select_car_first">Velg bilen din i innstillingene først.</string>
|
||||
<string name="chargeprice_battery_range">Lad fra %1$.0f%% til %2$.0f%%</string>
|
||||
<string name="chargeprice_battery_range_from">Lad fra</string>
|
||||
<string name="chargeprice_vehicle">Kjøretøy</string>
|
||||
<string name="close">lukk</string>
|
||||
<string name="chargeprice_title">Priser</string>
|
||||
<string name="chargeprice_connection_error">Kunne ikke laste inn priser</string>
|
||||
<plurals name="pref_my_tariffs_summary">
|
||||
<item quantity="one">(vil bli framhevet i prissammenligningen)</item>
|
||||
<item quantity="other">(vil bli framhevet i prissammenligningen)</item>
|
||||
</plurals>
|
||||
<string name="settings_charger_data">Ladestasjoner</string>
|
||||
<string name="data_source_goingelectric">GoingElectric.de</string>
|
||||
<string name="data_source_openchargemap">Open Charge Map</string>
|
||||
<string name="crash_report_comment_prompt">Du kan legge til en kommentar nedenfor:</string>
|
||||
<string name="github_sponsors_desc">Støtt EVMap med GitHub-sponsorer</string>
|
||||
<string name="donate_desc">Støtt utviklingen av EVMap med en engangsdonasjon</string>
|
||||
<string name="settings_android_auto">Android Auto</string>
|
||||
<string name="pref_map_rotate_gestures_enabled">Skru på kartrotasjon</string>
|
||||
<string name="deleted_recent_search_results">Nylige søkeresultater slettet</string>
|
||||
<string name="autocomplete_connection_error">Kunne ikke laste inn forslag</string>
|
||||
<string name="pref_language_device_default">Enhetsforvalg</string>
|
||||
<string name="pref_chargeprice_currency_chf">Sveitserfranc (CHF)</string>
|
||||
<string name="pref_chargeprice_currency_czk">Tsjekkiske kroner (CZK)</string>
|
||||
<string name="pref_chargeprice_currency_dkk">Danske kroner (DKK)</string>
|
||||
<string name="pref_chargeprice_currency_hrk">Kroatiske kroner (HRK)</string>
|
||||
<string name="pref_map_rotate_gestures_on">Kartet kan roteres med to fingre</string>
|
||||
<string name="pref_map_rotate_gestures_off">Kartet vil orienteres mot nord</string>
|
||||
<string name="refresh_live_data">oppdater sanntidsstatus</string>
|
||||
<string name="pref_chargeprice_currency_isk">Islandske kroner (ISK)</string>
|
||||
<string name="pref_chargeprice_currency_pln">Polske zloty (PLN)</string>
|
||||
<string name="pref_chargeprice_currency_usd">Amerikanske dollar (USD)</string>
|
||||
<string name="filters_deactivated">Filtre deaktivert</string>
|
||||
<string name="pref_navigate_use_maps_off">Navigasjonsknapp starter kartprogram med laderposisjon</string>
|
||||
<string name="show_more">flere …</string>
|
||||
<string name="filters_activated">Filtre aktivert</string>
|
||||
<string name="donate">Doner</string>
|
||||
<string name="donation_successful">Takk. ❤️</string>
|
||||
<string name="map_type_normal">Forvalg</string>
|
||||
<string name="donation_failed">Noe gikk galt. 😕</string>
|
||||
<string name="filter_custom">Endret filter</string>
|
||||
<string name="welcome_to_evmap">Velkommen til EVMap</string>
|
||||
<string name="rename">Gi nytt navn</string>
|
||||
<string name="undo">Angre</string>
|
||||
<plurals name="charge_cards_compatible_num">
|
||||
<item quantity="one">%d kompatibel betalingsmetode</item>
|
||||
<item quantity="other">%d kompatible betalingsmetoder</item>
|
||||
</plurals>
|
||||
<string name="plug_ccs">CCS</string>
|
||||
<string name="verified">bekreftet</string>
|
||||
<string name="pref_darkmode_always_on">alltid på</string>
|
||||
<string name="pref_darkmode_device_default">Enhetsforvalg</string>
|
||||
<string name="pref_darkmode_always_off">alltid av</string>
|
||||
<string name="filter_exclude_faults">Utelat ladere med rapporterte feil</string>
|
||||
<string name="plug_cee_blau">CEE blå</string>
|
||||
<string name="filter_open_247">Døgnåpent</string>
|
||||
<string name="pref_chargeprice_currency_huf">Ungarske forint (HUF)</string>
|
||||
<string name="donation_dialog_detail">Fri programvare utviklet på fritiden. Kodebidrag mottas med takk. Siden programmet er mer og mer populært må driftskostnader dekkes. Overvei å gi din støtte gjennom GitHub-sponsorer.</string>
|
||||
<string name="pref_chargeprice_allow_unbalanced_load_summary">Tillat lading over 4.5 kW på vekselstrømsstasjoner med enfaselader</string>
|
||||
<string name="connectors">Tilkobling</string>
|
||||
<string name="operator">Operatør</string>
|
||||
<string name="amenities">Tilleggstjenester</string>
|
||||
<string name="filter_min_connectors">Min. antall tilkoblinger</string>
|
||||
<string name="filter_connectors">Tilkoblinger</string>
|
||||
<string name="filter_operators">Operatører</string>
|
||||
<string name="pref_darkmode_summary">Når mørk drakt er iført</string>
|
||||
<string name="category_service_off_motorway">Rasteplass (ikke på motorvei)</string>
|
||||
<string name="welcome_2_title">Effekten til veies tilgjengeliggjøres</string>
|
||||
<string name="chargeprice_donation_dialog_detail">Du bruker prissammenligningen en del. Dette bekostes av utvikleren som månedlig avgift til Chargeprice.app-datatilbyderen.
|
||||
\nOvervei å støtte EVMap med en donasjon.</string>
|
||||
<string name="navigate">Navigasjon</string>
|
||||
<string name="chargeprice_session_fee">startgebyr</string>
|
||||
<string name="powered_by_chargeprice">tilbudt av Chargeprice</string>
|
||||
<string name="chargeprice_base_fee">Grunnkostnad: %2$s%1$.2f/måned</string>
|
||||
<string name="chargeprice_no_tariffs_found">Chargeprice.app fant ikke noen ladeabonnementer kompatible med denne laderen.</string>
|
||||
<string name="pref_chargeprice_no_base_fee">Kun vis abonnementer uten månedlige gebyr</string>
|
||||
<string name="chargeprice_select_connector">Velg tilkobling</string>
|
||||
<string name="chargeprice_provider_customer_tariff">Kun for tilbyderkunder</string>
|
||||
<string name="chargeprice_blocking_fee">Blokkeringsgebyr >%s</string>
|
||||
<string name="pref_chargeprice_show_provider_customer_tariffs">Vis abonnementer fra kundekoblingssalg</string>
|
||||
<string name="pref_chargeprice_show_provider_customer_tariffs_summary">Noen tilbydere gir billigere abonnementer til sine kunder (f.eks. husstandselektrisitet, gass, osv)</string>
|
||||
<string name="pref_my_tariffs">Mine ladeabonnementer</string>
|
||||
<string name="chargeprice_no_compatible_connectors">Ingen av tilkoblingene på denne ladestasjonen er kompatible med ditt kjøretøy.</string>
|
||||
<string name="data_sources_description">Flere datakilder støttes for innhenting av stasjoner. Velg den du ønsker å bruke og gjør endringer senere i innstillingene hvis nødvendig.</string>
|
||||
<string name="unknown_operator">Ukjent operatør</string>
|
||||
<plurals name="chargeprice_some_tariffs_selected">
|
||||
<item quantity="one">%d plan valgt</item>
|
||||
<item quantity="other">%d planer valgt</item>
|
||||
</plurals>
|
||||
<string name="data_source_goingelectric_desc">Veldig god dekning i Tyskland, Østerrike, Sveits, og mange land i nærheten. Beskrivelser på tysk. Gemenskapsdrevet.</string>
|
||||
<string name="powered_by_mapbox">tilbudt av Mapbox</string>
|
||||
<string name="pref_search_provider_info">Data for stedssøk. Spesielt for Google Maps er dette relativt kostbart. Hvis du bruker dette ofte bes du om å donere gjennom «Om EVMap → Doner».</string>
|
||||
<string name="data_source_openchargemap_desc">Verdensomspennende dekning med varierende kvalitet. Beskrivelser på engelsk eller lokalt språk. Gemenskapsdrevet og åpen data fra myndigheter i noen land (f.eks. Nord-Amerika, Storbritannia, Frankrike, Norge.)</string>
|
||||
<string name="lets_go">Begynn</string>
|
||||
<string name="crash_report_text">EVMap har krasjet. Send en rapport til utvikleren.</string>
|
||||
<string name="chargeprice_all_tariffs_selected">alle planer valgt</string>
|
||||
<string name="pref_search_provider">Stedssøkstilbyder</string>
|
||||
<string name="pref_chargeprice_allow_unbalanced_load">Tillat skjev-belastning</string>
|
||||
<string name="edit_on_goingelectric_info">Hvis en tom side vises her må du logge inn på GoingElectric.de først.</string>
|
||||
<string name="pref_provider_google_maps">Google Maps</string>
|
||||
<string name="pref_provider_osm_mapbox">OpenStreetMap (Mapbox)</string>
|
||||
<string name="charging_free">Gratis</string>
|
||||
<string name="parking_free">Gratis</string>
|
||||
<string name="charging_paid">Betalt</string>
|
||||
<string name="parking_paid">Betalt</string>
|
||||
<string name="privacy_link">https://evmap.vonforst.net/en/privacy.html</string>
|
||||
<string name="faq_link">https://evmap.vonforst.net/en/faq.html</string>
|
||||
<string name="chargeprice_faq_link">https://evmap.vonforst.net/en/chargeprice_faq.html</string>
|
||||
</resources>
|
||||
@@ -1,40 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_language_names">
|
||||
<item>Device default</item>
|
||||
<item>English</item>
|
||||
<item>German</item>
|
||||
<item>@string/pref_language_device_default</item>
|
||||
<item>@string/pref_language_en</item>
|
||||
<item>@string/pref_language_de</item>
|
||||
<item>@string/pref_language_fr</item>
|
||||
<item>@string/pref_language_nb_rNO</item>
|
||||
</string-array>
|
||||
<string-array name="pref_language_values" tranlatable="false">
|
||||
<string-array name="pref_language_values" translatable="false">
|
||||
<item>default</item>
|
||||
<item>en</item>
|
||||
<item>de</item>
|
||||
<item>fr</item>
|
||||
<item>nb-NO</item>
|
||||
</string-array>
|
||||
<string-array name="pref_darkmode_names">
|
||||
<item>Device default</item>
|
||||
<item>always on</item>
|
||||
<item>always off</item>
|
||||
<item>@string/pref_darkmode_device_default</item>
|
||||
<item>@string/pref_darkmode_always_on</item>
|
||||
<item>@string/pref_darkmode_always_off</item>
|
||||
</string-array>
|
||||
<string-array name="pref_darkmode_values" tranlatable="false">
|
||||
<string-array name="pref_darkmode_values" translatable="false">
|
||||
<item>default</item>
|
||||
<item>on</item>
|
||||
<item>off</item>
|
||||
</string-array>
|
||||
<string-array name="pref_chargeprice_currency_names">
|
||||
<item>Swiss franc (CHF)</item>
|
||||
<item>Czech koruna (CZK)</item>
|
||||
<item>Danish krone (DKK)</item>
|
||||
<item>Euro (EUR)</item>
|
||||
<item>Pound sterling (GBP)</item>
|
||||
<item>Croatian kuna (HRK)</item>
|
||||
<item>Hungarian forint (HUF)</item>
|
||||
<item>Icelandic króna (ISK)</item>
|
||||
<item>Norwegian krone (NOK)</item>
|
||||
<item>Polish złoty (PLN)</item>
|
||||
<item>Swedish krona (SEK)</item>
|
||||
<item>US dollar (USD)</item>
|
||||
<item>@string/pref_chargeprice_currency_chf</item>
|
||||
<item>@string/pref_chargeprice_currency_czk</item>
|
||||
<item>@string/pref_chargeprice_currency_dkk</item>
|
||||
<item>@string/pref_chargeprice_currency_eur</item>
|
||||
<item>@string/pref_chargeprice_currency_gbp</item>
|
||||
<item>@string/pref_chargeprice_currency_hrk</item>
|
||||
<item>@string/pref_chargeprice_currency_huf</item>
|
||||
<item>@string/pref_chargeprice_currency_isk</item>
|
||||
<item>@string/pref_chargeprice_currency_nok</item>
|
||||
<item>@string/pref_chargeprice_currency_pln</item>
|
||||
<item>@string/pref_chargeprice_currency_sek</item>
|
||||
<item>@string/pref_chargeprice_currency_usd</item>
|
||||
</string-array>
|
||||
<string-array name="pref_chargeprice_currency_values" donottranslate="true">
|
||||
<string-array name="pref_chargeprice_currency_values" translatable="false">
|
||||
<item>CHF</item>
|
||||
<item>CZK</item>
|
||||
<item>DKK</item>
|
||||
@@ -49,8 +53,8 @@
|
||||
<item>USD</item>
|
||||
</string-array>
|
||||
<string-array name="pref_data_source_names">
|
||||
<item>GoingElectric.de</item>
|
||||
<item>Open Charge Map</item>
|
||||
<item>@string/data_source_goingelectric</item>
|
||||
<item>@string/data_source_openchargemap</item>
|
||||
</string-array>
|
||||
<string-array name="pref_data_source_values" tranlatable="false">
|
||||
<item>goingelectric</item>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<color name="colorSecondaryDark">#00b249</color>
|
||||
<color name="colorSecondaryContainer">#b5f4c7</color>
|
||||
<color name="colorOnSecondaryContainer">#007c00</color>
|
||||
<color name="charger_100kw">#ffeb3b</color>
|
||||
<color name="charger_100kw">#FDD835</color>
|
||||
<color name="charger_43kw">#ff9800</color>
|
||||
<color name="charger_20kw">#03a9f4</color>
|
||||
<color name="charger_11kw">#9e9e9e</color>
|
||||
|
||||
@@ -7,4 +7,9 @@
|
||||
<string name="twitter_url">https://twitter.com/ev_map</string>
|
||||
<string name="goingelectric_forum_url"><![CDATA[https://www.goingelectric.de/forum/viewtopic.php?f=5&t=56342]]></string>
|
||||
<string name="github_sponsors_link">https://github.com/sponsors/johan12345/</string>
|
||||
<string name="chargeprice_api_url">https://api.chargeprice.app/v1/</string>
|
||||
<string name="pref_language_en">English</string>
|
||||
<string name="pref_language_de">Deutsch</string>
|
||||
<string name="pref_language_fr">Français</string>
|
||||
<string name="pref_language_nb_rNO">Norsk Bokmål</string>
|
||||
</resources>
|
||||
@@ -18,8 +18,10 @@
|
||||
<string name="cost_detail"><![CDATA[<b>Charging:</b> %1$s · <b>Parking:</b> %2$s]]></string>
|
||||
<string name="cost_detail_charging"><![CDATA[<b>%s charging</b>]]></string>
|
||||
<string name="cost_detail_parking"><![CDATA[<b>%s parking</b>]]></string>
|
||||
<string name="free">Free</string>
|
||||
<string name="paid">Paid</string>
|
||||
<string name="charging_free">Free</string>
|
||||
<string name="charging_paid">Paid</string>
|
||||
<string name="parking_free">Free</string>
|
||||
<string name="parking_paid">Paid</string>
|
||||
<string name="amenities">Amenities</string>
|
||||
<string name="general_info">General information</string>
|
||||
<string name="realtime_data_unavailable">Real-time status unavailable</string>
|
||||
@@ -202,7 +204,10 @@
|
||||
<string name="chargeprice_no_compatible_connectors">None of the connectors on this charging station is compatible with your vehicle.</string>
|
||||
<string name="pref_chargeprice_currency">Currency</string>
|
||||
<string name="pref_my_tariffs">My charging plans</string>
|
||||
<string name="pref_my_tariffs_summary">(will be highlighted in price comparison)</string>
|
||||
<plurals name="pref_my_tariffs_summary">
|
||||
<item quantity="one">(will be highlighted in price comparison)</item>
|
||||
<item quantity="other">(will be highlighted in price comparison)</item>
|
||||
</plurals>
|
||||
<string name="chargeprice_all_tariffs_selected">all plans selected</string>
|
||||
<string name="license">License</string>
|
||||
<string name="settings_charger_data">Charging stations</string>
|
||||
@@ -247,4 +252,22 @@
|
||||
<string name="pref_map_rotate_gestures_off">Map will be fixed to north-up</string>
|
||||
<string name="refresh_live_data">refresh real-time status</string>
|
||||
<string name="autocomplete_connection_error">Suggestions could not be loaded</string>
|
||||
<string name="pref_language_device_default">Device default</string>
|
||||
<string name="pref_darkmode_device_default">Device default</string>
|
||||
<string name="pref_darkmode_always_on">always on</string>
|
||||
<string name="pref_darkmode_always_off">always off</string>
|
||||
<string name="pref_chargeprice_currency_chf">Swiss franc (CHF)</string>
|
||||
<string name="pref_chargeprice_currency_czk">Czech koruna (CZK)</string>
|
||||
<string name="pref_chargeprice_currency_dkk">Danish krone (DKK)</string>
|
||||
<string name="pref_chargeprice_currency_eur">Euro (EUR)</string>
|
||||
<string name="pref_chargeprice_currency_gbp">Pound sterling (GBP)</string>
|
||||
<string name="pref_chargeprice_currency_hrk">Croatian kuna (HRK)</string>
|
||||
<string name="pref_chargeprice_currency_huf">Hungarian forint (HUF)</string>
|
||||
<string name="pref_chargeprice_currency_isk">Icelandic króna (ISK)</string>
|
||||
<string name="pref_chargeprice_currency_nok">Norwegian krone (NOK)</string>
|
||||
<string name="pref_chargeprice_currency_pln">Polish złoty (PLN)</string>
|
||||
<string name="pref_chargeprice_currency_sek">Swedish krona (SEK)</string>
|
||||
<string name="pref_chargeprice_currency_usd">US dollar (USD)</string>
|
||||
<string name="pref_provider_google_maps">Google Maps</string>
|
||||
<string name="pref_provider_osm_mapbox">OpenStreetMap (Mapbox)</string>
|
||||
</resources>
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
<item name="colorOnSecondaryContainer">@color/colorSecondaryDark</item>
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
<item name="preferenceTheme">@style/AppTheme.Preference</item>
|
||||
<item name="alertDialogTheme">@style/AppTheme.AlertDialog</item>
|
||||
<item name="materialAlertDialogTheme">@style/AppTheme.AlertDialog</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Preference" parent="@style/PreferenceThemeOverlay">
|
||||
@@ -67,4 +69,10 @@
|
||||
<item name="iconTint">?android:textColorSecondary</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AlertDialog" parent="ThemeOverlay.Material3.MaterialAlertDialog">
|
||||
<!-- this is necessary to make sure the dialog gets "pushed up" when the keyboard appears -->
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
<item name="dialogCornerRadius">28dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
app:defaultToAll="false" />
|
||||
<net.vonforst.evmap.ui.MultiSelectDialogPreference
|
||||
android:key="chargeprice_my_tariffs"
|
||||
android:title="@string/pref_my_tariffs"
|
||||
android:summary="@string/pref_my_tariffs_summary" />
|
||||
android:title="@string/pref_my_tariffs" />
|
||||
<ListPreference
|
||||
android:key="chargeprice_currency"
|
||||
android:title="@string/pref_chargeprice_currency"
|
||||
|
||||
@@ -63,14 +63,14 @@ class ChargepriceApiTest {
|
||||
|
||||
runBlocking {
|
||||
val result = chargeprice.getChargePrices(
|
||||
ChargepriceRequest().apply {
|
||||
dataAdapter = "going_electric"
|
||||
ChargepriceRequest(
|
||||
dataAdapter = "going_electric",
|
||||
station =
|
||||
ChargepriceStation.fromEvmap(charger, listOf("Typ2", "Schuko"))
|
||||
ChargepriceStation.fromEvmap(charger, listOf("Typ2", "Schuko")),
|
||||
options = ChargepriceOptions(energy = 22.0, duration = 60)
|
||||
}, "en"
|
||||
), "en"
|
||||
)
|
||||
assertEquals(25, result.size)
|
||||
assertEquals(25, result.data!!.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ allprojects {
|
||||
google()
|
||||
mavenCentral()
|
||||
//noinspection JcenterRepositoryObsolete
|
||||
jcenter() // still required for https://github.com/kamikat/moshi-jsonapi
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,9 @@ Not all API keys are strictly required if you only want to work on certain parts
|
||||
example, you can choose only one of the map providers and one of the charging station databases. The
|
||||
Chargeprice API key is also only required if you want to test the price comparison feature.
|
||||
|
||||
All API keys are available for free. Some APIs require payment above a certain limit, but the free
|
||||
tier should be plenty for local testing and development.
|
||||
All APIs can be used for free, at least for testing. Some APIs require payment above a certain usage
|
||||
limit or to get access to the full dataset, but the free tiers should be plenty for local testing
|
||||
and development.
|
||||
|
||||
Below you find a list of all the services and how to obtain the API keys.
|
||||
|
||||
@@ -152,14 +153,19 @@ Pricing providers
|
||||
<details>
|
||||
<summary>How to obtain an API key</summary>
|
||||
|
||||
1. Check the
|
||||
[Pricing page](https://github.com/chargeprice/chargeprice-api-docs/blob/master/plans.md)
|
||||
for information on the current plans at Chargeprice. There should be a free tier up to a certain
|
||||
limit of API calls per month.
|
||||
2. Contact [contact@chargeprice.net](mailto:contact@chargeprice.net), stating that you would like to
|
||||
contribute to the development the open source EVMap app and therefore need access to the
|
||||
Chargeprice API for testing.
|
||||
3. When your access to the API is approved, you will receive an API key via email.
|
||||
Since February 2022, the Chargeprice API is no longer available for free to new customers. However,
|
||||
you can use their
|
||||
[staging API](https://github.com/chargeprice/chargeprice-api-docs/blob/master/test_the_api.md)
|
||||
for free to test the Chargeprice features. This is already
|
||||
[configured](https://github.com/johan12345/EVMap/blob/master/app/src/debug/res/values/donottranslate.xml)
|
||||
by default for the debug version of the app, so you can leave the `chargeprice_key` field in your
|
||||
new `app/src/main/res/values/apikeys.xml` file blank. Note that the staging API contains only a
|
||||
limited dataset, so it only outputs prices for certain charge point operators and payment plans (see
|
||||
[here](https://docs.google.com/document/d/14zlFr5IEhhR3uGXO5QePKjNUQANVwA-Ba-cZbOCiOBk/edit) for
|
||||
details).
|
||||
|
||||
In case you want to pay for access to the full Chargeprice API, check out their
|
||||
[API docs](https://github.com/chargeprice/chargeprice-api-docs) on GitHub and contact them at
|
||||
[sales@chargeprice.net](mailto:sales@chargeprice.net).
|
||||
</details>
|
||||
|
||||
|
||||
12
fastlane/metadata/android/de-DE/changelogs/102.txt
Normal file
12
fastlane/metadata/android/de-DE/changelogs/102.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Unter https://hosted.weblate.org/engage/evmap/ können Sie jetzt dazu beitragen,
|
||||
EVMap in andere Sprachen zu übersetzen!
|
||||
|
||||
Verbesserungen:
|
||||
- Neues Design für Dialoge
|
||||
- Karte kann bei geöffnetem Filtermenü verschoben werden
|
||||
- Vorbereitungen für Übersetzung der App in andere Sprachen
|
||||
- Android 11 und niedriger: Ortung verbessert, wenn GPS aktiviert aber nicht verfügbar (z.B. in Gebäuden)
|
||||
|
||||
Fehler behoben:
|
||||
- Absturz im Preisvergleich behoben
|
||||
- Verbesserungen für weitere kleine Darstellungsfehler
|
||||
9
fastlane/metadata/android/de-DE/changelogs/104.txt
Normal file
9
fastlane/metadata/android/de-DE/changelogs/104.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Verbesserungen:
|
||||
- Übersetzung auf Norwegisch und Französisch (Danke an die Beitragenden!)
|
||||
- Kontrast der Marker auf der Karte (v.a. gelb) erhöht
|
||||
- Android Automotive OS: Filter "Störung ausschließen" verfügbar
|
||||
|
||||
Fehler behoben:
|
||||
- "Meine Tarife" wurden nicht mehr oben angeordnet und hervorgehoben
|
||||
- Dark Mode: Weißes Aufblitzen bei Wechsel zur Karte teilweise reduziert
|
||||
- Android Auto: Abstürze behoben
|
||||
@@ -1 +1 @@
|
||||
EVMap - Elektroauto-Ladestationen
|
||||
EVMap - Elektroauto laden
|
||||
11
fastlane/metadata/android/en-US/changelogs/102.txt
Normal file
11
fastlane/metadata/android/en-US/changelogs/102.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
At https://hosted.weblate.org/engage/evmap/ you can now help translating EVMap into other languages!
|
||||
|
||||
Improvements:
|
||||
- New design for dialogs
|
||||
- Map can be moved when filter menu is open
|
||||
- Preparations for translating the app into other languages
|
||||
- Android 11 and lower: Improved localization if GPS is enabled but not available (e.g. in buildings)
|
||||
|
||||
Bugfixes:
|
||||
- Fixed crash in price comparison
|
||||
- Improvements for some more minor visual issues
|
||||
9
fastlane/metadata/android/en-US/changelogs/104.txt
Normal file
9
fastlane/metadata/android/en-US/changelogs/104.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Improvements:
|
||||
- Added Norwegian and French translations (thanks to the contributors!)
|
||||
- Improved contrast of markers on map (especially yellow)
|
||||
- Android Automotive OS: Filter "exclude chargers with reported faults" available
|
||||
|
||||
Bugfixes:
|
||||
- "My charging plans" were not sorted and highlighted
|
||||
- Dark Mode: Partly fixed white flashing when switching back to map
|
||||
- Android Auto: fixed crashes
|
||||
@@ -1 +1 @@
|
||||
EVMap - Electric vehicle chargers
|
||||
EVMap - EV chargers
|
||||
18
fastlane/metadata/android/fr/full_description.txt
Normal file
18
fastlane/metadata/android/fr/full_description.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
Grâce à EVMap, vous pouvez trouver les chargeurs de véhicules électriques confortablement à l'aide de votre téléphone Android. Il fournit un accès mobile aux bases de données communautaires de GoingElectric.de et Open Charge Map, qui contiennent des informations sur les points de charge dans le monde entier. Pour de nombreux points de charge en Europe, vous pouvez voir des informations sur le statut en temps réel.
|
||||
|
||||
Caractéristiques :
|
||||
- Conception matérielle ("Material Design")
|
||||
- Affiche toutes les stations de recharge des répertoires GoingElectric.de et Open Charge Map gérés par la communauté.
|
||||
- Informations sur la disponibilité en temps réel (uniquement en Europe)
|
||||
- Comparaison des prix intégrée grâce à Chargeprice.app (uniquement en Europe)
|
||||
- Données cartographiques provenant d'OpenStreetMap (Mapbox)
|
||||
- Recherche de lieux
|
||||
- Options de filtrage avancées, y compris les profils de filtrage enregistrés
|
||||
- Liste de favoris, avec également des informations sur la disponibilité
|
||||
- Pas de publicité, entièrement open source
|
||||
|
||||
EVMap est un projet open source et peut être trouvé sur https://github.com/johan12345/EVMap.
|
||||
|
||||
Cette application n'est pas un produit officiel de GoingElectric.de ou Open Charge Map, elle utilise uniquement leurs API publiques.
|
||||
|
||||
Une liste des permissions nécessaires avec des explications est disponible ici : https://evmap.vonforst.net/en/permissions.html
|
||||
1
fastlane/metadata/android/fr/short_description.txt
Normal file
1
fastlane/metadata/android/fr/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
Trouver des stations de recharge pour véhicules électriques
|
||||
1
fastlane/metadata/android/fr/title.txt
Normal file
1
fastlane/metadata/android/fr/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
EVMap - Charger son VE
|
||||
19
fastlane/metadata/android/nb-NO/full_description.txt
Normal file
19
fastlane/metadata/android/nb-NO/full_description.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
Finn steder å lade fra den gemenskapsdrevne databasen til GoingElectric.de og Open Charge Map med din Android-enhet.
|
||||
Du finner info om ladestasjoner i hele verden og sanntidsinfo for mange av dem som er å finne i Europa.
|
||||
|
||||
- Fri programvare
|
||||
- Materiell design
|
||||
- Sanntidsinfo (kun i Europa)
|
||||
- Integrert sammenligningsinfo ved bruk av Chargeprice.app (kun i Europa)
|
||||
- Kartdata fra OpenStreetMap (Mapbox)
|
||||
- Søk etter steder
|
||||
- Avanserte filtreringsvalg, inkludert lagrede filterprofiler
|
||||
- Favorittliste, som også har tilgjengelighetsinfo
|
||||
- Ingen reklame
|
||||
|
||||
Du finner kildekoden på https://github.com/johan12345/EVMap.
|
||||
|
||||
Dette kartet er ikke et offisielt program fra hverken GoingElectric.de eller Open Charge Map.
|
||||
Kun de offentlige API-ene derfra benyttes.
|
||||
|
||||
Nødvendige tilganger er forklart på https://evmap.vonforst.net/en/permissions.html
|
||||
1
fastlane/metadata/android/nb-NO/short_description.txt
Normal file
1
fastlane/metadata/android/nb-NO/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
Finn ladestasjoner for elektriske kjøretøy
|
||||
1
fastlane/metadata/android/nb-NO/title.txt
Normal file
1
fastlane/metadata/android/nb-NO/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
EVMap — Elbil-ladere
|
||||
Reference in New Issue
Block a user