rework from DialogFragment into included view

This commit is contained in:
johan12345
2024-01-21 17:45:24 +01:00
parent c6395feaa3
commit 0e278bfedb
7 changed files with 127 additions and 91 deletions

View File

@@ -1,6 +1,7 @@
package net.vonforst.evmap.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding

View File

@@ -1,10 +1,8 @@
package net.vonforst.evmap.fragment
import android.os.Bundle
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.BundleCompat
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
@@ -13,97 +11,53 @@ import net.vonforst.evmap.adapter.ConnectorAdapter
import net.vonforst.evmap.adapter.ConnectorDetailsAdapter
import net.vonforst.evmap.adapter.SingleViewAdapter
import net.vonforst.evmap.api.availability.ChargeLocationStatus
import net.vonforst.evmap.api.availability.ChargepointStatus
import net.vonforst.evmap.databinding.DialogConnectorDetailsBinding
import net.vonforst.evmap.databinding.DialogConnectorDetailsHeaderBinding
import net.vonforst.evmap.databinding.DialogDataSourceSelectBinding
import net.vonforst.evmap.databinding.FragmentChargepriceHeaderBinding
import net.vonforst.evmap.model.Chargepoint
import net.vonforst.evmap.model.FILTERS_DISABLED
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.ui.MaterialDialogFragment
import java.time.Instant
class ConnectorDetailsDialog : MaterialDialogFragment() {
private lateinit var binding: DialogConnectorDetailsBinding
companion object {
fun getInstance(
chargepoint: Chargepoint,
status: List<ChargepointStatus>?,
evseIds: List<String>? = null,
labels: List<String?>? = null,
lastChange: List<Instant?>? = null
): ConnectorDetailsDialog {
val dialog = ConnectorDetailsDialog()
dialog.arguments = Bundle().apply {
putParcelable("chargepoint", chargepoint)
putParcelableArrayList("status", status?.let { ArrayList(status) })
putStringArrayList("evseIds", evseIds?.let { ArrayList(it) })
putStringArrayList("labels", labels?.let { ArrayList(it) })
putSerializable("lastChange", lastChange?.let { ArrayList(it) })
}
return dialog
}
}
override fun createView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = DialogConnectorDetailsBinding.inflate(inflater, container, false)
prefs = PreferenceDataSource(requireContext())
return binding.root
}
private lateinit var prefs: PreferenceDataSource
override fun initView(view: View, savedInstanceState: Bundle?) {
val args = requireArguments()
val chargepoint = BundleCompat.getParcelable(args, "chargepoint", Chargepoint::class.java)!!
val status =
BundleCompat.getParcelableArrayList(args, "status", ChargepointStatus::class.java)
val evseIds = args.getStringArrayList("evseIds")
val labels = args.getStringArrayList("labels")
val lastChange = args.getSerializable("lastChange") as ArrayList<Instant>?
val items = if (status != null) {
List(chargepoint.count) { i ->
ConnectorDetailsAdapter.ConnectorDetails(
status.get(i),
evseIds?.get(i),
labels?.get(i),
lastChange?.get(i)
)
}.sortedBy { it.evseId ?: it.label }
} else emptyList()
class ConnectorDetailsDialog(
val binding: DialogConnectorDetailsBinding,
context: Context,
onClose: () -> Unit
) {
private val headerBinding: DialogConnectorDetailsHeaderBinding
private val detailsAdapter = ConnectorDetailsAdapter()
init {
binding.list.apply {
itemAnimator = null
layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
}
val headerBinding = DataBindingUtil.inflate<DialogConnectorDetailsHeaderBinding>(
headerBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.dialog_connector_details_header, binding.list, false
)
if (items.isEmpty()) headerBinding.divider.visibility = View.GONE
val joinedAdapter = ConcatAdapter(
binding.list.adapter = ConcatAdapter(
SingleViewAdapter(headerBinding.root),
ConnectorDetailsAdapter().apply {
submitList(items)
}
detailsAdapter
)
binding.list.adapter = joinedAdapter
headerBinding.item = ConnectorAdapter.ChargepointWithAvailability(chargepoint, status)
binding.btnClose.setOnClickListener {
dismiss()
onClose()
}
}
fun setData(cp: Chargepoint, status: ChargeLocationStatus?) {
val cpStatus = status?.status?.get(cp)
val items = if (status != null) {
List(cp.count) { i ->
ConnectorDetailsAdapter.ConnectorDetails(
cpStatus?.get(i),
status.evseIds?.get(cp)?.get(i),
status.labels?.get(cp)?.get(i),
status.lastChange?.get(cp)?.get(i)
)
}.sortedBy { it.evseId ?: it.label }
} else emptyList()
detailsAdapter.submitList(items)
headerBinding.divider.visibility = if (items.isEmpty()) View.GONE else View.VISIBLE
headerBinding.item = ConnectorAdapter.ChargepointWithAvailability(cp, cpStatus)
}
}

View File

@@ -53,6 +53,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.transition.MaterialArcMotion
import com.google.android.material.transition.MaterialContainerTransform
import com.google.android.material.transition.MaterialContainerTransform.FADE_MODE_CROSS
import com.google.android.material.transition.MaterialFadeThrough
import com.google.android.material.transition.MaterialSharedAxis
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike
@@ -107,6 +108,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
private var requestingLocationUpdates = false
private lateinit var bottomSheetBehavior: BottomSheetBehaviorGoogleMapsLike<View>
private lateinit var detailAppBarBehavior: MergedAppBarLayoutBehavior
private lateinit var detailsDialog: ConnectorDetailsDialog
private lateinit var prefs: PreferenceDataSource
private var markers: MutableBiMap<Marker, ChargeLocation> = HashBiMap()
private var clusterMarkers: List<Marker> = emptyList()
@@ -128,6 +130,12 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
return
}
if (vm.selectedChargepoint.value != null) {
closeConnectorDetailsDialog()
vm.selectedChargepoint.value = null
return
}
if (binding.search.hasFocus()) {
removeSearchFocus()
}
@@ -269,6 +277,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
vm.bottomSheetState.value?.let { bottomSheetBehavior.state = it }
}
bottomSheetBehavior.isCollapsible = bottomSheetCollapsible
binding.detailView.connectorDetails
setupObservers()
setupClickListeners()
@@ -322,6 +331,12 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
binding.appLogo.root.visibility = View.GONE
binding.search.visibility = View.VISIBLE
}
detailsDialog =
ConnectorDetailsDialog(binding.detailView.connectorDetails, requireContext()) {
closeConnectorDetailsDialog()
vm.selectedChargepoint.value = null
}
}
override fun onResume() {
@@ -670,6 +685,14 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
vm.mapTrafficEnabled.observe(viewLifecycleOwner) {
map?.setTrafficEnabled(it)
}
vm.selectedChargepoint.observe(viewLifecycleOwner) {
binding.detailView.connectorDetailsCard.visibility =
if (it != null) View.VISIBLE else View.INVISIBLE
if (it != null) {
detailsDialog.setData(it, vm.availability.value?.data)
}
updateBackPressedCallback()
}
updateBackPressedCallback()
}
@@ -714,6 +737,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
|| vm.searchResult.value != null
|| (vm.layersMenuOpen.value ?: false)
|| binding.search.hasFocus()
|| vm.selectedChargepoint.value != null
}
private fun unhighlightAllMarkers() {
@@ -815,15 +839,9 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
binding.detailView.connectors.apply {
adapter = ConnectorAdapter().apply {
onClickListener = { item ->
val dialog = ConnectorDetailsDialog.getInstance(
item.chargepoint,
item.status,
vm.availability.value?.data?.evseIds?.get(item.chargepoint),
vm.availability.value?.data?.labels?.get(item.chargepoint),
vm.availability.value?.data?.lastChange?.get(item.chargepoint),
)
dialog.show(parentFragmentManager, null)
onClickListener = {
vm.selectedChargepoint.value = it.chargepoint
openConnectorDetailsDialog()
}
}
itemAnimator = null
@@ -910,6 +928,44 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
}
}
private fun openConnectorDetailsDialog() {
val chargepoints = vm.chargerDetails.value?.data?.chargepointsMerged ?: return
val chargepoint = vm.selectedChargepoint.value ?: return
val index = chargepoints.indexOf(chargepoint).takeIf { it >= 0 } ?: return
val vh = binding.detailView.connectors.findViewHolderForAdapterPosition(index) ?: return
val materialTransform = MaterialContainerTransform().apply {
startView = vh.itemView
endView = binding.detailView.connectorDetailsCard
setPathMotion(MaterialArcMotion())
duration = 250
scrimColor = Color.TRANSPARENT
addTarget(binding.detailView.connectorDetailsCard)
isElevationShadowEnabled = false
fadeMode = FADE_MODE_CROSS
}
TransitionManager.beginDelayedTransition(binding.root, materialTransform)
}
private fun closeConnectorDetailsDialog() {
val chargepoints = vm.chargerDetails.value?.data?.chargepointsMerged ?: return
val chargepoint = vm.selectedChargepoint.value ?: return
val index = chargepoints.indexOf(chargepoint).takeIf { it >= 0 } ?: return
val vh = binding.detailView.connectors.findViewHolderForAdapterPosition(index) ?: return
val materialTransform = MaterialContainerTransform().apply {
startView = binding.detailView.connectorDetailsCard
endView = vh.itemView
setPathMotion(MaterialArcMotion())
duration = 200
scrimColor = Color.TRANSPARENT
addTarget(vh.itemView)
isElevationShadowEnabled = false
fadeMode = FADE_MODE_CROSS
}
TransitionManager.beginDelayedTransition(binding.root, materialTransform)
}
private fun showPaymentMethodsDialog(charger: ChargeLocation) {
val activity = activity ?: return
val chargecardData = vm.chargeCardMap.value ?: return

View File

@@ -139,8 +139,8 @@ fun <T> setRecyclerViewData(recyclerView: ViewPager2, items: List<T>?) {
}
@BindingAdapter("connectorIcon")
fun getConnectorItem(view: ImageView, type: String) {
view.setImageResource(iconForPlugType(type))
fun getConnectorItem(view: ImageView, type: String?) {
view.setImageResource(type?.let { iconForPlugType(it) } ?: 0)
}
@BindingAdapter("srcCompat")

View File

@@ -16,7 +16,6 @@ import kotlinx.parcelize.Parcelize
import net.vonforst.evmap.api.availability.AvailabilityRepository
import net.vonforst.evmap.api.availability.ChargeLocationStatus
import net.vonforst.evmap.api.availability.tesla.Pricing
import net.vonforst.evmap.api.availability.tesla.TeslaChargingOwnershipGraphQlApi
import net.vonforst.evmap.api.createApi
import net.vonforst.evmap.api.fronyx.PredictionData
import net.vonforst.evmap.api.fronyx.PredictionRepository
@@ -150,7 +149,11 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
}
val chargerSparse: MutableLiveData<ChargeLocation?> by lazy {
state.getLiveData("chargerSparse")
state.getLiveData<ChargeLocation?>("chargerSparse").apply {
observeForever {
selectedChargepoint.value = null
}
}
}
private val triggerChargerDetailsRefresh = MutableLiveData(false)
val chargerDetails: LiveData<Resource<ChargeLocation>> = chargerSparse.switchMap { charger ->
@@ -167,6 +170,10 @@ class MapViewModel(application: Application, private val state: SavedStateHandle
}
}
val selectedChargepoint: MutableLiveData<Chargepoint?> by lazy {
state.getLiveData("selectedChargepoint")
}
val charger: MediatorLiveData<Resource<ChargeLocation>> by lazy {
MediatorLiveData<Resource<ChargeLocation>>().apply {
addSource(chargerDetails) {

View File

@@ -560,6 +560,24 @@
app:layout_constraintBottom_toBottomOf="@+id/textView13"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/textView13" />
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewElevatedStyle"
android:id="@+id/connector_details_card"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@id/connectors"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardCornerRadius="24dp"
android:layout_marginBottom="@dimen/detail_corner_radius_negative"
android:paddingBottom="@dimen/detail_corner_radius"
app:cardElevation="6dp"
android:visibility="gone">
<include
layout="@layout/dialog_connector_details"
android:id="@+id/connector_details" />
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -79,7 +79,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="36dp"
android:layout_marginTop="4dp"
android:text="@{UtilsKt.nameForPlugType(ChargepointApiKt.stringProvider(context), item.chargepoint.type) + &quot; · &quot; + item.chargepoint.formatPower()}"
android:text="@{item != null ? UtilsKt.nameForPlugType(ChargepointApiKt.stringProvider(context), item.chargepoint.type) + &quot; · &quot; + item.chargepoint.formatPower() : null}"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
app:goneUnless="@{item.chargepoint.hasKnownPower()}"
app:layout_constraintBottom_toTopOf="@id/textView8"