Compare commits

..

36 Commits
1.6.6 ... 1.6.9

Author SHA1 Message Date
johan12345
9222dec613 Release 1.6.9 2023-09-14 20:26:08 +02:00
johan12345
71c36fbc8f MapFragment: Change default map location behavior
Only jump back to current location on app restart if we were previously looking at the current location (and have location permission enabled). Otherwise stay at the last viewed location.

This seems to be the same as what e.g. the Google Maps app does.

#191
2023-09-14 20:16:41 +02:00
johan12345
830477e664 Automotive FilterScreen: fix possible crash when profile is deleted 2023-09-13 22:44:23 +02:00
johan12345
3ce91a9c50 ChargerDetailScreen: fix nullability issue 2023-09-10 12:39:19 +02:00
johan12345
a3b2b94b25 ACRA: switch -normal build variants to use HTTP sending as well 2023-09-06 21:51:50 +02:00
johan12345
a7770e1c1b fix Github Actions build 2023-09-03 18:34:24 +02:00
johan12345
fcd51307cb Release 1.6.8 2023-09-03 18:11:57 +02:00
johan12345
ba4a9c29b2 ACRA: keep key-value format for email reports 2023-09-03 18:11:24 +02:00
johan12345
3463177ad2 EnBW: avoid endless loop introduced by d636cde7 2023-09-02 23:25:42 +02:00
johan12345
09deaf5080 AA/AAOS: add general info & amenities
(if room available)
2023-09-02 22:27:45 +02:00
johan12345
23f429bbea disable CarAppTest due to Robolectric incompatibility 2023-09-02 22:16:35 +02:00
johan12345
1184d3b6cc AA/AAOS FilterScreen: add delete button to rows 2023-09-02 22:09:07 +02:00
johan12345
c95a60807b Upgrade SDK to 34 & dependencies 2023-09-02 22:01:47 +02:00
johan12345
4b8cf82843 update Android Gradle plugin 2023-09-02 21:20:32 +02:00
johan12345
f33b9e8117 Merge branch 'rework-acra' 2023-09-02 17:39:46 +02:00
johan12345
cbc3040807 Rework units configuration
units (imperial or metric) can be configured globally, this applies to distances within the app, vehicle data, and the map scale bar

fixes #293
2023-09-02 17:39:37 +02:00
johan12345
92619ea95e ACRA: switch from email to HTTP sending for automotive flavor 2023-09-02 16:47:56 +02:00
johan12345
a7007284ff ACRA: introduce AAOS-friendly crash reporting screen 2023-09-02 16:38:02 +02:00
johan12345
7fce566052 add referral link to donate page 2023-08-27 19:53:32 +02:00
Johan von Forstner
0c44b4b074 Update FUNDING.yml 2023-08-27 19:52:48 +02:00
johan12345
a652d96f74 fix CarAppTest 2023-08-27 19:12:55 +02:00
johan12345
8a9b3ad948 Android Auto/Automotive: Make users explicitly accept the privacy policy 2023-08-27 19:05:04 +02:00
Johan von Forstner
c48f33e265 Release 1.6.7 2023-08-21 19:26:30 +02:00
johan12345
8ba4897026 Android Automotive: update donate link 2023-08-21 19:22:45 +02:00
Johan von Forstner
42916d71ca Onboarding: Do not select GoingElectric by default
instead make the user actively choose the data source
#291
2023-08-03 16:47:20 +02:00
Johan von Forstner
5ca7524e8b OpenChargeMap: Map "Tesla Model S/X" plug to Chargepoint.SUPERCHARGER
#6
2023-08-03 16:34:10 +02:00
Johan von Forstner
c37f72a26b reduce vertical padding around dialogs
#292
2023-08-02 20:46:16 +02:00
Johan von Forstner
6f0113c50d adjustments to data source selection screen for smaller screens 2023-08-02 19:58:05 +02:00
Johan von Forstner
f99ea7ca9e Fix formatting of Portuguese string 2023-08-02 16:21:02 +02:00
Johan von Forstner
788db0c10f fix popup menu theme in dark mode
see #287
2023-08-02 16:09:48 +02:00
Hosted Weblate
db2213a50f Translated using Weblate (Portuguese)
Currently translated at 100.0% (349 of 349 strings)

Co-authored-by: Celso Azevedo <mail@celsoazevedo.com>
Translate-URL: https://hosted.weblate.org/projects/evmap/android/pt/
Translation: EVMap/Android
2023-08-02 16:08:45 +02:00
Johan von Forstner
ace4126035 more uppercase consistency 2023-08-02 15:45:47 +02:00
Johan von Forstner
d5d6e4f314 GoingElectric: nullability fix 2023-08-01 20:23:54 +02:00
Johan von Forstner
55999d15e6 upper/lower case consistency
as mentioned in comment in #287
2023-07-30 20:01:11 +02:00
Johan von Forstner
b61ca609d3 GoingElectric: fix "Tesla Supercharger CCS" filter
"Tesla Supercharger CCS" is a bit peculiar - it is available as a filter, but the API just returns "CCS" in the charging station details. So we cannot use it for filtering as it won't work in the local database. So we join them into a single filter option. If you want to find Tesla Superchargers with CCS, you can still do that using the network filter.
2023-07-27 10:16:22 +02:00
johan12345
b0afad2144 fix nullability issue in TeslaAvailabilityDetector 2023-07-19 20:59:47 +02:00
65 changed files with 672 additions and 240 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,2 +1,2 @@
github: johan12345
custom: 'https://paypal.me/johan98'
custom: ['https://paypal.me/johan98', 'http://ts.la/johan94494']

View File

@@ -5,4 +5,5 @@
<string name="chargeprice_key" translatable="false">ci</string>
<string name="openchargemap_key" translatable="false">ci</string>
<string name="fronyx_key" translatable="false">ci</string>
<string name="acra_credentials" translatable="false">ci:ci</string>
</resources>

View File

@@ -32,6 +32,7 @@ jobs:
MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }}
GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }}
FRONYX_API_KEY: ${{ secrets.FRONYX_API_KEY }}
ACRA_CRASHREPORT_CREDENTIALS: ${{ secrets.ACRA_CRASHREPORT_CREDENTIALS }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}
KEYSTORE_ALIAS_PASSWORD: ${{ secrets.KEYSTORE_ALIAS_PASSWORD }}

View File

@@ -13,16 +13,14 @@ apply plugin: 'pt.jcosta.resourceplaceholders'
def supportedLocales = "en,de,fr,nb-rNO,nl,pt,ro"
android {
compileSdkVersion 33
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "net.vonforst.evmap"
compileSdk 34
minSdkVersion 21
targetSdkVersion 33
targetSdkVersion 34
// NOTE: always increase versionCode by 2 since automotive flavor uses versionCode + 1
versionCode 192
versionName "1.6.6"
versionCode 200
versionName "1.6.9"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resConfigs supportedLocales.split(',')
@@ -147,6 +145,13 @@ android {
if (fronyxKey != null) {
variant.resValue "string", "fronyx_key", fronyxKey
}
def acraKey = env.ACRA_CRASHREPORT_CREDENTIALS ?: project.findProperty("ACRA_CRASHREPORT_CREDENTIALS")
if (acraKey == null && project.hasProperty("ACRA_CRASHREPORT_CREDENTIALS_ENCRYPTED")) {
acraKey = decode(project.findProperty("ACRA_CRASHREPORT_CREDENTIALS_ENCRYPTED"), "FmK.d,-f*p+rD+WK!eds")
}
if (acraKey != null) {
variant.resValue "string", "acra_credentials", acraKey
}
}
packagingOptions {
@@ -168,13 +173,13 @@ dependencies {
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation "androidx.activity:activity-ktx:1.7.2"
implementation "androidx.fragment:fragment-ktx:1.5.7"
implementation "androidx.fragment:fragment-ktx:1.6.1"
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation 'androidx.browser:browser:1.5.0'
implementation 'androidx.recyclerview:recyclerview:1.3.1'
implementation 'androidx.browser:browser:1.6.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
implementation "androidx.work:work-runtime-ktx:2.8.1"
@@ -197,7 +202,7 @@ dependencies {
implementation 'com.github.romandanylyk:PageIndicatorView:b1bad589b5'
// Android Auto
def carAppVersion = '1.3.0-rc01'
def carAppVersion = '1.4.0-beta01'
implementation "androidx.car.app:app:$carAppVersion"
normalImplementation "androidx.car.app:app-projected:$carAppVersion"
automotiveImplementation "androidx.car.app:app-automotive:$carAppVersion"
@@ -219,8 +224,8 @@ dependencies {
fossImplementation 'com.github.ev-map:mapbox-events-android:a21c324501'
// Google Places
googleImplementation 'com.google.android.libraries.places:places:3.1.0'
googleImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.4'
googleImplementation 'com.google.android.libraries.places:places:3.2.0'
googleImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.1'
// Mapbox Geocoding
implementation 'com.mapbox.mapboxsdk:mapbox-sdk-services:5.5.0'
@@ -235,20 +240,20 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// room library
def room_version = "2.5.1"
def room_version = "2.6.0-beta01"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
implementation 'com.github.anboralabs:spatia-room:0.2.7'
// billing library
def billing_version = "6.0.0"
def billing_version = "6.0.1"
googleImplementation "com.android.billingclient:billing:$billing_version"
googleImplementation "com.android.billingclient:billing-ktx:$billing_version"
// ACRA (crash reporting)
def acraVersion = "5.8.4"
implementation("ch.acra:acra-mail:$acraVersion")
def acraVersion = "5.11.1"
implementation("ch.acra:acra-http:$acraVersion")
implementation("ch.acra:acra-dialog:$acraVersion")
implementation("ch.acra:acra-limiter:$acraVersion")
@@ -262,13 +267,12 @@ dependencies {
testImplementation "com.squareup.okhttp3:mockwebserver:4.11.0"
//noinspection GradleDependency
testImplementation 'org.json:json:20080701'
testImplementation 'org.robolectric:robolectric:4.9.2'
testImplementation 'org.robolectric:robolectric:4.10.3'
testImplementation 'androidx.test:core:1.5.0'
testImplementation 'androidx.arch.core:core-testing:2.2.0'
// testing for car app
testGoogleImplementation "androidx.car.app:app-testing:$carAppVersion"
testGoogleImplementation 'org.robolectric:robolectric:4.9.2'
testGoogleImplementation 'androidx.test:core:1.5.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'

View File

@@ -42,5 +42,9 @@ class DonateFragment : Fragment() {
binding.btnDonate.setOnClickListener {
(activity as? MapsActivity)?.openUrl(getString(R.string.paypal_link))
}
binding.referrals.referralTesla.setOnClickListener {
(requireActivity() as MapsActivity).openUrl(getString(R.string.tesla_referral_link))
}
}
}

View File

@@ -1,18 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar_container"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
android:fitsSystemWindows="true">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
@@ -21,31 +19,55 @@
</com.google.android.material.appbar.AppBarLayout>
<Button
android:id="@+id/btnDonate"
style="@style/Widget.Material3.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/donate_paypal"
app:icon="@drawable/ic_paypal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView20" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView20"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/donations_info"
app:layout_constraintBottom_toTopOf="@+id/btnDonate"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar_container"
app:layout_constraintVertical_chainStyle="packed" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<Button
android:id="@+id/btnDonate"
style="@style/Widget.Material3.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginStart="16dp"
android:text="@string/donate_paypal"
app:icon="@drawable/ic_paypal"
app:layout_constraintBottom_toTopOf="@id/referrals"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView20" />
<TextView
android:id="@+id/textView20"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/donations_info"
app:layout_constraintBottom_toTopOf="@+id/btnDonate"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<include
android:id="@+id/referrals"
layout="@layout/fragment_donate_referral"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="36dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnDonate" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</LinearLayout>

View File

@@ -11,6 +11,7 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.color.MaterialColors
import com.google.android.material.snackbar.Snackbar
@@ -18,12 +19,17 @@ import com.google.android.material.transition.MaterialSharedAxis
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.DonationAdapter
import net.vonforst.evmap.adapter.SingleViewAdapter
import net.vonforst.evmap.databinding.FragmentDonateBinding
import net.vonforst.evmap.databinding.FragmentDonateHeaderBinding
import net.vonforst.evmap.databinding.FragmentDonateReferralBinding
import net.vonforst.evmap.viewmodel.DonateViewModel
class DonateFragment : Fragment() {
private lateinit var binding: FragmentDonateBinding
private val vm: DonateViewModel by viewModels()
private lateinit var header: FragmentDonateHeaderBinding
private lateinit var referrals: FragmentDonateReferralBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -40,6 +46,9 @@ class DonateFragment : Fragment() {
binding.lifecycleOwner = this
binding.vm = vm
header = FragmentDonateHeaderBinding.inflate(inflater, container, false)
referrals = FragmentDonateReferralBinding.inflate(inflater, container, false)
return binding.root
}
@@ -51,25 +60,35 @@ class DonateFragment : Fragment() {
(requireActivity() as MapsActivity).appBarConfiguration
)
binding.productsList.apply {
adapter = DonationAdapter().apply {
onClickListener = {
vm.startPurchase(it, requireActivity())
}
val donationAdapter = DonationAdapter().apply {
onClickListener = {
vm.startPurchase(it, requireActivity())
}
}
binding.productsList.apply {
val joinedAdapter = ConcatAdapter(
SingleViewAdapter(header.root),
donationAdapter,
SingleViewAdapter(referrals.root)
)
adapter = joinedAdapter
layoutManager = LinearLayoutManager(context)
}
vm.products.observe(viewLifecycleOwner) {
print(it)
donationAdapter.submitList(it.data)
}
vm.purchaseSuccessful.observe(viewLifecycleOwner, Observer {
vm.purchaseSuccessful.observe(viewLifecycleOwner) {
Snackbar.make(view, R.string.donation_successful, Snackbar.LENGTH_LONG).show()
})
vm.purchaseFailed.observe(viewLifecycleOwner, Observer {
}
vm.purchaseFailed.observe(viewLifecycleOwner) {
Snackbar.make(view, R.string.donation_failed, Snackbar.LENGTH_LONG).show()
})
}
referrals.referralTesla.setOnClickListener {
(requireActivity() as MapsActivity).openUrl(getString(R.string.tesla_referral_link))
}
// Workaround for AndroidX bug: https://github.com/material-components/material-components-android/issues/1984
view.setBackgroundColor(MaterialColors.getColor(view, android.R.attr.windowBackground))

View File

@@ -35,29 +35,16 @@
</com.google.android.material.appbar.AppBarLayout>
<TextView
android:id="@+id/textView20"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/donations_info"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar_container" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/products_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
app:data="@{vm.products.data}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView20"
tools:listitem="@layout/item_donation" />
app:layout_constraintTop_toBottomOf="@id/toolbar_container"
tools:itemCount="1"
tools:listitem="@layout/fragment_donate_preview" />
<ProgressBar
android:id="@+id/progressBar3"

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@+id/textView20"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:text="@string/donations_info"
xmlns:android="http://schemas.android.com/apk/res/android" />

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/fragment_donate_header" />
<include layout="@layout/item_donation" />
<include layout="@layout/item_donation" />
<include layout="@layout/item_donation" />
<include layout="@layout/fragment_donate_referral" />
</LinearLayout>

View File

@@ -327,7 +327,8 @@
android:name=".auto.CarAppService"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:exported="true">
android:exported="true"
android:foregroundServiceType="location">
<intent-filter>
<action
android:name="androidx.car.app.CarAppService"

View File

@@ -1,5 +1,6 @@
package net.vonforst.evmap
import android.app.Activity
import android.app.Application
import android.os.Build
import androidx.work.*
@@ -8,10 +9,11 @@ import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.ui.updateAppLocale
import net.vonforst.evmap.ui.updateNightMode
import org.acra.config.dialog
import org.acra.config.httpSender
import org.acra.config.limiter
import org.acra.config.mailSender
import org.acra.data.StringFormat
import org.acra.ktx.initAcra
import org.acra.sender.HttpSender
import java.time.Duration
class EvMapApplication : Application(), Configuration.Provider {
@@ -33,10 +35,15 @@ class EvMapApplication : Application(), Configuration.Provider {
if (!BuildConfig.DEBUG) {
initAcra {
buildConfigClass = BuildConfig::class.java
reportFormat = StringFormat.KEY_VALUE_LIST
mailSender {
mailTo = "evmap+crashreport@vonforst.net"
// Vehicles often don't have an email app, so use HTTP to send instead
reportFormat = StringFormat.JSON
httpSender {
uri = getString(R.string.acra_backend_url)
val creds = getString(R.string.acra_credentials).split(":")
basicAuthLogin = creds[0]
basicAuthPassword = creds[1]
httpMethod = HttpSender.Method.POST
}
dialog {
@@ -45,6 +52,10 @@ class EvMapApplication : Application(), Configuration.Provider {
commentPrompt = getString(R.string.crash_report_comment_prompt)
resIcon = R.drawable.ic_launcher_foreground
resTheme = R.style.AppTheme
if (BuildConfig.FLAVOR_automotive == "automotive") {
reportDialogClass =
Class.forName("androidx.car.app.activity.CarAppActivity") as Class<out Activity>?
}
}
limiter {

View File

@@ -5,10 +5,13 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Typeface
import android.icu.util.LocaleData
import android.icu.util.ULocale
import android.os.Build
import android.os.Bundle
import android.text.*
import android.text.style.StyleSpan
import net.vonforst.evmap.storage.PreferenceDataSource
import java.util.*
fun Bundle.optDouble(name: String): Double? {
@@ -88,9 +91,25 @@ fun Context.isDarkMode() =
const val kmPerMile = 1.609344
const val meterPerFt = 0.3048
const val ftPerMile = 5280
const val ydPerMile = 1760
fun shouldUseImperialUnits(ctx: Context): Boolean {
val prefs = PreferenceDataSource(ctx)
return when (prefs.units) {
"metric" -> false
"imperial" -> true
else -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
when (LocaleData.getMeasurementSystem(ULocale.getDefault())) {
LocaleData.MeasurementSystem.US, LocaleData.MeasurementSystem.UK -> true
LocaleData.MeasurementSystem.SI -> false
else -> false
}
} else {
return Locale.getDefault().country in listOf("US", "GB", "MM", "LR")
}
}
fun shouldUseImperialUnits(): Boolean {
return Locale.getDefault().country in listOf("US", "GB", "MM", "LR")
}
fun PackageManager.getPackageInfoCompat(packageName: String, flags: Int = 0): PackageInfo =

View File

@@ -105,20 +105,19 @@ class EnBwAvailabilityDetector(client: OkHttpClient, baseUrl: String? = null) :
var markers =
api.getMarkers(lng - coordRange, lng + coordRange, lat - coordRange, lat + coordRange)
while (markers.any { it.grouped }) {
markers = markers.flatMap {
if (it.grouped) {
api.getMarkers(
it.viewPort.lowerLeftLon,
it.viewPort.upperRightLon,
it.viewPort.lowerLeftLat,
it.viewPort.upperRightLat
)
} else {
listOf(it)
}
markers = markers.flatMap {
if (it.grouped) {
api.getMarkers(
it.viewPort.lowerLeftLon,
it.viewPort.upperRightLon,
it.viewPort.lowerLeftLat,
it.viewPort.upperRightLat
)
} else {
listOf(it)
}
}
if (markers.any { it.grouped }) throw AvailabilityDetectorException("markers still grouped")
val nearest = markers.minByOrNull { marker ->
distanceBetween(marker.lat, marker.lon, lat, lng)

View File

@@ -235,7 +235,7 @@ interface TeslaGraphQlApi {
data class GetNearbyChargingSitesResponse(val data: GetNearbyChargingSitesResponseData)
@JsonClass(generateAdapter = true)
data class GetNearbyChargingSitesResponseData(val charging: GetNearbyChargingSitesResponseDataCharging)
data class GetNearbyChargingSitesResponseData(val charging: GetNearbyChargingSitesResponseDataCharging?)
@JsonClass(generateAdapter = true)
data class GetNearbyChargingSitesResponseDataCharging(val nearbySites: GetNearbyChargingSitesResponseDataChargingNearbySites)
@@ -518,7 +518,8 @@ class TeslaAvailabilityDetector(
val results = api.getNearbyChargingSites(
req,
req.operationName
).data.charging.nearbySites.sitesAndDistances
).data.charging?.nearbySites?.sitesAndDistances
?: throw AvailabilityDetectorException("no candidates found.")
val result =
results.minByOrNull { it.haversineDistanceMiles.value }
?: throw AvailabilityDetectorException("no candidates found.")

View File

@@ -153,6 +153,10 @@ class GoingElectricApiWrapper(
// no connectors chosen
return Resource.success(ChargepointList.empty())
}
if (connectorsVal != null && connectorsVal.values.contains("CCS")) {
// see note about Tesla Supercharger CCS filter in getFilters below
connectorsVal.values.add("Tesla Supercharger CCS")
}
val connectors = formatMultipleChoice(connectorsVal)
val chargeCardsVal = filters?.getMultipleChoiceValue("chargecards")
@@ -247,6 +251,10 @@ class GoingElectricApiWrapper(
// no connectors chosen
return Resource.success(ChargepointList.empty())
}
if (connectorsVal != null && connectorsVal.values.contains("CCS")) {
// see note about Tesla Supercharger CCS filter in getFilters below
connectorsVal.values.add("Tesla Supercharger CCS")
}
val connectors = formatMultipleChoice(connectorsVal)
val chargeCardsVal = filters?.getMultipleChoiceValue("chargecards")
@@ -434,9 +442,18 @@ class GoingElectricApiWrapper(
val networks = refData.networks
val chargeCards = refData.chargecards
val plugMap = plugs.associateWith { plug ->
nameForPlugType(sp, GEChargepoint.convertTypeFromGE(plug))
}
/*
"Tesla Supercharger CCS" is a bit peculiar - it is available as a filter, but the API
just returns "CCS" in the charging station details. So we cannot use it for filtering as
it won't work in the local database. So we join them into a single filter option.
If you want to find Tesla Superchargers with CCS, you can still do that using the network
filter.
*/
val plugMap = plugs
.filter { it != "Tesla Supercharger CCS" }
.associateWith { plug ->
nameForPlugType(sp, GEChargepoint.convertTypeFromGE(plug))
}
val networkMap = networks.associateWith { it }
val chargecardMap = chargeCards.associate { it.id.toString() to it.name }
val categoryMap = mapOf(

View File

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

View File

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

View File

@@ -34,6 +34,7 @@ import net.vonforst.evmap.location.LocationEngine
import net.vonforst.evmap.location.Priority
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.utils.checkFineLocationPermission
import org.acra.interaction.DialogInteraction
interface LocationAwareScreen {
@@ -122,11 +123,13 @@ class EVMapSession(val cas: CarAppService) : Session(), DefaultLifecycleObserver
}
override fun onCreateScreen(intent: Intent): Screen {
handleActionsIntent(intent)
val mapScreen = MapScreen(carContext, this)
val screens = mutableListOf<Screen>(mapScreen)
handleActionsIntent(intent)?.let {
screens.add(it)
}
if (!prefs.dataSourceSet) {
screens.add(
ChooseDataSourceScreen(
@@ -149,6 +152,14 @@ class EVMapSession(val cas: CarAppService) : Session(), DefaultLifecycleObserver
)
)
}
if (!prefs.privacyAccepted) {
screens.add(
AcceptPrivacyScreen(carContext)
)
}
handleACRAIntent(intent)?.let {
screens.add(it)
}
if (screens.size > 1) {
val screenManager = carContext.getCarService(ScreenManager::class.java)
@@ -160,7 +171,13 @@ class EVMapSession(val cas: CarAppService) : Session(), DefaultLifecycleObserver
return screens.last()
}
private fun handleActionsIntent(intent: Intent): Boolean {
private fun handleACRAIntent(intent: Intent): Screen? {
return if (intent.hasExtra(DialogInteraction.EXTRA_REPORT_CONFIG)) {
CrashReportScreen(carContext, intent)
} else null
}
private fun handleActionsIntent(intent: Intent): Screen? {
intent.data?.let {
if (it.host == "find_charger") {
val lat = it.getQueryParameter("latitude")?.toDouble()
@@ -169,15 +186,14 @@ class EVMapSession(val cas: CarAppService) : Session(), DefaultLifecycleObserver
if (lat != null && lon != null) {
prefs.placeSearchResultAndroidAuto = LatLng(lat, lon)
prefs.placeSearchResultAndroidAutoName = name ?: "%.4f,%.4f".format(lat, lon)
return true
return null
} else if (name != null) {
val screenManager = carContext.getCarService(ScreenManager::class.java)
screenManager.push(PlaceSearchScreen(carContext, this, name))
return true
val screen = PlaceSearchScreen(carContext, this, name)
return screen
}
}
}
return false
return null
}
override fun onNewIntent(intent: Intent) {

View File

@@ -290,6 +290,18 @@ class ChargerDetailScreen(ctx: CarContext, val chargerSparse: ChargeLocation) :
}.build())
}
}
if (rows.count() < maxRows && charger.generalInformation != null) {
rows.add(Row.Builder().apply {
setTitle(carContext.getString(R.string.general_info))
addText(charger.generalInformation)
}.build())
}
if (rows.count() < maxRows && charger.amenities != null) {
rows.add(Row.Builder().apply {
setTitle(carContext.getString(R.string.amenities))
addText(charger.amenities)
}.build())
}
return rows
}
@@ -371,8 +383,10 @@ class ChargerDetailScreen(ctx: CarContext, val chargerSparse: ChargeLocation) :
} else {
append(nameForPlugType(carContext.stringProvider(), cp.type))
}
append(" ")
append(cp.formatPower())
cp.formatPower()?.let {
append(" ")
append(it)
}
}
availability?.status?.get(cp)?.let { status ->
chargepointsText.append(

View File

@@ -0,0 +1,45 @@
package net.vonforst.evmap.auto
import android.content.Intent
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.Action
import androidx.car.app.model.CarColor
import androidx.car.app.model.CarIcon
import androidx.car.app.model.MessageTemplate
import androidx.car.app.model.Template
import net.vonforst.evmap.R
import org.acra.dialog.CrashReportDialogHelper
/**
* ACRA-compatible crash reporting screen for the Car App Library
*
* only used on Android Automotive OS
*/
class CrashReportScreen(ctx: CarContext, intent: Intent) : Screen(ctx) {
val helper = CrashReportDialogHelper(ctx, intent)
override fun onGetTemplate(): Template {
return MessageTemplate.Builder(carContext.getString(R.string.crash_report_text)).apply {
setHeaderAction(Action.APP_ICON)
setTitle(carContext.getString(R.string.app_name))
addAction(
Action.Builder()
.setTitle(carContext.getString(R.string.ok))
.setFlags(Action.FLAG_PRIMARY)
.setBackgroundColor(CarColor.PRIMARY)
.setOnClickListener {
helper.sendCrash(null, null)
screenManager.pop()
}.build()
)
addAction(
Action.Builder()
.setTitle(carContext.getString(R.string.cancel))
.setOnClickListener {
helper.cancelReports()
screenManager.pop()
}.build()
)
}.build()
}
}

View File

@@ -41,7 +41,9 @@ class FilterScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
if (filterStatus in listOf(FILTERS_DISABLED, FILTERS_FAVORITES, FILTERS_CUSTOM)) {
page = 0
} else {
page = paginateProfiles(it).indexOfFirst { it.any { it.id == filterStatus } }
val index =
paginateProfiles(it).indexOfFirst { it.any { it.id == filterStatus } }
page = index.takeUnless { it == -1 } ?: 0
}
invalidate()
}
@@ -204,6 +206,37 @@ class FilterScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
Row.IMAGE_TYPE_ICON
)
setOnClickListener { onItemClick(it.id) }
if (carContext.carAppApiLevel >= 6) {
// Delete action
addAction(Action.Builder().apply {
setIcon(
CarIcon.Builder(
IconCompat.createWithResource(
carContext,
R.drawable.ic_delete
)
).build()
)
setOnClickListener {
lifecycleScope.launch {
db.filterProfileDao().delete(it)
if (prefs.filterStatus == it.id) {
prefs.filterStatus = FILTERS_DISABLED
}
CarToast.makeText(
carContext,
carContext.getString(
R.string.deleted_filterprofile,
it.name
),
CarToast.LENGTH_SHORT
).show()
invalidate()
}
}
}.build())
}
}.build())
}
if (page < paginatedProfiles.size - 1) {
@@ -293,7 +326,8 @@ class EditFiltersScreen(ctx: CarContext) : Screen(ctx) {
setActionStrip(ActionStrip.Builder().apply {
val currentProfile = vm.filterProfile.value
if (currentProfile != null) {
if (currentProfile != null && carContext.carAppApiLevel < 6) {
// Delete action (when row actions are not available)
addAction(Action.Builder().apply {
setIcon(
CarIcon.Builder(

View File

@@ -348,7 +348,8 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
DistanceSpan.create(
roundValueToDistance(
distanceMeters,
energyLevel?.distanceDisplayUnit?.value
energyLevel?.distanceDisplayUnit?.value,
carContext
)
),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE

View File

@@ -23,6 +23,7 @@ class PermissionScreen(
Action.Builder()
.setTitle(carContext.getString(R.string.grant_on_phone))
.setBackgroundColor(CarColor.PRIMARY)
.setFlags(Action.FLAG_PRIMARY)
.setOnClickListener(ParkedOnlyOnClickListener.create {
requestPermissions()
})

View File

@@ -105,7 +105,8 @@ class PlaceSearchScreen(
DistanceSpan.create(
roundValueToDistance(
it,
energyLevel?.distanceDisplayUnit?.value
energyLevel?.distanceDisplayUnit?.value,
carContext
)
),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE

View File

@@ -5,6 +5,7 @@ import android.content.Intent
import android.content.pm.PackageManager.NameNotFoundException
import android.hardware.Sensor
import android.hardware.SensorManager
import android.text.Html
import androidx.annotation.StringRes
import androidx.car.app.CarContext
import androidx.car.app.CarToast
@@ -13,6 +14,7 @@ import androidx.car.app.annotations.ExperimentalCarApi
import androidx.car.app.constraints.ConstraintManager
import androidx.car.app.model.*
import androidx.core.graphics.drawable.IconCompat
import androidx.core.text.HtmlCompat
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import net.vonforst.evmap.*
@@ -646,7 +648,7 @@ class AboutScreen(ctx: CarContext) : Screen(ctx) {
.setOnClickListener(ParkedOnlyOnClickListener.create {
if (BuildConfig.FLAVOR_automotive == "automotive") {
// we can't open the donation page on the phone in this case
openUrl(carContext, carContext.getString(R.string.paypal_link))
openUrl(carContext, carContext.getString(R.string.donate_link))
} else {
val intent = Intent(carContext, MapsActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -703,6 +705,34 @@ class AboutScreen(ctx: CarContext) : Screen(ctx) {
}
}
class AcceptPrivacyScreen(ctx: CarContext) : Screen(ctx) {
val prefs = PreferenceDataSource(ctx)
override fun onGetTemplate(): Template {
val textWithoutLink = HtmlCompat.fromHtml(
carContext.getString(R.string.accept_privacy),
HtmlCompat.FROM_HTML_MODE_LEGACY
).toString()
return MessageTemplate.Builder(textWithoutLink).apply {
setTitle(carContext.getString(R.string.privacy))
addAction(Action.Builder()
.setTitle(carContext.getString(R.string.ok))
.setFlags(Action.FLAG_PRIMARY)
.setBackgroundColor(CarColor.PRIMARY)
.setOnClickListener {
prefs.privacyAccepted = true
screenManager.pop()
}.build()
)
addAction(Action.Builder()
.setTitle(carContext.getString(R.string.privacy))
.setOnClickListener(ParkedOnlyOnClickListener.create {
openUrl(carContext, carContext.getString(R.string.privacy_link))
}).build()
)
}.build()
}
}
class DeveloperOptionsScreen(ctx: CarContext) : Screen(ctx) {
val prefs = PreferenceDataSource(ctx)

View File

@@ -19,7 +19,11 @@ import androidx.core.graphics.drawable.IconCompat
import net.vonforst.evmap.BuildConfig
import net.vonforst.evmap.R
import net.vonforst.evmap.api.availability.ChargepointStatus
import net.vonforst.evmap.ftPerMile
import net.vonforst.evmap.getPackageInfoCompat
import net.vonforst.evmap.kmPerMile
import net.vonforst.evmap.shouldUseImperialUnits
import net.vonforst.evmap.ydPerMile
import java.util.*
import kotlin.math.roundToInt
@@ -69,33 +73,26 @@ val emptyCarIcon: CarIcon by lazy {
).asCarIcon()
}
private const val kmPerMile = 1.609344
private const val ftPerMile = 5280
private const val ydPerMile = 1760
fun getDefaultDistanceUnit(): Int {
return if (usesImperialUnits(Locale.getDefault())) {
fun getDefaultDistanceUnit(ctx: Context): Int {
return if (shouldUseImperialUnits(ctx)) {
CarUnit.MILE
} else {
CarUnit.KILOMETER
}
}
fun usesImperialUnits(locale: Locale): Boolean {
return locale.country in listOf("US", "GB", "MM", "LR")
|| locale.country == "" && locale.language == "en"
}
fun getDefaultSpeedUnit(): Int {
return when (Locale.getDefault().country) {
"US", "GB", "MM", "LR" -> CarUnit.MILES_PER_HOUR
else -> CarUnit.KILOMETERS_PER_HOUR
fun getDefaultSpeedUnit(ctx: Context): Int {
return if (shouldUseImperialUnits(ctx)) {
CarUnit.MILES_PER_HOUR
} else {
CarUnit.KILOMETERS_PER_HOUR
}
}
fun formatCarUnitDistance(value: Float?, unit: Int?): String {
fun formatCarUnitDistance(value: Float?, unit: Int?, ctx: Context): String {
if (value == null) return ""
return when (unit ?: getDefaultDistanceUnit()) {
return when (unit ?: getDefaultDistanceUnit(ctx)) {
// distance units: base unit is meters
CarUnit.METER -> "%.0f m".format(value)
CarUnit.KILOMETER -> "%.1f km".format(value / 1000)
@@ -105,9 +102,9 @@ fun formatCarUnitDistance(value: Float?, unit: Int?): String {
}
}
fun formatCarUnitSpeed(value: Float?, unit: Int?): String {
fun formatCarUnitSpeed(value: Float?, unit: Int?, ctx: Context): String {
if (value == null) return ""
return when (unit ?: getDefaultSpeedUnit()) {
return when (unit ?: getDefaultSpeedUnit(ctx)) {
// speed units: base unit is meters per second
CarUnit.METERS_PER_SEC -> "%.0f m/s".format(value)
CarUnit.KILOMETERS_PER_HOUR -> "%.0f km/h".format(value * 3.6)
@@ -116,9 +113,9 @@ fun formatCarUnitSpeed(value: Float?, unit: Int?): String {
}
}
fun roundValueToDistance(value: Double, unit: Int? = null): Distance {
fun roundValueToDistance(value: Double, unit: Int? = null, ctx: Context): Distance {
// value is in meters
when (unit ?: getDefaultDistanceUnit()) {
when (unit ?: getDefaultDistanceUnit(ctx)) {
CarUnit.MILE -> {
// imperial system
val miles = value / 1000 / kmPerMile

View File

@@ -139,7 +139,8 @@ class VehicleDataScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx
setText(
formatCarUnitDistance(
energyLevel.rangeRemainingMeters.value,
energyLevel.distanceDisplayUnit.value
energyLevel.distanceDisplayUnit.value,
carContext
)
)
setImage(
@@ -173,7 +174,8 @@ class VehicleDataScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx
setText(
formatCarUnitSpeed(
rawSpeed,
speed.speedDisplayUnit.value
speed.speedDisplayUnit.value,
carContext
)
)
setImage(
@@ -183,7 +185,8 @@ class VehicleDataScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx
setText(
formatCarUnitSpeed(
speed.displaySpeedMetersPerSecond.value,
speed.speedDisplayUnit.value
speed.speedDisplayUnit.value,
carContext
)
)
setImage(

View File

@@ -6,7 +6,6 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.graphics.Color
import android.location.Geocoder
import android.os.Bundle
import android.text.method.KeyListener
import android.view.*
@@ -78,6 +77,7 @@ 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.shouldUseImperialUnits
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.ui.*
import net.vonforst.evmap.utils.boundingBox
@@ -194,6 +194,9 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
searchResultIcon = null
}
binding.detailAppBar.toolbar.popupTheme =
com.google.android.material.R.style.ThemeOverlay_AppCompat_DayNight
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _, insets ->
@@ -908,23 +911,19 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
}
binding.scaleView.apply {
when (prefs.mapScale) {
"both" -> {
visibility = View.VISIBLE
if (prefs.showMapScale) {
visibility = View.VISIBLE
if (prefs.mapScaleMetersAndMiles) {
metersAndMiles()
} else {
if (shouldUseImperialUnits(requireContext())) {
milesOnly()
} else {
metersOnly()
}
}
"meters" -> {
visibility = View.VISIBLE
metersOnly()
}
"miles" -> {
visibility = View.VISIBLE
milesOnly()
}
"off" -> visibility = View.GONE
} else {
visibility = View.GONE
}
}
vm.mapPosition.observe(viewLifecycleOwner) {
@@ -1044,7 +1043,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
binding.search.requestFocus()
binding.search.setSelection(locationName.length)
}
if (context.checkAnyLocationPermission()) {
if (context.checkAnyLocationPermission() && prefs.currentMapMyLocationEnabled) {
enableLocation(!positionSet, false)
positionSet = true
}
@@ -1401,6 +1400,9 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
prefs.currentMapLocation = it.bounds.center
prefs.currentMapZoom = it.zoom
}
vm.myLocationEnabled.value?.let {
prefs.currentMapMyLocationEnabled = it
}
}
override fun onDestroy() {

View File

@@ -243,9 +243,11 @@ class DataSourceSelectFragment : OnboardingPageFragment() {
}
}
}
when (prefs.dataSource) {
"goingelectric" -> binding.rgDataSource.rbGoingElectric.isChecked = true
"openchargemap" -> binding.rgDataSource.rbOpenChargeMap.isChecked = true
if (prefs.dataSourceSet) {
when (prefs.dataSource) {
"goingelectric" -> binding.rgDataSource.rbGoingElectric.isChecked = true
"openchargemap" -> binding.rgDataSource.rbOpenChargeMap.isChecked = true
}
}
binding.btnGetStarted.setOnClickListener {

View File

@@ -28,7 +28,7 @@ class AndroidAutoSettingsFragment : BaseSettingsFragment() {
setPreferencesFromResource(R.xml.settings_android_auto, rootKey)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
when (key) {
"chargeprice_battery_range_android_auto_min", "chargeprice_battery_range_android_auto_max" -> {
updateRangePreferenceSummary()

View File

@@ -101,11 +101,12 @@ class ChargepriceSettingsFragment : BaseSettingsFragment() {
setPreferencesFromResource(R.xml.settings_chargeprice, rootKey)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
when (key) {
"chargeprice_my_vehicle" -> {
updateMyVehiclesSummary()
}
"chargeprice_my_tariffs" -> {
updateMyTariffsSummary()
}

View File

@@ -86,7 +86,7 @@ class DataSettingsFragment : BaseSettingsFragment() {
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
when (key) {
"search_provider" -> {
if (prefs.searchProvider == "google") {

View File

@@ -25,7 +25,7 @@ class DeveloperSettingsFragment : BaseSettingsFragment() {
setPreferencesFromResource(R.xml.settings_developer, rootKey)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
}

View File

@@ -20,7 +20,7 @@ class SettingsFragment : BaseSettingsFragment() {
findPreference<Preference>("developer_options")?.isVisible = prefs.developerModeEnabled
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
}
}

View File

@@ -35,7 +35,7 @@ class UiSettingsFragment : BaseSettingsFragment() {
langPref.value = getAppLocale()
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
when (key) {
"darkmode" -> {
updateNightMode(prefs)

View File

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

View File

@@ -14,6 +14,24 @@ import java.time.Instant
class PreferenceDataSource(val context: Context) {
val sp = PreferenceManager.getDefaultSharedPreferences(context)
init {
if (sp.contains("map_scale")) {
// migration
val mapScale = sp.getString("map_scale", null)
sp.edit().putBoolean("map_scale_show", mapScale != "off")
.putBoolean("map_scale_meters_and_miles", mapScale == "both")
.putString(
"units", when (mapScale) {
"meters" -> "metric"
"miles" -> "imperial"
else -> "default"
}
)
.remove("map_scale")
.apply()
}
}
var dataSource: String
get() = sp.getString("data_source", "goingelectric")!!
set(value) {
@@ -252,8 +270,14 @@ class PreferenceDataSource(val context: Context) {
sp.edit().putBoolean("dev_mode_enabled", value).apply()
}
val mapScale: String
get() = sp.getString("map_scale", null) ?: "both"
val showMapScale: Boolean
get() = sp.getBoolean("map_scale_show", true)
val mapScaleMetersAndMiles: Boolean
get() = sp.getBoolean("map_scale_meters_and_miles", true)
val units: String
get() = sp.getString("units", null) ?: "default"
var currentMapLocation: LatLng
get() = sp.getLatLng("current_map_location") ?: LatLng(50.113388, 9.252536)
@@ -267,6 +291,12 @@ class PreferenceDataSource(val context: Context) {
sp.edit().putFloat("current_map_zoom", value).apply()
}
var currentMapMyLocationEnabled: Boolean
get() = sp.getBoolean("current_map_my_location_enabled", false)
set(value) {
sp.edit().putBoolean("current_map_my_location_enabled", value).apply()
}
var privacyAccepted: Boolean
get() = sp.getBoolean("privacy_accepted", false)
set(value) {

View File

@@ -309,9 +309,9 @@ fun time(value: Int): String {
else "%d:%02d h".format(h, min)
}
fun distance(meters: Number?): String? {
fun distance(meters: Number?, ctx: Context): String? {
if (meters == null) return null
if (shouldUseImperialUnits()) {
if (shouldUseImperialUnits(ctx)) {
val ft = meters.toDouble() / meterPerFt
val mi = meters.toDouble() / 1e3 / kmPerMile
return when {

View File

@@ -147,7 +147,7 @@
android:gravity="end"
android:maxLines="1"
android:minWidth="50dp"
android:text="@{BindingAdaptersKt.distance(distance)}"
android:text="@{BindingAdaptersKt.distance(distance, context)}"
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
app:layout_constraintBottom_toBottomOf="@+id/txtConnectors"
app:layout_constraintEnd_toStartOf="@+id/guideline2"

View File

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

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/textView20"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/referrals"
android:textAppearance="@style/TextAppearance.Material3.TitleSmall"
android:textColor="?colorPrimary" />
<TextView
android:id="@+id/textView21"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/referrals_info" />
<Button
android:id="@+id/referral_tesla"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/referral_tesla"
app:icon="@drawable/ic_tesla" />
</LinearLayout>

View File

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

View File

@@ -62,7 +62,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@{BindingAdaptersKt.distance(item.distanceMeters)}"
android:text="@{BindingAdaptersKt.distance(item.distanceMeters, context)}"
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
app:goneUnless="@{item.distanceMeters != null}"
app:layout_constraintEnd_toEndOf="@+id/icon"

View File

@@ -97,7 +97,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="@{BindingAdaptersKt.distance(item.distance)}"
android:text="@{BindingAdaptersKt.distance(item.distance, context)}"
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
app:goneUnless="@{item.distance != null}"
app:layout_constraintEnd_toStartOf="@id/btnDelete"

View File

@@ -113,7 +113,7 @@
<string name="goingelectric_forum">Forenthread bei GoingElectric.de</string>
<string name="contact">Kontakt</string>
<string name="menu_report_new_charger">Ladesäule melden</string>
<string name="edit_at_datasource">bei %s bearbeiten</string>
<string name="edit_at_datasource">Bei %s bearbeiten</string>
<string name="categories">Kategorien</string>
<string name="category_car_dealership">Autohaus</string>
<string name="category_service_on_motorway">Autobahnraststätte</string>
@@ -314,11 +314,12 @@
<string name="tesla_pricing_blocking_fee">Blockiergebühr: %s</string>
<string name="average_utilization">Durchschnittliche Auslastung</string>
<string name="website">Website</string>
<string name="pref_map_scale">Kartenmaßstab</string>
<string name="pref_map_scale_both">Meter und Meilen</string>
<string name="pref_map_scale_meters">Meter</string>
<string name="pref_map_scale_miles">Meilen</string>
<string name="pref_map_scale_off">aus</string>
<string name="pref_map_scale">Kartenmaßstab zeigen</string>
<string name="pref_map_scale_meters_and_miles">Meilen und Meter am Kartenmaßstab</string>
<string name="pref_units">Einheiten</string>
<string name="pref_units_default">Geräteeinstellung verwenden</string>
<string name="pref_units_metric">Metrisch</string>
<string name="pref_units_imperial">Imperial</string>
<string name="data_retrieved_at">Daten abgerufen %s</string>
<string name="settings_caching">Cache</string>
<string name="settings_cache_count">Cache-Größe</string>
@@ -348,7 +349,7 @@
<string name="auto_settings">Einstellungen</string>
<string name="welcome_android_auto">Android Auto-Unterstützung</string>
<string name="welcome_android_auto_detail">Auf unterstützen Autos kannst du EVMap auch mit Android Auto nutzen. Öffne dazu einfach die EVMap-App aus dem Menü von Android Auto.</string>
<string name="sounds_cool">klingt cool</string>
<string name="sounds_cool">Klingt cool</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap konnte das Fahrzeugmodell nicht erkennen.</string>
<string name="auto_chargeprice_vehicle_unknown">Keins der in der App ausgewählten Fahrzeuge passt zu diesem Fahrzeug (%1$s %2$s).</string>
<string name="auto_chargeprice_vehicle_ambiguous">Mehrere der in der App ausgewählten Fahrzeuge passen zu diesem Fahrzeug (%1$s %2$s).</string>
@@ -361,4 +362,7 @@
<string name="auto_multipage">(%d/%d)</string>
<string name="reload">Neu laden</string>
<string name="accept_privacy"><![CDATA[Ich habe die <a href=\"%s\">Datenschutzerklärung</a> von EVMap gelesen und bin damit einverstanden.]]></string>
<string name="referrals">Empfehlungslinks</string>
<string name="referrals_info">Du kannst auch einen der Empfehlungslinks unten benutzen, um den Entwickler mit deinem Kauf zu unterstützen.</string>
<string name="referral_tesla">Tesla</string>
</resources>

View File

@@ -55,7 +55,7 @@
<string name="filter_exclude_faults">Exclure les chargeurs avec des défauts signalés</string>
<string name="charge_cards">Méthodes de paiement</string>
<string name="goingelectric_forum">Fil de discussion du forum sur GoingElectric.de</string>
<string name="edit_at_datasource">modifier à %s</string>
<string name="edit_at_datasource">Modifier à %s</string>
<string name="categories">Catégories</string>
<string name="category_car_dealership">Concessionnaire automobile</string>
<string name="category_public_authorities">Pouvoirs publics</string>
@@ -308,7 +308,7 @@
<string name="auto_range">Autonomie</string>
<string name="auto_speed">Vitesse</string>
<string name="welcome_android_auto">Prise en charge dAndroid Auto</string>
<string name="sounds_cool">ça a l\'air cool</string>
<string name="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>

View File

@@ -127,7 +127,7 @@
<string name="category_amusement_park">Fornøyelsespark</string>
<string name="category_cinema">Kino</string>
<string name="category_parking_multi">Parkeringshus</string>
<string name="edit_at_datasource">rediger på %s</string>
<string name="edit_at_datasource">Rediger på %s</string>
<string name="category_camping">Campingplass</string>
<string name="category_service_on_motorway">Rasteplass (på motorvei)</string>
<string name="category_shopping_mall">Kjøpesenter</string>
@@ -326,7 +326,7 @@
<string name="welcome_android_auto_detail">Du kan også bruke EVMap inne i Android Auto på bilder som støtter dette ved å velge det i Android Auto-menyen.</string>
<string name="settings_android_auto_chargeprice_range">Prissammenligning for laderekkevidde fordelt på pris</string>
<string name="selecting_all">valgte alle elementene</string>
<string name="sounds_cool">den er grei</string>
<string name="sounds_cool">Den er grei</string>
<string name="auto_chargers_ahead">Kun ladere i kjøreretningen</string>
<string name="loading">Laster inn …</string>
<string name="auto_multipage_goto">Side %d</string>

View File

@@ -287,7 +287,7 @@
<string name="show_less">minder…</string>
<string name="map_type">Kaarttype</string>
<string name="map_details">Kaartdetails</string>
<string name="edit_at_datasource">aanpassen op %s</string>
<string name="edit_at_datasource">Aanpassen op %s</string>
<string name="charge_cards">Betaalmethoden</string>
<string name="pref_map_provider">Kaartaanbieder</string>
<string name="twitter">Twitter</string>
@@ -319,7 +319,7 @@
<string name="auto_settings">Instellingen</string>
<string name="welcome_android_auto">Android Auto support</string>
<string name="welcome_android_auto_detail">Je kan EVMap ook gebruiken in Android Auto op ondersteunde voertuigen. Selecteer gewoon de EVMap app in het Android Auto menu.</string>
<string name="sounds_cool">klinkt cool</string>
<string name="sounds_cool">Klinkt cool</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap kon je voertuigtype niet bepalen.</string>
<string name="auto_chargers_ahead">Alleen laadpunten in rijrichting</string>
<string name="settings_android_auto_chargeprice_range">Laadbereik voor prijsvergelijking</string>

View File

@@ -76,7 +76,7 @@
<string name="category_public_authorities">Autoridades públicas</string>
<string name="category_private_charger">Carregador privado</string>
<string name="category_rest_area">Área de descanso</string>
<string name="edit_at_datasource">editado em %s</string>
<string name="edit_at_datasource">Editado em %s</string>
<string name="categories">Categorias</string>
<string name="category_service_on_motorway">Área de serviço (autoestrada)</string>
<string name="category_service_off_motorway">Área de serviço (fora da autoestrada)</string>
@@ -319,11 +319,7 @@
<string name="average_utilization">Utilização média</string>
<string name="tesla_pricing_owners">Apenas veículos Tesla:</string>
<string name="website">Website</string>
<string name="pref_map_scale_off">desativar</string>
<string name="pref_map_scale_both">metros e milhas</string>
<string name="pref_map_scale_meters">metros</string>
<string name="pref_map_scale_miles">milhas</string>
<string name="pref_map_scale">Barra de escala do mapa</string>
<string name="pref_map_scale">Mostrar barra de escala do mapa</string>
<string name="data_retrieved_at">Informação atualizada %s</string>
<string name="settings_cache_count">Tamanho da cache</string>
<string name="settings_cache_clear">Limpar cache</string>
@@ -364,4 +360,6 @@
<string name="auto_chargeprice_vehicle_unknown">Nenhum dos veículos selecionados na app corresponde a este veículo (%1$s %2$s).</string>
<string name="auto_chargers_ahead">Apenas carregadores na direção do destino</string>
<string name="sounds_cool">Continuar</string>
<string name="reload">Recarregar</string>
<string name="accept_privacy"><![CDATA[Li e aceito a <a href=\"%s\">política de privacidade</a> do EVMap.]]></string>
</resources>

View File

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

View File

@@ -66,16 +66,14 @@
<item>goingelectric</item>
<item>openchargemap</item>
</string-array>
<string-array name="pref_map_scale_names">
<item>@string/pref_map_scale_both</item>
<item>@string/pref_map_scale_meters</item>
<item>@string/pref_map_scale_miles</item>
<item>@string/pref_map_scale_off</item>
<string-array name="pref_units_names">
<item>@string/pref_units_default</item>
<item>@string/pref_units_metric</item>
<item>@string/pref_units_imperial</item>
</string-array>
<string-array name="pref_map_scale_values" translatable="false">
<item>both</item>
<item>meters</item>
<item>miles</item>
<item>off</item>
<string-array name="pref_units_values" translatable="false">
<item>default</item>
<item>metric</item>
<item>imperial</item>
</string-array>
</resources>

View File

@@ -32,5 +32,8 @@
</string>
<string name="hide_on_scroll_fab_behavior">net.vonforst.evmap.ui.HideOnScrollFabBehavior</string>
<string name="paypal_link" translatable="false">https://paypal.me/johan98</string>
<string name="donate_link" translatable="false">https://ev-map.app/donate/</string>
<string name="tesla_referral_link" translatable="false">http://ts.la/johan94494</string>
<string name="copyright_summary">©20202023 Johan von Forstner and contributors</string>
<string name="acra_backend_url" translatable="false">https://acra.muc.vonforst.net/report</string>
</resources>

View File

@@ -113,7 +113,7 @@
<string name="goingelectric_forum">Forum thread at GoingElectric.de</string>
<string name="contact">Contact</string>
<string name="menu_report_new_charger">New charger</string>
<string name="edit_at_datasource">edit at %s</string>
<string name="edit_at_datasource">Edit at %s</string>
<string name="categories">Categories</string>
<string name="category_car_dealership">Car Dealership</string>
<string name="category_service_on_motorway">Service area (on motorway)</string>
@@ -314,11 +314,12 @@
<string name="tesla_pricing_blocking_fee">Blocking fee: %s</string>
<string name="average_utilization">Average Utilization</string>
<string name="website">Website</string>
<string name="pref_map_scale">Map scale bar</string>
<string name="pref_map_scale_both">meters and miles</string>
<string name="pref_map_scale_meters">meters</string>
<string name="pref_map_scale_miles">miles</string>
<string name="pref_map_scale_off">off</string>
<string name="pref_map_scale">Show map scale bar</string>
<string name="pref_map_scale_meters_and_miles">Miles and meters on map scale bar</string>
<string name="pref_units">Units</string>
<string name="pref_units_default">Device default</string>
<string name="pref_units_metric">Metric</string>
<string name="pref_units_imperial">Imperial</string>
<string name="data_retrieved_at">Data retrieved %s</string>
<string name="settings_caching">Caching</string>
<string name="settings_cache_count">Cache size</string>
@@ -348,7 +349,7 @@
<string name="auto_settings">Settings</string>
<string name="welcome_android_auto">Android Auto support</string>
<string name="welcome_android_auto_detail">You can also use EVMap from within Android Auto on supported cars. Simply select the EVMap app in the Android Auto menu.</string>
<string name="sounds_cool">sounds cool</string>
<string name="sounds_cool">Sounds cool</string>
<string name="auto_chargeprice_vehicle_unavailable">EVMap could not determine your vehicle model.</string>
<string name="auto_chargeprice_vehicle_unknown">None of the vehicles selected in the app matches this vehicle (%1$s %2$s).</string>
<string name="auto_chargeprice_vehicle_ambiguous">Multiple vehicles selected in the app match this vehicle (%1$s %2$s).</string>
@@ -361,4 +362,7 @@
<string name="auto_multipage">(%d/%d)</string>
<string name="reload">Reload</string>
<string name="accept_privacy"><![CDATA[I have read and accepted EVMap\'s <a href=\"%s\">privacy policy</a>.]]></string>
<string name="referrals">Referral links</string>
<string name="referrals_info">You can also use one of the referral links below to support the developer with your purchase.</string>
<string name="referral_tesla">Tesla</string>
</resources>

View File

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

View File

@@ -8,6 +8,13 @@
android:entryValues="@array/pref_language_values"
android:defaultValue="default"
android:summary="%s" />
<ListPreference
android:key="units"
android:title="@string/pref_units"
android:entries="@array/pref_units_names"
android:entryValues="@array/pref_units_values"
android:defaultValue="default"
android:summary="%s" />
<ListPreference
android:key="darkmode"
@@ -22,13 +29,15 @@
android:summaryOn="@string/pref_map_rotate_gestures_on"
android:summaryOff="@string/pref_map_rotate_gestures_off"
android:defaultValue="true" />
<ListPreference
android:key="map_scale"
<CheckBoxPreference
android:key="map_scale_show"
android:title="@string/pref_map_scale"
android:entries="@array/pref_map_scale_names"
android:entryValues="@array/pref_map_scale_values"
android:defaultValue="both"
android:summary="%s" />
android:defaultValue="true" />
<CheckBoxPreference
android:key="map_scale_meters_and_miles"
android:title="@string/pref_map_scale_meters_and_miles"
android:defaultValue="true" />
<CheckBoxPreference
android:key="navigate_use_maps"
android:title="@string/pref_navigate_use_maps"

View File

@@ -10,6 +10,7 @@ import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ApplicationProvider
import net.vonforst.evmap.FakeAndroidKeyStore
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
@@ -18,6 +19,7 @@ import org.robolectric.annotation.internal.DoNotInstrument
@RunWith(RobolectricTestRunner::class)
@DoNotInstrument
@Ignore("Disabled because Robolectric does not yet support API 34")
class CarAppTest {
private val testCarContext =
TestCarContext.createCarContext(ApplicationProvider.getApplicationContext()).apply {
@@ -43,7 +45,7 @@ class CarAppTest {
val screenCreated =
testCarContext.getCarService(TestScreenManager::class.java).screensPushed.last()
// location permission required
assert(screenCreated is PermissionScreen)
// accept privacy required
assert(screenCreated is AcceptPrivacyScreen)
}
}

View File

@@ -1,16 +1,16 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.8.21'
ext.kotlin_version = '1.9.0'
ext.about_libs_version = '8.9.4'
ext.nav_version = '2.5.3'
ext.nav_version = '2.7.1'
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.0.2'
classpath 'com.android.tools.build:gradle:8.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libs_version"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"

View File

@@ -29,6 +29,9 @@ be put into the app in the form of a resource file called `apikeys.xml` under
<string name="fronyx_key" translatable="false">
insert your Fronyx key here
</string>
<string name="acra_credentials" translatable="false">
insert your ACRA crash reporting credentials here
</string>
</resources>
```
@@ -186,4 +189,12 @@ The API is not publically available, contact [fronyx](https://fronyx.io/contact-
key and documentation.
If you don't want to test this functionality, simply leave the API key blank.
</details>
</details>
Crash reporting
---------------
Crash reporting for release builds is done using [ACRA](https://github.com/ACRA/acra).
This should not be needed for debugging.
If you still want to try it out, you can host any compatible backend such as
[Acrarium](https://github.com/F43nd1r/Acrarium/) yourself.

View File

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

View File

@@ -0,0 +1,8 @@
Verbesserungen:
- Neue Einstellung für Maßeinheiten
- Anpassungen für Android 14
- Android Auto: Weitere Detailbeschreibungen zu den Ladestationen
- Android Auto: Löschbutton in der Filterliste
Fehler behoben:
- Fehler beim Laden der EnBW Echtzeitdaten

View File

@@ -0,0 +1,5 @@
Verbesserungen:
- Beim Start der App wird nun der zuletzt gesehene Kartenausschnitt gezeigt
Fehler behoben:
- Abstürze behoben

View File

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

View File

@@ -0,0 +1,8 @@
Improvements:
- New setting for units of measurement
- Adjustments for Android 14
- Android Auto: More detailed descriptions of chargers
- Android Auto: Delete button in filter list
Bugfixes:
- Errors loading realtime data from EnBW

View File

@@ -0,0 +1,5 @@
Improvements:
- When starting the app, the last viewed map area will be shown
Bugfixes:
- Fixed crashes