mirror of
https://github.com/ev-map/EVMap.git
synced 2025-12-26 08:37:45 -05:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb7ade5e48 | ||
|
|
a59444e24b | ||
|
|
c6b7157d5b | ||
|
|
3d9a622f09 | ||
|
|
50bb245777 | ||
|
|
128cebfc20 | ||
|
|
c106bc40cc | ||
|
|
52af10d549 |
@@ -13,8 +13,8 @@ android {
|
||||
applicationId "net.vonforst.evmap"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 44
|
||||
versionName "0.7.0"
|
||||
versionCode 45
|
||||
versionName "0.7.1"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import net.vonforst.evmap.ui.ChargerIconGenerator
|
||||
import net.vonforst.evmap.ui.availabilityText
|
||||
import net.vonforst.evmap.ui.getMarkerTint
|
||||
import net.vonforst.evmap.utils.distanceBetween
|
||||
import java.io.IOException
|
||||
import java.time.Duration
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
@@ -413,62 +414,69 @@ class MapScreen(ctx: CarContext, val session: EVMapSession, val favorites: Boole
|
||||
return
|
||||
}
|
||||
updateCoroutine = lifecycleScope.launch {
|
||||
// load chargers
|
||||
if (favorites) {
|
||||
chargers = db.chargeLocationsDao().getAllChargeLocationsAsync().sortedBy {
|
||||
distanceBetween(
|
||||
location.latitude, location.longitude,
|
||||
it.coordinates.lat, it.coordinates.lng
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val response = api.getChargepointsRadius(
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
searchRadius,
|
||||
zoom = 16f
|
||||
)
|
||||
chargers =
|
||||
response.body()?.chargelocations?.filterIsInstance(ChargeLocation::class.java)
|
||||
chargers?.let {
|
||||
if (it.size < 6) {
|
||||
// try again with larger radius
|
||||
val response = api.getChargepointsRadius(
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
searchRadius * 5,
|
||||
zoom = 16f
|
||||
try {
|
||||
// load chargers
|
||||
if (favorites) {
|
||||
chargers = db.chargeLocationsDao().getAllChargeLocationsAsync().sortedBy {
|
||||
distanceBetween(
|
||||
location.latitude, location.longitude,
|
||||
it.coordinates.lat, it.coordinates.lng
|
||||
)
|
||||
chargers =
|
||||
response.body()?.chargelocations?.filterIsInstance(ChargeLocation::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove outdated availabilities
|
||||
availabilities = availabilities.filter {
|
||||
Duration.between(
|
||||
it.value.first,
|
||||
ZonedDateTime.now()
|
||||
) > availabilityUpdateThreshold
|
||||
}.toMutableMap()
|
||||
|
||||
// update availabilities
|
||||
chargers?.take(maxRows)?.map {
|
||||
lifecycleScope.async {
|
||||
// update only if not yet stored
|
||||
if (!availabilities.containsKey(it.id)) {
|
||||
val date = ZonedDateTime.now()
|
||||
val availability = getAvailability(it).data
|
||||
if (availability != null) {
|
||||
availabilities[it.id] = date to availability
|
||||
} else {
|
||||
val response = api.getChargepointsRadius(
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
searchRadius,
|
||||
zoom = 16f
|
||||
)
|
||||
chargers =
|
||||
response.body()?.chargelocations?.filterIsInstance(ChargeLocation::class.java)
|
||||
chargers?.let {
|
||||
if (it.size < 6) {
|
||||
// try again with larger radius
|
||||
val response = api.getChargepointsRadius(
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
searchRadius * 5,
|
||||
zoom = 16f
|
||||
)
|
||||
chargers =
|
||||
response.body()?.chargelocations?.filterIsInstance(ChargeLocation::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
}?.awaitAll()
|
||||
|
||||
updateCoroutine = null
|
||||
invalidate()
|
||||
// remove outdated availabilities
|
||||
availabilities = availabilities.filter {
|
||||
Duration.between(
|
||||
it.value.first,
|
||||
ZonedDateTime.now()
|
||||
) > availabilityUpdateThreshold
|
||||
}.toMutableMap()
|
||||
|
||||
// update availabilities
|
||||
chargers?.take(maxRows)?.map {
|
||||
lifecycleScope.async {
|
||||
// update only if not yet stored
|
||||
if (!availabilities.containsKey(it.id)) {
|
||||
val date = ZonedDateTime.now()
|
||||
val availability = getAvailability(it).data
|
||||
if (availability != null) {
|
||||
availabilities[it.id] = date to availability
|
||||
}
|
||||
}
|
||||
}
|
||||
}?.awaitAll()
|
||||
|
||||
updateCoroutine = null
|
||||
invalidate()
|
||||
} catch (e: IOException) {
|
||||
withContext(Dispatchers.Main) {
|
||||
CarToast.makeText(carContext, R.string.connection_error, CarToast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -612,22 +620,29 @@ class ChargerDetailScreen(ctx: CarContext, val chargerSparse: ChargeLocation) :
|
||||
|
||||
private fun loadCharger() {
|
||||
lifecycleScope.launch {
|
||||
val response = api.getChargepointDetail(chargerSparse.id)
|
||||
charger = response.body()?.chargelocations?.get(0) as ChargeLocation
|
||||
try {
|
||||
val response = api.getChargepointDetail(chargerSparse.id)
|
||||
charger = response.body()?.chargelocations?.get(0) as ChargeLocation
|
||||
|
||||
val photo = charger?.photos?.firstOrNull()
|
||||
photo?.let {
|
||||
val size = (carContext.resources.displayMetrics.density * 64).roundToInt()
|
||||
val url = "https://api.goingelectric.de/chargepoints/photo/?key=${apikey}" +
|
||||
"&id=${photo.id}&size=${size}"
|
||||
val request = ImageRequest.Builder(carContext).data(url).build()
|
||||
this@ChargerDetailScreen.photo =
|
||||
(carContext.imageLoader.execute(request).drawable as BitmapDrawable).bitmap
|
||||
val photo = charger?.photos?.firstOrNull()
|
||||
photo?.let {
|
||||
val size = (carContext.resources.displayMetrics.density * 64).roundToInt()
|
||||
val url = "https://api.goingelectric.de/chargepoints/photo/?key=${apikey}" +
|
||||
"&id=${photo.id}&size=${size}"
|
||||
val request = ImageRequest.Builder(carContext).data(url).build()
|
||||
this@ChargerDetailScreen.photo =
|
||||
(carContext.imageLoader.execute(request).drawable as BitmapDrawable).bitmap
|
||||
}
|
||||
|
||||
availability = charger?.let { getAvailability(it).data }
|
||||
|
||||
invalidate()
|
||||
} catch (e: IOException) {
|
||||
withContext(Dispatchers.Main) {
|
||||
CarToast.makeText(carContext, R.string.connection_error, CarToast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
availability = charger?.let { getAvailability(it).data }
|
||||
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,13 +132,31 @@ class ChargepriceAdapter() :
|
||||
}
|
||||
|
||||
class CheckableConnectorAdapter : DataBindingAdapter<Chargepoint>() {
|
||||
private var checkedItem: Int = 0
|
||||
private var checkedItem: Int? = 0
|
||||
|
||||
var enabledConnectors: List<String>? = null
|
||||
get() = field
|
||||
set(value) {
|
||||
field = value
|
||||
checkedItem?.let {
|
||||
if (value != null && getItem(it).type !in value) {
|
||||
val index = currentList.indexOfFirst {
|
||||
it.type in value
|
||||
}
|
||||
checkedItem = if (index == -1) null else index
|
||||
onCheckedItemChangedListener?.invoke(getCheckedItem())
|
||||
}
|
||||
}
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int = R.layout.item_connector_button
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder<Chargepoint>, position: Int) {
|
||||
super.bind(holder, getItem(position))
|
||||
val item = getItem(position)
|
||||
super.bind(holder, item)
|
||||
val binding = holder.binding as ItemConnectorButtonBinding
|
||||
binding.enabled = enabledConnectors?.let { item.type in it } ?: true
|
||||
val root = binding.root as CheckableConstraintLayout
|
||||
root.isChecked = checkedItem == position
|
||||
root.setOnClickListener {
|
||||
@@ -148,18 +166,18 @@ class CheckableConnectorAdapter : DataBindingAdapter<Chargepoint>() {
|
||||
if (checked) {
|
||||
checkedItem = position
|
||||
notifyDataSetChanged()
|
||||
onCheckedItemChangedListener?.invoke(getCheckedItem())
|
||||
onCheckedItemChangedListener?.invoke(getCheckedItem()!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCheckedItem(): Chargepoint = getItem(checkedItem)
|
||||
fun getCheckedItem(): Chargepoint? = checkedItem?.let { getItem(it) }
|
||||
|
||||
fun setCheckedItem(item: Chargepoint) {
|
||||
checkedItem = currentList.indexOf(item)
|
||||
fun setCheckedItem(item: Chargepoint?) {
|
||||
checkedItem = item?.let { currentList.indexOf(item) } ?: null
|
||||
}
|
||||
|
||||
var onCheckedItemChangedListener: ((Chargepoint) -> Unit)? = null
|
||||
var onCheckedItemChangedListener: ((Chargepoint?) -> Unit)? = null
|
||||
}
|
||||
|
||||
class ChargepriceTagsAdapter() :
|
||||
|
||||
@@ -33,13 +33,18 @@ data class ChargepriceStation(
|
||||
@Json(name = "charge_points") val chargePoints: List<ChargepriceChargepoint>
|
||||
) {
|
||||
companion object {
|
||||
fun fromGoingelectric(geCharger: ChargeLocation): ChargepriceStation {
|
||||
fun fromGoingelectric(
|
||||
geCharger: ChargeLocation,
|
||||
compatibleConnectors: List<String>
|
||||
): ChargepriceStation {
|
||||
return ChargepriceStation(
|
||||
geCharger.coordinates.lng,
|
||||
geCharger.coordinates.lat,
|
||||
geCharger.address.country,
|
||||
geCharger.network,
|
||||
geCharger.chargepoints.map {
|
||||
geCharger.chargepoints.filter {
|
||||
it.type in compatibleConnectors
|
||||
}.map {
|
||||
ChargepriceChargepoint(it.power, it.type)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.vonforst.evmap.api.goingelectric
|
||||
|
||||
import android.util.Log
|
||||
import com.squareup.moshi.*
|
||||
import java.lang.reflect.Type
|
||||
import java.time.Instant
|
||||
@@ -130,11 +131,18 @@ internal class HoursAdapter {
|
||||
return Hours(null, null)
|
||||
} else {
|
||||
val match = regex.find(str)
|
||||
?: throw IllegalArgumentException("$str does not match hours format")
|
||||
return Hours(
|
||||
LocalTime.parse(match.groupValues[1]),
|
||||
LocalTime.parse(match.groupValues[2])
|
||||
)
|
||||
if (match != null) {
|
||||
return Hours(
|
||||
LocalTime.parse(match.groupValues[1]),
|
||||
LocalTime.parse(match.groupValues[2])
|
||||
)
|
||||
} else {
|
||||
// I cannot reproduce this case, but it seems to occur once in a while
|
||||
Log.e("GoingElectricApi", "invalid hours value: " + str)
|
||||
return Hours(
|
||||
LocalTime.MIN, LocalTime.MIN
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package net.vonforst.evmap.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
@@ -73,6 +75,7 @@ class ChargepriceFragment : DialogFragment() {
|
||||
)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
@@ -107,14 +110,10 @@ class ChargepriceFragment : DialogFragment() {
|
||||
)
|
||||
}
|
||||
vm.chargepriceMetaForChargepoint.observe(viewLifecycleOwner) {
|
||||
chargepriceAdapter.meta = it.data
|
||||
chargepriceAdapter.meta = it?.data
|
||||
}
|
||||
|
||||
val connectorsAdapter = CheckableConnectorAdapter()
|
||||
binding.connectorsList.apply {
|
||||
adapter = connectorsAdapter
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
val observer: Observer<Chargepoint> = Observer {
|
||||
connectorsAdapter.setCheckedItem(it)
|
||||
@@ -126,6 +125,15 @@ class ChargepriceFragment : DialogFragment() {
|
||||
vm.chargepoint.observe(viewLifecycleOwner, observer)
|
||||
}
|
||||
|
||||
vm.vehicleCompatibleConnectors.observe(viewLifecycleOwner) {
|
||||
connectorsAdapter.enabledConnectors = it
|
||||
}
|
||||
|
||||
binding.connectorsList.apply {
|
||||
adapter = connectorsAdapter
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
binding.imgChargepriceLogo.setOnClickListener {
|
||||
(requireActivity() as MapsActivity).openUrl("https://www.chargeprice.app/?poi_id=${charger.id}&poi_source=going_electric")
|
||||
}
|
||||
@@ -137,7 +145,14 @@ class ChargepriceFragment : DialogFragment() {
|
||||
binding.batteryRange.setLabelFormatter { value: Float ->
|
||||
val fmt = NumberFormat.getNumberInstance()
|
||||
fmt.maximumFractionDigits = 0
|
||||
fmt.format(value.toDouble())
|
||||
fmt.format(value.toDouble()) + "%"
|
||||
}
|
||||
binding.batteryRange.setOnTouchListener { _: View, motionEvent: MotionEvent ->
|
||||
when (motionEvent.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> vm.batteryRangeSliderDragging.value = true
|
||||
MotionEvent.ACTION_UP -> vm.batteryRangeSliderDragging.value = false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
binding.toolbar.setOnMenuItemClickListener {
|
||||
@@ -150,8 +165,8 @@ class ChargepriceFragment : DialogFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
vm.chargePricesForChargepoint.observe(viewLifecycleOwner, Observer { res ->
|
||||
when (res.status) {
|
||||
vm.chargePricesForChargepoint.observe(viewLifecycleOwner) { res ->
|
||||
when (res?.status) {
|
||||
Status.ERROR -> {
|
||||
connectionErrorSnackbar?.dismiss()
|
||||
connectionErrorSnackbar = Snackbar
|
||||
@@ -166,13 +181,13 @@ class ChargepriceFragment : DialogFragment() {
|
||||
}
|
||||
connectionErrorSnackbar!!.show()
|
||||
}
|
||||
Status.SUCCESS -> {
|
||||
Status.SUCCESS, null -> {
|
||||
connectionErrorSnackbar?.dismiss()
|
||||
}
|
||||
Status.LOADING -> {
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -31,6 +31,8 @@ class SettingsFragment : PreferenceFragmentCompat(),
|
||||
}
|
||||
})
|
||||
|
||||
private lateinit var myVehiclePreference: ListPreference
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
|
||||
@@ -42,7 +44,7 @@ class SettingsFragment : PreferenceFragmentCompat(),
|
||||
(requireActivity() as MapsActivity).appBarConfiguration
|
||||
)
|
||||
|
||||
val myVehiclePreference = findPreference<ListPreference>("chargeprice_my_vehicle")!!
|
||||
myVehiclePreference = findPreference<ListPreference>("chargeprice_my_vehicle")!!
|
||||
myVehiclePreference.isEnabled = false
|
||||
vm.vehicles.observe(viewLifecycleOwner) { res ->
|
||||
res.data?.let { cars ->
|
||||
@@ -78,6 +80,15 @@ class SettingsFragment : PreferenceFragmentCompat(),
|
||||
"darkmode" -> {
|
||||
updateNightMode(prefs)
|
||||
}
|
||||
"chargeprice_my_vehicle" -> {
|
||||
vm.vehicles.value?.data?.let { cars ->
|
||||
val vehicle = cars.find { it.id == prefs.chargepriceMyVehicle }
|
||||
vehicle?.let {
|
||||
myVehiclePreference.summary = "${it.brand} ${it.name}"
|
||||
prefs.chargepriceMyVehicleDcChargeports = it.dcChargePorts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,13 @@ class PreferenceDataSource(val context: Context) {
|
||||
sp.edit().putString("chargeprice_my_vehicle", value).apply()
|
||||
}
|
||||
|
||||
var chargepriceMyVehicleDcChargeports: List<String>?
|
||||
get() = sp.getString("chargeprice_my_vehicle_dc_chargeports", null)?.split(",")
|
||||
set(value) {
|
||||
sp.edit().putString("chargeprice_my_vehicle_dc_chargeports", value?.joinToString(","))
|
||||
.apply()
|
||||
}
|
||||
|
||||
var chargepriceNoBaseFee: Boolean
|
||||
get() = sp.getBoolean("chargeprice_no_base_fee", false)
|
||||
set(value) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.use
|
||||
import androidx.core.text.HtmlCompat
|
||||
@@ -272,4 +273,22 @@ fun setRangeSliderListeners(slider: RangeSlider, attrChange: InverseBindingListe
|
||||
slider.addOnChangeListener { _, _, _ ->
|
||||
attrChange.onChange()
|
||||
}
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun colorEnabled(ctx: Context, enabled: Boolean): Int {
|
||||
val attr = if (enabled) {
|
||||
android.R.attr.textColorSecondary
|
||||
} else {
|
||||
android.R.attr.textColorHint
|
||||
}
|
||||
val typedValue = ctx.obtainStyledAttributes(intArrayOf(attr))
|
||||
val color = typedValue.getColor(0, 0)
|
||||
typedValue.recycle()
|
||||
return color
|
||||
}
|
||||
|
||||
@BindingAdapter("app:tint")
|
||||
fun setImageTintList(view: ImageView, @ColorInt color: Int) {
|
||||
view.imageTintList = ColorStateList.valueOf(color)
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package net.vonforst.evmap.viewmodel
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.*
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.banana.jsonapi2.HasOne
|
||||
import net.vonforst.evmap.api.chargeprice.*
|
||||
@@ -32,18 +31,63 @@ class ChargepriceViewModel(application: Application, chargepriceApiKey: String)
|
||||
}
|
||||
}
|
||||
|
||||
private val acConnectors = listOf(
|
||||
Chargepoint.CEE_BLAU,
|
||||
Chargepoint.CEE_ROT,
|
||||
Chargepoint.SCHUKO,
|
||||
Chargepoint.TYPE_1,
|
||||
Chargepoint.TYPE_2
|
||||
)
|
||||
private val plugMapping = mapOf(
|
||||
"ccs" to Chargepoint.CCS,
|
||||
"tesla_suc" to Chargepoint.SUPERCHARGER,
|
||||
"tesla_ccs" to Chargepoint.CCS,
|
||||
"chademo" to Chargepoint.CHADEMO
|
||||
)
|
||||
val vehicleCompatibleConnectors: LiveData<List<String>> by lazy {
|
||||
MutableLiveData<List<String>>().apply {
|
||||
value = prefs.chargepriceMyVehicleDcChargeports?.map {
|
||||
plugMapping.get(it)
|
||||
}?.filterNotNull()?.plus(acConnectors)
|
||||
}
|
||||
}
|
||||
|
||||
val noCompatibleConnectors: LiveData<Boolean> by lazy {
|
||||
MediatorLiveData<Boolean>().apply {
|
||||
value = false
|
||||
listOf(charger, vehicleCompatibleConnectors).forEach {
|
||||
addSource(it) {
|
||||
val charger = charger.value ?: return@addSource
|
||||
val connectors = vehicleCompatibleConnectors.value ?: return@addSource
|
||||
value = !charger.chargepoints.map { it.type }.any { it in connectors }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val batteryRange: MutableLiveData<List<Float>> by lazy {
|
||||
MutableLiveData<List<Float>>().apply {
|
||||
value = listOf(20f, 80f)
|
||||
}
|
||||
}
|
||||
val batteryRangeSliderDragging: MutableLiveData<Boolean> by lazy {
|
||||
MutableLiveData<Boolean>().apply {
|
||||
value = false
|
||||
}
|
||||
}
|
||||
|
||||
val chargePrices: MediatorLiveData<Resource<List<ChargePrice>>> by lazy {
|
||||
MediatorLiveData<Resource<List<ChargePrice>>>().apply {
|
||||
value = Resource.loading(null)
|
||||
listOf(charger, vehicle, batteryRange).forEach {
|
||||
listOf(
|
||||
charger,
|
||||
vehicle,
|
||||
batteryRange,
|
||||
batteryRangeSliderDragging,
|
||||
vehicleCompatibleConnectors
|
||||
).forEach {
|
||||
addSource(it) {
|
||||
loadPrices()
|
||||
if (!batteryRangeSliderDragging.value!!) loadPrices()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,18 +155,20 @@ class ChargepriceViewModel(application: Application, chargepriceApiKey: String)
|
||||
chargePrices.value = Resource.loading(null)
|
||||
val geCharger = charger.value
|
||||
val car = vehicle.value
|
||||
if (geCharger == null || car == null) {
|
||||
val compatibleConnectors = vehicleCompatibleConnectors.value
|
||||
if (geCharger == null || car == null || compatibleConnectors == null) {
|
||||
chargePrices.value = Resource.error(null, null)
|
||||
return
|
||||
}
|
||||
|
||||
val cpStation = ChargepriceStation.fromGoingelectric(geCharger, compatibleConnectors)
|
||||
|
||||
loadPricesJob?.cancel()
|
||||
loadPricesJob = viewModelScope.launch {
|
||||
delay(800)
|
||||
try {
|
||||
val result = api.getChargePrices(ChargepriceRequest().apply {
|
||||
dataAdapter = "going_electric"
|
||||
station = ChargepriceStation.fromGoingelectric(geCharger)
|
||||
station = cpStation
|
||||
vehicle = HasOne(car)
|
||||
options = ChargepriceOptions(
|
||||
batteryRange = batteryRange.value!!.map { it.toDouble() },
|
||||
|
||||
@@ -130,6 +130,20 @@
|
||||
app:layout_constraintStart_toStartOf="@+id/charge_prices_list"
|
||||
app:layout_constraintTop_toTopOf="@+id/charge_prices_list" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView9"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/chargeprice_no_compatible_connectors"
|
||||
app:goneUnless="@{vm.noCompatibleConnectors}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/charge_prices_list"
|
||||
app:layout_constraintTop_toTopOf="@+id/charge_prices_list" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView3"
|
||||
android:layout_width="0dp"
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
<variable
|
||||
name="item"
|
||||
type="Chargepoint" />
|
||||
|
||||
<variable
|
||||
name="enabled"
|
||||
type="boolean" />
|
||||
</data>
|
||||
|
||||
<net.vonforst.evmap.ui.CheckableConstraintLayout
|
||||
@@ -19,7 +23,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/button_outline"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:clickable="@{enabled}"
|
||||
android:focusable="true"
|
||||
android:layout_margin="4dp">
|
||||
|
||||
@@ -36,7 +40,8 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="?colorControlNormal"
|
||||
app:tint="@{BindingAdaptersKt.colorEnabled(context, enabled)}"
|
||||
tools:tint="?colorControlNormal"
|
||||
tools:srcCompat="@drawable/ic_connector_typ2" />
|
||||
|
||||
<TextView
|
||||
@@ -48,6 +53,7 @@
|
||||
android:layout_marginEnd="4dp"
|
||||
android:text="@{String.format("× %d", item.count)}"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
|
||||
android:textColor="@{BindingAdaptersKt.colorEnabled(context, enabled)}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/imageView"
|
||||
app:layout_constraintTop_toTopOf="@+id/imageView"
|
||||
@@ -61,6 +67,7 @@
|
||||
android:layout_marginBottom="4dp"
|
||||
android:text="@{item.formatPower()}"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
|
||||
android:textColor="@{BindingAdaptersKt.colorEnabled(context, enabled)}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -187,4 +187,5 @@
|
||||
<string name="close">schließen</string>
|
||||
<string name="chargeprice_title">Preisvergleich</string>
|
||||
<string name="chargeprice_connection_error">Could not load prices</string>
|
||||
<string name="chargeprice_no_compatible_connectors">Keiner der Anschlüsse dieser Ladestation ist mit deinem Fahrzeug kompatibel.</string>
|
||||
</resources>
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
<string name="chargeprice_per_kwh">per kWh</string>
|
||||
<string name="chargeprice_per_minute">per min</string>
|
||||
<string name="chargeprice_blocking_fee">Blocking fee >%s</string>
|
||||
<string name="chargeprice_no_tariffs_found">Keine geeigneten Tarife für diese Ladestation bei Chargeprice.app gefunden.</string>
|
||||
<string name="chargeprice_no_tariffs_found">Chargeprice.app found no charging plans compatible with this charger.</string>
|
||||
<string name="powered_by_chargeprice">powered by Chargeprice</string>
|
||||
<string name="chargeprice_base_fee">Base fee: %2$s%1$.2f/month</string>
|
||||
<string name="chargeprice_min_spend">Minimum spend: %2$s%1$.2f/month</string>
|
||||
@@ -186,4 +186,5 @@
|
||||
<string name="close">close</string>
|
||||
<string name="chargeprice_title">Prices</string>
|
||||
<string name="chargeprice_connection_error">Could not load prices</string>
|
||||
<string name="chargeprice_no_compatible_connectors">None of the connectors on this charging station is compatible with your vehicle.</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<PreferenceCategory android:title="@string/settings_ui">
|
||||
<ListPreference
|
||||
@@ -44,11 +45,13 @@
|
||||
<CheckBoxPreference
|
||||
android:key="chargeprice_no_base_fee"
|
||||
android:title="@string/pref_chargeprice_no_base_fee"
|
||||
android:defaultValue="false" />
|
||||
android:defaultValue="false"
|
||||
app:singleLineTitle="false" />
|
||||
<CheckBoxPreference
|
||||
android:key="chargeprice_show_provider_customer_tariffs"
|
||||
android:title="@string/pref_chargeprice_show_provider_customer_tariffs"
|
||||
android:summary="@string/pref_chargeprice_show_provider_customer_tariffs_summary"
|
||||
android:defaultValue="false" />
|
||||
android:defaultValue="false"
|
||||
app:singleLineTitle="false" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
@@ -65,7 +65,8 @@ class ChargepriceApiTest {
|
||||
val result = chargeprice.getChargePrices(
|
||||
ChargepriceRequest().apply {
|
||||
dataAdapter = "going_electric"
|
||||
station = ChargepriceStation.fromGoingelectric(charger)
|
||||
station =
|
||||
ChargepriceStation.fromGoingelectric(charger, listOf("Typ2", "Schuko"))
|
||||
options = ChargepriceOptions(energy = 22.0, duration = 60)
|
||||
}, "en"
|
||||
)
|
||||
|
||||
3
fastlane/metadata/android/de-DE/changelogs/45.txt
Normal file
3
fastlane/metadata/android/de-DE/changelogs/45.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Verbesserungen:
|
||||
- Kleinere Verbesserungen für die Chargeprice.app-Integration
|
||||
- Verschiedene Abstürze behoben
|
||||
3
fastlane/metadata/android/en-US/changelogs/45.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/45.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Improvements:
|
||||
- Minor improvements for the Chargeprice.app integration
|
||||
- Fixed various crashes
|
||||
Reference in New Issue
Block a user