diff --git a/app/build.gradle b/app/build.gradle index 380bbadd..d5f71e01 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -82,7 +82,7 @@ dependencies { implementation "com.mikepenz:aboutlibraries:$about_libs_version" // navigation library - def nav_version = "2.3.0-alpha04" + def nav_version = "2.3.0-alpha05" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" diff --git a/app/src/main/java/net/vonforst/evmap/adapter/DataBindingAdapters.kt b/app/src/main/java/net/vonforst/evmap/adapter/DataBindingAdapters.kt index dc2cf6c0..a0ba5ec7 100644 --- a/app/src/main/java/net/vonforst/evmap/adapter/DataBindingAdapters.kt +++ b/app/src/main/java/net/vonforst/evmap/adapter/DataBindingAdapters.kt @@ -13,7 +13,10 @@ 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.BooleanFilter import net.vonforst.evmap.viewmodel.FavoritesViewModel +import net.vonforst.evmap.viewmodel.Filter +import net.vonforst.evmap.viewmodel.MultipleChoiceFilter interface Equatable { override fun equals(other: Any?): Boolean; @@ -132,4 +135,29 @@ class FavoritesAdapter(val vm: FavoritesViewModel) : override fun getItemViewType(position: Int): Int = R.layout.item_favorite override fun getItemId(position: Int): Long = getItem(position).charger.id +} + +class FiltersAdapter : DataBindingAdapter() { + init { + setHasStableIds(true) + } + + val itemids = mutableMapOf() + var maxId = 0L + + override fun getItemViewType(position: Int): Int = when (getItem(position)) { + is BooleanFilter -> R.layout.item_filter_boolean + is MultipleChoiceFilter -> R.layout.item_filter_boolean + } + + override fun getItemId(position: Int): Long { + val key = getItem(position).key + var value = itemids[key] + if (value == null) { + maxId++ + value = maxId + itemids[key] = maxId + } + return value + } } \ No newline at end of file diff --git a/app/src/main/java/net/vonforst/evmap/fragment/FavoritesFragment.kt b/app/src/main/java/net/vonforst/evmap/fragment/FavoritesFragment.kt index 5df7777b..43631a30 100644 --- a/app/src/main/java/net/vonforst/evmap/fragment/FavoritesFragment.kt +++ b/app/src/main/java/net/vonforst/evmap/fragment/FavoritesFragment.kt @@ -12,7 +12,6 @@ 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 @@ -78,10 +77,6 @@ class FavoritesFragment : Fragment() { ) } - vm.favorites.observe(viewLifecycleOwner, Observer { - print(it.toString()) - }) - if (ContextCompat.checkSelfPermission( requireContext(), Manifest.permission.ACCESS_FINE_LOCATION diff --git a/app/src/main/java/net/vonforst/evmap/fragment/FilterFragment.kt b/app/src/main/java/net/vonforst/evmap/fragment/FilterFragment.kt new file mode 100644 index 00000000..71112928 --- /dev/null +++ b/app/src/main/java/net/vonforst/evmap/fragment/FilterFragment.kt @@ -0,0 +1,109 @@ +package net.vonforst.evmap.fragment + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.Toolbar +import androidx.databinding.DataBindingUtil +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import androidx.navigation.ui.setupWithNavController +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import net.vonforst.evmap.MapsActivity +import net.vonforst.evmap.R +import net.vonforst.evmap.adapter.FiltersAdapter +import net.vonforst.evmap.databinding.FragmentFilterBinding +import net.vonforst.evmap.ui.exitCircularReveal +import net.vonforst.evmap.ui.startCircularReveal +import net.vonforst.evmap.viewmodel.FilterViewModel +import net.vonforst.evmap.viewmodel.viewModelFactory + +class FilterFragment : DialogFragment(), MapsActivity.FragmentCallback { + private lateinit var binding: FragmentFilterBinding + private val vm: FilterViewModel by viewModels(factoryProducer = { + viewModelFactory { + FilterViewModel( + requireActivity().application, + getString(R.string.goingelectric_key) + ) + } + }) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle( + STYLE_NORMAL, + R.style.FullScreenDialogStyle + ); + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DataBindingUtil.inflate(inflater, R.layout.fragment_filter, container, false) + binding.lifecycleOwner = this + binding.vm = vm + + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val toolbar = view.findViewById(R.id.toolbar) as Toolbar + + val navController = findNavController() + toolbar.setupWithNavController( + navController, + (requireActivity() as MapsActivity).appBarConfiguration + ) + + binding.filtersList.apply { + adapter = FiltersAdapter() + layoutManager = + LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + addItemDecoration( + DividerItemDecoration( + context, LinearLayoutManager.VERTICAL + ) + ) + } + + view.startCircularReveal() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + android.R.id.home -> { + exitAfterTransition() + false + } + else -> super.onOptionsItemSelected(item) + } + } + + private fun exitAfterTransition() { + view?.exitCircularReveal { + findNavController().popBackStack() + } + } + + override fun getRootView(): View { + return binding.root + } + + override fun goBack(): Boolean { + exitAfterTransition() + return true + } + + override fun onResume() { + super.onResume() + val hostActivity = activity as? MapsActivity ?: return + hostActivity.fragmentCallback = this + } +} \ No newline at end of file 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 93fd2377..f6639fd5 100644 --- a/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt +++ b/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt @@ -36,7 +36,6 @@ import com.google.android.gms.maps.model.* import com.google.android.libraries.places.api.model.Place import com.google.android.libraries.places.widget.Autocomplete import com.google.android.libraries.places.widget.model.AutocompleteActivityMode -import com.google.android.material.snackbar.Snackbar import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike import com.mahc.custombottomsheetbehavior.MergedAppBarLayoutBehavior import kotlinx.android.synthetic.main.fragment_map.* @@ -462,12 +461,14 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac } override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { + return when (item.itemId) { R.id.menu_filter -> { - Snackbar.make(root, R.string.not_implemented, Snackbar.LENGTH_SHORT).show() - return true + requireView().findNavController().navigate( + R.id.action_map_to_filterFragment + ) + true } - else -> return super.onOptionsItemSelected(item) + else -> super.onOptionsItemSelected(item) } } diff --git a/app/src/main/java/net/vonforst/evmap/ui/AnimationUtils.kt b/app/src/main/java/net/vonforst/evmap/ui/AnimationUtils.kt new file mode 100644 index 00000000..65fc156e --- /dev/null +++ b/app/src/main/java/net/vonforst/evmap/ui/AnimationUtils.kt @@ -0,0 +1,54 @@ +package net.vonforst.evmap.ui + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.view.View +import android.view.ViewAnimationUtils +import android.view.animation.DecelerateInterpolator +import kotlin.math.hypot + +fun View.startCircularReveal() { + addOnLayoutChangeListener(object : View.OnLayoutChangeListener { + override fun onLayoutChange( + v: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, + oldRight: Int, oldBottom: Int + ) { + v.removeOnLayoutChangeListener(this) + val cx = v.right + val cy = v.top + val radius = hypot(right.toDouble(), bottom.toDouble()).toInt() + ViewAnimationUtils.createCircularReveal(v, cx, cy, 0f, radius.toFloat()).apply { + interpolator = DecelerateInterpolator(2f) + duration = 1000 + start() + } + } + }) +} + +fun View.exitCircularReveal(block: () -> Unit) { + val startRadius = hypot(this.width.toDouble(), this.height.toDouble()) + ViewAnimationUtils.createCircularReveal(this, this.width, 0, startRadius.toFloat(), 0f).apply { + duration = 350 + interpolator = DecelerateInterpolator(1f) + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + block() + super.onAnimationEnd(animation) + } + }) + start() + } +} + +/** + * @return the position of the current [View]'s center in the screen + */ +fun View.findLocationOfCenterOnTheScreen(): IntArray { + val positions = intArrayOf(0, 0) + getLocationInWindow(positions) + // Get the center of the view + positions[0] = positions[0] + width / 2 + positions[1] = positions[1] + height / 2 + return positions +} \ No newline at end of file diff --git a/app/src/main/java/net/vonforst/evmap/viewmodel/FilterViewModel.kt b/app/src/main/java/net/vonforst/evmap/viewmodel/FilterViewModel.kt new file mode 100644 index 00000000..8851a34d --- /dev/null +++ b/app/src/main/java/net/vonforst/evmap/viewmodel/FilterViewModel.kt @@ -0,0 +1,36 @@ +package net.vonforst.evmap.viewmodel + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import net.vonforst.evmap.R +import net.vonforst.evmap.adapter.Equatable +import net.vonforst.evmap.api.goingelectric.GoingElectricApi +import net.vonforst.evmap.storage.AppDatabase + +class FilterViewModel(application: Application, geApiKey: String) : + AndroidViewModel(application) { + private var api = GoingElectricApi.create(geApiKey) + private var db = AppDatabase.getInstance(application) + + val filters: MutableLiveData> by lazy { + MutableLiveData>().apply { + value = listOf( + BooleanFilter(application.getString(R.string.filter_free), "freecharging") + ) + } + } +} + +sealed class Filter : Equatable { + abstract val name: String + abstract val key: String +} + +data class BooleanFilter(override val name: String, override val key: String) : Filter() + +data class MultipleChoiceFilter( + override val name: String, + override val key: String, + val choices: Map +) : Filter() \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_filter.xml b/app/src/main/res/layout/fragment_filter.xml new file mode 100644 index 00000000..86c23be8 --- /dev/null +++ b/app/src/main/res/layout/fragment_filter.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_filter_boolean.xml b/app/src/main/res/layout/item_filter_boolean.xml new file mode 100644 index 00000000..b4b02b47 --- /dev/null +++ b/app/src/main/res/layout/item_filter_boolean.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 40038b3b..1dc68688 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -17,6 +17,13 @@ app:exitAnim="@anim/fragment_fade_exit" app:popEnterAnim="@anim/fragment_fade_enter" app:popExitAnim="@anim/fragment_fade_exit" /> + + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index ef26ae28..bc6ee6d0 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -44,4 +44,5 @@ Navigationsbutton startet Karten-App mit Position der Ladesäule Koordinaten Teilen + Nur kostenlose Ladesäulen \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b2980f35..79c76583 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -43,4 +43,5 @@ Navigation button launches maps app with charger location Coordinates Share + Only free chargers diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 7346b251..d6f57870 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -17,4 +17,9 @@ +