show availability in favorites view

This commit is contained in:
Johan von Forstner
2020-04-22 20:33:06 +02:00
parent 6be926c308
commit cfb6af28c0
6 changed files with 115 additions and 51 deletions

View File

@@ -123,11 +123,7 @@ fun buildDetails(loc: ChargeLocation?, ctx: Context): List<DetailAdapter.Detail>
}
class FavoritesAdapter(val vm: FavoritesViewModel) : DataBindingAdapter<ChargeLocation>() {
class FavoritesAdapter(val vm: FavoritesViewModel) :
DataBindingAdapter<FavoritesViewModel.FavoritesListItem>() {
override fun getItemViewType(position: Int): Int = R.layout.item_favorite
override fun bind(holder: ViewHolder<ChargeLocation>, item: ChargeLocation) {
holder.binding.setVariable(BR.vm, vm)
super.bind(holder, item)
}
}

View File

@@ -1,11 +1,15 @@
package net.vonforst.evmap.api.availability
import com.facebook.stetho.okhttp3.StethoInterceptor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import net.vonforst.evmap.api.await
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.api.goingelectric.Chargepoint
import net.vonforst.evmap.viewmodel.Resource
import okhttp3.OkHttpClient
import okhttp3.Request
import retrofit2.HttpException
import java.io.IOException
import java.util.concurrent.TimeUnit
@@ -104,4 +108,26 @@ val availabilityDetectors = listOf(
okhttp,
"6336fe713f2eb7fa04b97ff6651b76f8"
) // SW Kiel*/
)
)
suspend fun getAvailability(charger: ChargeLocation): Resource<ChargeLocationStatus> {
var value: Resource<ChargeLocationStatus>? = null
withContext(Dispatchers.IO) {
for (ad in availabilityDetectors) {
try {
value = Resource.success(ad.getAvailability(charger))
break
} catch (e: IOException) {
value = Resource.error(e.message, null)
e.printStackTrace()
} catch (e: HttpException) {
value = Resource.error(e.message, null)
e.printStackTrace()
} catch (e: AvailabilityDetectorException) {
value = Resource.error(e.message, null)
e.printStackTrace()
}
}
}
return value ?: Resource.error(null, null)
}

View File

@@ -1,12 +1,16 @@
package net.vonforst.evmap.viewmodel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.*
import com.google.android.gms.maps.model.LatLng
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import net.vonforst.evmap.adapter.Equatable
import net.vonforst.evmap.api.availability.ChargeLocationStatus
import net.vonforst.evmap.api.availability.ChargepointStatus
import net.vonforst.evmap.api.availability.getAvailability
import net.vonforst.evmap.api.distanceBetween
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.api.goingelectric.GoingElectricApi
import net.vonforst.evmap.storage.AppDatabase
@@ -24,21 +28,68 @@ class FavoritesViewModel(application: Application, geApiKey: String) :
MutableLiveData<LatLng>()
}
/*val availability: MediatorLiveData<Map<Long, Resource<ChargeLocationStatus>>> by lazy {
val availability: MediatorLiveData<Map<Long, Resource<ChargeLocationStatus>>> by lazy {
MediatorLiveData<Map<Long, Resource<ChargeLocationStatus>>>().apply {
addSource(favorites) { chargers ->
if (chargers != null) {
viewModelScope.launch {
chargers.map {
availability.value = Resource.loading(null)
val data = hashMapOf<Long, Resource<ChargeLocationStatus>>()
chargers.forEach { charger ->
data[charger.id] = Resource.loading(null)
}
availability.value = data
chargers.map { charger ->
async {
data[charger.id] = getAvailability(charger)
availability.value = data
}
}.awaitAll()
}
} else {
value = null
}
}
}
}*/
}
val listData: MediatorLiveData<List<FavoritesListItem>> by lazy {
MediatorLiveData<List<FavoritesListItem>>().apply {
val callback = { _: Any ->
listData.value = favorites.value?.map { charger ->
FavoritesListItem(
charger,
totalAvailable(charger.id),
charger.chargepoints.sumBy { it.count },
location.value.let { loc ->
if (loc == null) null else {
distanceBetween(
loc.latitude,
loc.longitude,
charger.coordinates.lat,
charger.coordinates.lng
) / 1000
}
})
}
}
addSource(favorites, callback)
addSource(location, callback)
addSource(availability, callback)
}
}
data class FavoritesListItem(
val charger: ChargeLocation,
val available: Int?,
val total: Int,
val distance: Double?
) : Equatable
private fun totalAvailable(id: Long): Int? {
val values = availability.value?.get(id)?.data?.status?.values ?: return null
return values.sumBy { it.filter { it == ChargepointStatus.AVAILABLE }.size }
}
fun insertFavorite(charger: ChargeLocation) {
viewModelScope.launch {

View File

@@ -3,12 +3,9 @@ package net.vonforst.evmap.viewmodel
import android.app.Application
import androidx.lifecycle.*
import com.google.android.gms.maps.model.LatLngBounds
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.vonforst.evmap.api.availability.AvailabilityDetectorException
import net.vonforst.evmap.api.availability.ChargeLocationStatus
import net.vonforst.evmap.api.availability.availabilityDetectors
import net.vonforst.evmap.api.availability.getAvailability
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.api.goingelectric.ChargepointList
import net.vonforst.evmap.api.goingelectric.ChargepointListItem
@@ -16,9 +13,7 @@ import net.vonforst.evmap.api.goingelectric.GoingElectricApi
import net.vonforst.evmap.storage.AppDatabase
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException
import retrofit2.Response
import java.io.IOException
data class MapPosition(val bounds: LatLngBounds, val zoom: Float)
@@ -133,25 +128,7 @@ class MapViewModel(application: Application, geApiKey: String) : AndroidViewMode
private suspend fun loadAvailability(charger: ChargeLocation) {
availability.value = Resource.loading(null)
var value: Resource<ChargeLocationStatus>? = null
withContext(Dispatchers.IO) {
for (ad in availabilityDetectors) {
try {
value = Resource.success(ad.getAvailability(charger))
break
} catch (e: IOException) {
value = Resource.error(e.message, null)
e.printStackTrace()
} catch (e: HttpException) {
value = Resource.error(e.message, null)
e.printStackTrace()
} catch (e: AvailabilityDetectorException) {
value = Resource.error(e.message, null)
e.printStackTrace()
}
}
}
availability.value = value
availability.value = getAvailability(charger)
}
private fun loadChargerDetails(charger: ChargeLocation) {

View File

@@ -33,6 +33,6 @@
android:id="@+id/favs_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:data="@{vm.favorites}" />
app:data="@{vm.listData}" />
</LinearLayout>
</layout>

View File

@@ -6,14 +6,11 @@
<data>
<import type="net.vonforst.evmap.api.UtilsKt" />
<import type="net.vonforst.evmap.viewmodel.Status" />
<variable
name="item"
type="net.vonforst.evmap.api.goingelectric.ChargeLocation" />
<variable
name="vm"
type="net.vonforst.evmap.viewmodel.FavoritesViewModel" />
type="net.vonforst.evmap.viewmodel.FavoritesViewModel.FavoritesListItem" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
@@ -25,7 +22,7 @@
android:id="@+id/textView15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.name}"
android:text="@{item.charger.name}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -37,7 +34,7 @@
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="@{item.address.toString()}"
android:text="@{item.charger.address.toString()}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView15"
@@ -49,7 +46,7 @@
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="@{item.formatChargepoints()}"
android:text="@{item.charger.formatChargepoints()}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:layout_constraintStart_toStartOf="@+id/textView2"
app:layout_constraintTop_toBottomOf="@+id/textView2"
@@ -59,9 +56,26 @@
android:id="@+id/textView16"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/distance_format(UtilsKt.distanceBetween(vm.location.latitude, vm.location.longitude, item.coordinates.lat, item.coordinates.lng) / 1000)}"
android:text="@{@string/distance_format(item.distance)}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
tools:text="9999,9 km" />
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/rounded_rect"
android:padding="2dp"
android:text="@{String.format(&quot;%d/%d&quot;, item.available, item.total)}"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="@android:color/white"
app:backgroundTintAvailability="@{item.available}"
app:goneUnless="@{item.available != null}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:backgroundTint="@color/available"
tools:text="80/99" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>