mirror of
https://github.com/ev-map/EVMap.git
synced 2026-04-26 17:19:09 -04:00
add FilterFragment
This commit is contained in:
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
109
app/src/main/java/net/vonforst/evmap/fragment/FilterFragment.kt
Normal file
109
app/src/main/java/net/vonforst/evmap/fragment/FilterFragment.kt
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
54
app/src/main/java/net/vonforst/evmap/ui/AnimationUtils.kt
Normal file
54
app/src/main/java/net/vonforst/evmap/ui/AnimationUtils.kt
Normal 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
|
||||
}
|
||||
@@ -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()
|
||||
38
app/src/main/res/layout/fragment_filter.xml
Normal file
38
app/src/main/res/layout/fragment_filter.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="net.vonforst.evmap.viewmodel.FilterViewModel" />
|
||||
|
||||
<variable
|
||||
name="vm"
|
||||
type="FilterViewModel" />
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/toolbar_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:actionBarSize" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/filters_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:data="@{vm.filters}" />
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
47
app/src/main/res/layout/item_filter_boolean.xml
Normal file
47
app/src/main/res/layout/item_filter_boolean.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="net.vonforst.evmap.viewmodel.BooleanFilter" />
|
||||
|
||||
<variable
|
||||
name="item"
|
||||
type="BooleanFilter" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView17"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@{item.name}"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/switch1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Free charging" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/switch1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
@@ -17,6 +17,13 @@
|
||||
app:exitAnim="@anim/fragment_fade_exit"
|
||||
app:popEnterAnim="@anim/fragment_fade_enter"
|
||||
app:popExitAnim="@anim/fragment_fade_exit" />
|
||||
<action
|
||||
android:id="@+id/action_map_to_filterFragment"
|
||||
app:destination="@id/filter"
|
||||
app:exitAnim="@anim/fragment_fade_exit"
|
||||
app:enterAnim="@anim/fragment_fade_enter"
|
||||
app:popEnterAnim="@anim/fragment_fade_enter"
|
||||
app:popExitAnim="@anim/fragment_fade_exit" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/about"
|
||||
@@ -38,4 +45,9 @@
|
||||
android:name="net.vonforst.evmap.fragment.FavoritesFragment"
|
||||
android:label="@string/menu_favs"
|
||||
tools:layout="@layout/fragment_favorites" />
|
||||
<dialog
|
||||
android:id="@+id/filter"
|
||||
android:name="net.vonforst.evmap.fragment.FilterFragment"
|
||||
android:label="@string/menu_filter"
|
||||
tools:layout="@layout/fragment_filter" />
|
||||
</navigation>
|
||||
@@ -44,4 +44,5 @@
|
||||
<string name="pref_navigate_use_maps_off">Navigationsbutton startet Karten-App mit Position der Ladesäule</string>
|
||||
<string name="coordinates">Koordinaten</string>
|
||||
<string name="share">Teilen</string>
|
||||
<string name="filter_free">Nur kostenlose Ladesäulen</string>
|
||||
</resources>
|
||||
@@ -43,4 +43,5 @@
|
||||
<string name="pref_navigate_use_maps_off">Navigation button launches maps app with charger location</string>
|
||||
<string name="coordinates">Coordinates</string>
|
||||
<string name="share">Share</string>
|
||||
<string name="filter_free">Only free chargers</string>
|
||||
</resources>
|
||||
|
||||
@@ -17,4 +17,9 @@
|
||||
|
||||
<style name="AppTheme" parent="AppTheme.Base" />
|
||||
|
||||
<style name="FullScreenDialogStyle" parent="AppTheme">
|
||||
<item name="android:windowFullscreen">false</item>
|
||||
<item name="android:windowIsFloating">false</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user