add FilterFragment

This commit is contained in:
Johan von Forstner
2020-04-25 19:43:48 +02:00
parent 78421ec79f
commit e5dd0e19ab
13 changed files with 338 additions and 11 deletions

View File

@@ -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<Filter>() {
init {
setHasStableIds(true)
}
val itemids = mutableMapOf<String, Long>()
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
}
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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<List<Filter>> by lazy {
MutableLiveData<List<Filter>>().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<String, String>
) : Filter()