diff --git a/app/src/google/java/net/vonforst/evmap/auto/FilterScreen.kt b/app/src/google/java/net/vonforst/evmap/auto/FilterScreen.kt index afb284f1..71278ea4 100644 --- a/app/src/google/java/net/vonforst/evmap/auto/FilterScreen.kt +++ b/app/src/google/java/net/vonforst/evmap/auto/FilterScreen.kt @@ -7,7 +7,9 @@ import androidx.car.app.model.* import androidx.core.graphics.drawable.IconCompat import androidx.lifecycle.LiveData import net.vonforst.evmap.R +import net.vonforst.evmap.model.FILTERS_CUSTOM import net.vonforst.evmap.model.FILTERS_DISABLED +import net.vonforst.evmap.model.FILTERS_FAVORITES import net.vonforst.evmap.storage.AppDatabase import net.vonforst.evmap.storage.FilterProfile import net.vonforst.evmap.storage.PreferenceDataSource @@ -40,9 +42,12 @@ class FilterScreen(ctx: CarContext) : Screen(ctx) { } override fun onGetTemplate(): Template { + val filterStatus = + prefs.filterStatus.takeUnless { it == FILTERS_CUSTOM || it == FILTERS_FAVORITES } + ?: FILTERS_DISABLED return ListTemplate.Builder().apply { filterProfiles.value?.let { - setSingleList(buildFilterProfilesList(it.take(maxRows), prefs.filterStatus)) + setSingleList(buildFilterProfilesList(it.take(maxRows), filterStatus)) } ?: setLoading(true) setTitle(carContext.getString(R.string.menu_filter)) setHeaderAction(Action.BACK) diff --git a/app/src/google/java/net/vonforst/evmap/auto/MapScreen.kt b/app/src/google/java/net/vonforst/evmap/auto/MapScreen.kt index 230f9c19..cd6013fa 100644 --- a/app/src/google/java/net/vonforst/evmap/auto/MapScreen.kt +++ b/app/src/google/java/net/vonforst/evmap/auto/MapScreen.kt @@ -22,6 +22,7 @@ import net.vonforst.evmap.api.stringProvider import net.vonforst.evmap.model.ChargeLocation import net.vonforst.evmap.model.FILTERS_CUSTOM import net.vonforst.evmap.model.FILTERS_DISABLED +import net.vonforst.evmap.model.FILTERS_FAVORITES import net.vonforst.evmap.storage.AppDatabase import net.vonforst.evmap.storage.PreferenceDataSource import net.vonforst.evmap.ui.availabilityText @@ -69,7 +70,8 @@ class MapScreen(ctx: CarContext, val session: EVMapSession, val favorites: Boole private val referenceData = api.getReferenceData(lifecycleScope, carContext) private val filterStatus = MutableLiveData().apply { - value = prefs.filterStatus.takeUnless { it == FILTERS_CUSTOM } ?: FILTERS_DISABLED + value = prefs.filterStatus.takeUnless { it == FILTERS_CUSTOM || it == FILTERS_FAVORITES } + ?: FILTERS_DISABLED } private val filterValues = db.filterValueDao().getFilterValues(filterStatus, prefs.dataSource) private val filters = api.getFilters(referenceData, carContext.stringProvider()) @@ -139,7 +141,9 @@ class MapScreen(ctx: CarContext, val session: EVMapSession, val favorites: Boole screenManager.pushForResult(FilterScreen(carContext)) { chargers = null numUpdates = 0 - filterStatus.value = prefs.filterStatus + filterStatus.value = + prefs.filterStatus.takeUnless { it == FILTERS_CUSTOM || it == FILTERS_FAVORITES } + ?: FILTERS_DISABLED } session.mapScreen = null } diff --git a/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt b/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt index 647b7cb6..5d843fa7 100644 --- a/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt +++ b/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt @@ -1100,6 +1100,11 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac R.id.menu_group_filter_profiles, Menu.NONE, Menu.NONE, R.string.no_filters ) + val favoritesItem = popup.menu.add( + R.id.menu_group_filter_profiles, + Menu.NONE, + Menu.NONE, R.string.filter_favorites + ) profiles.forEach { profile -> val item = popup.menu.add( R.id.menu_group_filter_profiles, @@ -1116,11 +1121,12 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac profilesMap[FILTERS_DISABLED] = noFiltersItem profilesMap[FILTERS_CUSTOM] = customItem + profilesMap[FILTERS_FAVORITES] = favoritesItem popup.menu.setGroupCheckable(R.id.menu_group_filter_profiles, true, true); val manageFiltersItem = popup.menu.findItem(R.id.menu_manage_filter_profiles) - manageFiltersItem.isVisible = !profiles.isEmpty() + manageFiltersItem.isVisible = profiles.isNotEmpty() vm.filterStatus.observe(viewLifecycleOwner, Observer { id -> when (id) { @@ -1132,6 +1138,10 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac customItem.isVisible = true customItem.isChecked = true } + FILTERS_FAVORITES -> { + customItem.isVisible = false + favoritesItem.isChecked = true + } else -> { customItem.isVisible = false val item = profilesMap[id] diff --git a/app/src/main/java/net/vonforst/evmap/model/FiltersModel.kt b/app/src/main/java/net/vonforst/evmap/model/FiltersModel.kt index 2693aa90..59708066 100644 --- a/app/src/main/java/net/vonforst/evmap/model/FiltersModel.kt +++ b/app/src/main/java/net/vonforst/evmap/model/FiltersModel.kt @@ -129,4 +129,5 @@ fun FilterValues.getMultipleChoiceValue(key: String) = this.find { it.value.key == key }?.value as MultipleChoiceFilterValue? const val FILTERS_DISABLED = -2L -const val FILTERS_CUSTOM = -1L \ No newline at end of file +const val FILTERS_CUSTOM = -1L +const val FILTERS_FAVORITES = -3L \ No newline at end of file diff --git a/app/src/main/java/net/vonforst/evmap/storage/ChargeLocationsDao.kt b/app/src/main/java/net/vonforst/evmap/storage/ChargeLocationsDao.kt index e016862b..d8fdae7e 100644 --- a/app/src/main/java/net/vonforst/evmap/storage/ChargeLocationsDao.kt +++ b/app/src/main/java/net/vonforst/evmap/storage/ChargeLocationsDao.kt @@ -23,4 +23,12 @@ interface ChargeLocationsDao { @Query("SELECT * FROM chargelocation") fun getAllChargeLocationsBlocking(): List + + @Query("SELECT * FROM chargelocation WHERE lat >= :lat1 AND lat <= :lat2 AND lng >= :lng1 AND lng <= :lng2") + suspend fun getChargeLocationsInBoundsAsync( + lat1: Double, + lat2: Double, + lng1: Double, + lng2: Double + ): List } \ No newline at end of file diff --git a/app/src/main/java/net/vonforst/evmap/storage/FilterValueDao.kt b/app/src/main/java/net/vonforst/evmap/storage/FilterValueDao.kt index e8d6424e..fd1ebd4f 100644 --- a/app/src/main/java/net/vonforst/evmap/storage/FilterValueDao.kt +++ b/app/src/main/java/net/vonforst/evmap/storage/FilterValueDao.kt @@ -54,7 +54,7 @@ abstract class FilterValueDao { ) open fun getFilterValues(filterStatus: Long, dataSource: String): LiveData> = - if (filterStatus == FILTERS_DISABLED) { + if (filterStatus == FILTERS_DISABLED || filterStatus == FILTERS_FAVORITES) { MutableLiveData(emptyList()) } else { MediatorLiveData>().apply { diff --git a/app/src/main/java/net/vonforst/evmap/viewmodel/FilterViewModel.kt b/app/src/main/java/net/vonforst/evmap/viewmodel/FilterViewModel.kt index 3976eddf..e707888d 100644 --- a/app/src/main/java/net/vonforst/evmap/viewmodel/FilterViewModel.kt +++ b/app/src/main/java/net/vonforst/evmap/viewmodel/FilterViewModel.kt @@ -42,7 +42,7 @@ class FilterViewModel(application: Application) : AndroidViewModel(application) MediatorLiveData().apply { addSource(filterStatus) { id -> when (id) { - FILTERS_CUSTOM, FILTERS_DISABLED -> value = null + FILTERS_CUSTOM, FILTERS_DISABLED, FILTERS_FAVORITES -> value = null else -> viewModelScope.launch { value = db.filterProfileDao().getProfileById(id, prefs.dataSource) } diff --git a/app/src/main/java/net/vonforst/evmap/viewmodel/MapViewModel.kt b/app/src/main/java/net/vonforst/evmap/viewmodel/MapViewModel.kt index 27bf5daf..4db9e3a1 100644 --- a/app/src/main/java/net/vonforst/evmap/viewmodel/MapViewModel.kt +++ b/app/src/main/java/net/vonforst/evmap/viewmodel/MapViewModel.kt @@ -23,6 +23,7 @@ import net.vonforst.evmap.model.* import net.vonforst.evmap.storage.AppDatabase import net.vonforst.evmap.storage.FilterProfile import net.vonforst.evmap.storage.PreferenceDataSource +import net.vonforst.evmap.ui.cluster import net.vonforst.evmap.utils.distanceBetween import java.io.IOException @@ -304,6 +305,25 @@ class MapViewModel(application: Application) : AndroidViewModel(application) { val filters = data.second val api = api val refData = data.third + + if (filterStatus.value == FILTERS_FAVORITES) { + // load favorites from local DB + val b = mapPosition.bounds + var chargers = db.chargeLocationsDao().getChargeLocationsInBoundsAsync( + b.southwest.latitude, + b.northeast.latitude, + b.southwest.longitude, + b.northeast.longitude + ) as List + + val clusterDistance = getClusterDistance(mapPosition.zoom) + clusterDistance?.let { + chargers = cluster(chargers, mapPosition.zoom, clusterDistance) + } + chargepoints.value = Resource.success(chargers) + return@throttleLatest + } + var result = api.getChargepoints(refData, mapPosition.bounds, mapPosition.zoom, filters) if (result.status == Status.ERROR && result.data == null) { // keep old results if new data could not be loaded diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d1648108..73affd98 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -143,6 +143,7 @@ Als Profil speichern Keine Filter Verändertes Filterprofil + Favoriten Reihenfolge ändern Löschen Als Profil speichern diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 08b75d89..ce8fca2a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -142,6 +142,7 @@ Save as profile No filters Modified filter + Favorites reorder Delete Save as profile