add favorites view

This commit is contained in:
Johan von Forstner
2020-04-19 22:19:29 +02:00
parent febc72f190
commit 84bbdaf4ec
17 changed files with 363 additions and 25 deletions

View File

@@ -13,6 +13,7 @@ import net.vonforst.evmap.R
import net.vonforst.evmap.api.availability.ChargepointStatus
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.api.goingelectric.Chargepoint
import net.vonforst.evmap.viewmodel.FavoritesViewModel
interface Equatable {
override fun equals(other: Any?): Boolean;
@@ -29,15 +30,15 @@ abstract class DataBindingAdapter<T : Equatable>() :
}
override fun onBindViewHolder(holder: ViewHolder<T>, position: Int) =
holder.bind(getItem(position))
bind(holder, getItem(position))
class ViewHolder<T>(private val binding: ViewDataBinding) :
class ViewHolder<T>(val binding: ViewDataBinding) :
RecyclerView.ViewHolder(binding.root) {
}
fun bind(item: T) {
binding.setVariable(BR.item, item)
binding.executePendingBindings()
}
open fun bind(holder: ViewHolder<T>, item: T) {
holder.binding.setVariable(BR.item, item)
holder.binding.executePendingBindings()
}
class DiffCallback<T : Equatable> : DiffUtil.ItemCallback<T>() {
@@ -112,3 +113,13 @@ fun buildDetails(loc: ChargeLocation?, ctx: Context): List<DetailAdapter.Detail>
else null
)
}
class FavoritesAdapter(val vm: FavoritesViewModel) : DataBindingAdapter<ChargeLocation>() {
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

@@ -44,7 +44,7 @@ data class ChargeLocation(
//val chargecards: Boolean?
@Embedded val openinghours: OpeningHours?,
@Embedded val cost: Cost?
) : ChargepointListItem() {
) : ChargepointListItem(), Equatable {
val maxPower: Double
get() {
return chargepoints.map { it.power }.max() ?: 0.0

View File

@@ -0,0 +1,94 @@
package net.vonforst.evmap.fragment
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.android.gms.maps.model.LatLng
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.FavoritesAdapter
import net.vonforst.evmap.databinding.FragmentFavoritesBinding
import net.vonforst.evmap.viewmodel.FavoritesViewModel
import net.vonforst.evmap.viewmodel.viewModelFactory
class FavoritesFragment : Fragment() {
private lateinit var binding: FragmentFavoritesBinding
private lateinit var fusedLocationClient: FusedLocationProviderClient
private val vm: FavoritesViewModel by viewModels(factoryProducer = {
viewModelFactory {
FavoritesViewModel(
requireActivity().application,
getString(R.string.goingelectric_key)
)
}
})
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_favorites, container, false
)
binding.lifecycleOwner = this
binding.vm = vm
fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireContext())
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
val navController = findNavController()
toolbar.setupWithNavController(
navController,
(requireActivity() as MapsActivity).appBarConfiguration
)
binding.favsList.apply {
adapter = FavoritesAdapter(vm)
layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
addItemDecoration(
DividerItemDecoration(
context, LinearLayoutManager.VERTICAL
)
)
}
vm.favorites.observe(viewLifecycleOwner, Observer {
print(it.toString())
})
if (ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
fusedLocationClient.lastLocation.addOnSuccessListener { location ->
vm.location.value = LatLng(location.latitude, location.longitude)
}
}
}
}

View File

@@ -78,6 +78,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
private lateinit var clusterIconGenerator: ClusterIconGenerator
private lateinit var chargerIconGenerator: ChargerIconGenerator
private lateinit var animator: MarkerAnimator
private lateinit var favToggle: MenuItem
override fun onCreateView(
inflater: LayoutInflater,
@@ -115,6 +116,9 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
bottomSheetBehavior = BottomSheetBehaviorGoogleMapsLike.from(binding.bottomSheet)
detailAppBarBehavior = MergedAppBarLayoutBehavior.from(binding.detailAppBar)
binding.detailAppBar.toolbar.inflateMenu(R.menu.detail)
favToggle = binding.detailAppBar.toolbar.menu.findItem(R.id.menu_fav)
setupObservers()
setupClickListeners()
setupAdapters()
@@ -173,6 +177,25 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
binding.detailAppBar.toolbar.setNavigationOnClickListener {
bottomSheetBehavior.state = BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED
}
binding.detailAppBar.toolbar.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_fav -> {
toggleFavorite()
true
}
else -> false
}
}
}
private fun toggleFavorite() {
val favs = vm.favorites.value ?: return
val charger = vm.chargerSparse.value ?: return
if (favs.find { it.id == charger.id } != null) {
vm.deleteFavorite(charger)
} else {
vm.insertFavorite(charger)
}
}
private fun setupObservers() {
@@ -193,6 +216,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
}
binding.fabDirections.show()
detailAppBarBehavior.setToolbarTitle(it.name)
updateFavoriteToggle()
} else {
bottomSheetBehavior.state = BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN
}
@@ -201,6 +225,19 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
val chargepoints = it.data
if (chargepoints != null) updateMap(chargepoints)
})
vm.favorites.observe(viewLifecycleOwner, Observer {
updateFavoriteToggle()
})
}
private fun updateFavoriteToggle() {
val favs = vm.favorites.value ?: return
val charger = vm.chargerSparse.value ?: return
if (favs.find { it.id == charger.id } != null) {
favToggle.setIcon(R.drawable.ic_fav)
} else {
favToggle.setIcon(R.drawable.ic_fav_no)
}
}
private fun setupAdapters() {
@@ -340,8 +377,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) ==
PackageManager.PERMISSION_GRANTED
) == PackageManager.PERMISSION_GRANTED
}
private fun updateMap(chargepoints: List<ChargepointListItem>) {

View File

@@ -7,10 +7,10 @@ import net.vonforst.evmap.api.goingelectric.ChargeLocation
@Dao
interface ChargeLocationsDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg locations: ChargeLocation)
suspend fun insert(vararg locations: ChargeLocation)
@Delete
fun delete(vararg locations: ChargeLocation)
suspend fun delete(vararg locations: ChargeLocation)
@Query("SELECT * FROM chargelocation")
fun getAllChargeLocations(): LiveData<List<ChargeLocation>>

View File

@@ -19,23 +19,23 @@ class Converters {
}
@TypeConverter
fun fromChargepointList(value: List<Chargepoint>): String {
fun fromChargepointList(value: List<Chargepoint>?): String {
return chargepointListAdapter.toJson(value)
}
@TypeConverter
fun toChargepointList(value: String): List<Chargepoint> {
return chargepointListAdapter.fromJson(value)!!
fun toChargepointList(value: String): List<Chargepoint>? {
return chargepointListAdapter.fromJson(value)
}
@TypeConverter
fun fromChargerPhotoList(value: List<ChargerPhoto>): String {
fun fromChargerPhotoList(value: List<ChargerPhoto>?): String {
return chargerPhotoListAdapter.toJson(value)
}
@TypeConverter
fun toChargerPhotoList(value: String): List<ChargerPhoto> {
return chargerPhotoListAdapter.fromJson(value)!!
fun toChargerPhotoList(value: String): List<ChargerPhoto>? {
return chargerPhotoListAdapter.fromJson(value)
}
@TypeConverter

View File

@@ -0,0 +1,54 @@
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 com.google.android.gms.maps.model.LatLng
import kotlinx.coroutines.launch
import net.vonforst.evmap.api.goingelectric.ChargeLocation
import net.vonforst.evmap.api.goingelectric.GoingElectricApi
import net.vonforst.evmap.storage.AppDatabase
class FavoritesViewModel(application: Application, geApiKey: String) :
AndroidViewModel(application) {
private var api = GoingElectricApi.create(geApiKey)
private var db = AppDatabase.getInstance(application)
val favorites: LiveData<List<ChargeLocation>> by lazy {
db.chargeLocationsDao().getAllChargeLocations()
}
val location: MutableLiveData<LatLng> by lazy {
MutableLiveData<LatLng>()
}
/*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)
}
}
} else {
value = null
}
}
}
}*/
fun insertFavorite(charger: ChargeLocation) {
viewModelScope.launch {
db.chargeLocationsDao().insert(charger)
}
}
fun deleteFavorite(charger: ChargeLocation) {
viewModelScope.launch {
db.chargeLocationsDao().delete(charger)
}
}
}

View File

@@ -91,11 +91,15 @@ class MapViewModel(application: Application, geApiKey: String) : AndroidViewMode
}
fun insertFavorite(charger: ChargeLocation) {
db.chargeLocationsDao().insert(charger)
viewModelScope.launch {
db.chargeLocationsDao().insert(charger)
}
}
fun deleteFavorite(charger: ChargeLocation) {
db.chargeLocationsDao().delete(charger)
viewModelScope.launch {
db.chargeLocationsDao().delete(charger)
}
}
private fun loadChargepoints(mapPosition: MapPosition) {