Skip to content

Commit

Permalink
implement chargeprice feedback dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
johan12345 committed Sep 4, 2022
1 parent a6117d3 commit a22b347
Show file tree
Hide file tree
Showing 11 changed files with 563 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package net.vonforst.evmap.fragment

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.navigation.ui.setupWithNavController
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
import net.vonforst.evmap.databinding.FragmentChargepriceFeedbackBinding
import net.vonforst.evmap.viewmodel.ChargepriceFeedbackViewModel
import net.vonforst.evmap.viewmodel.viewModelFactory

class ChargepriceFeedbackFragment : Fragment() {

private lateinit var binding: FragmentChargepriceFeedbackBinding
private val vm: ChargepriceFeedbackViewModel by viewModels(factoryProducer = {
viewModelFactory {
ChargepriceFeedbackViewModel(
requireActivity().application,
getString(R.string.chargeprice_key),
getString(R.string.chargeprice_api_url)
)
}
})

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val fragmentArgs: ChargepriceFeedbackFragmentArgs by navArgs()
vm.feedbackType.value = fragmentArgs.feedbackType
vm.charger.value = fragmentArgs.charger
vm.vehicle.value = fragmentArgs.vehicle
vm.chargePrices.value = fragmentArgs.chargePrices?.toList()
vm.batteryRange.value = fragmentArgs.batteryRange?.toList()
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_chargeprice_feedback, container, false
)
binding.lifecycleOwner = this
binding.vm = vm
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

binding.toolbar.setupWithNavController(
findNavController(),
(requireActivity() as MapsActivity).appBarConfiguration
)
binding.tariffSpinner.setAdapter(
ArrayAdapter<String>(
requireContext(),
R.layout.item_simple_multiline,
R.id.text,
mutableListOf()
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import net.vonforst.evmap.api.equivalentPlugTypes
import net.vonforst.evmap.databinding.FragmentChargepriceBinding
import net.vonforst.evmap.model.Chargepoint
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.viewmodel.ChargepriceFeedbackType
import net.vonforst.evmap.viewmodel.ChargepriceViewModel
import net.vonforst.evmap.viewmodel.Status
import net.vonforst.evmap.viewmodel.savedStateViewModelFactory
Expand Down Expand Up @@ -182,6 +183,9 @@ class ChargepriceFragment : Fragment() {
binding.btnSettings.setOnClickListener {
findNavController().navigate(R.id.action_chargeprice_to_chargepriceSettingsFragment)
}
binding.btnFeedbackMissingPrice.setOnClickListener {
feedbackMissingPrice()
}

binding.batteryRange.setLabelFormatter { value: Float ->
val fmt = NumberFormat.getNumberInstance()
Expand All @@ -202,6 +206,14 @@ class ChargepriceFragment : Fragment() {
(activity as? MapsActivity)?.openUrl(getString(R.string.chargeprice_faq_link))
true
}
R.id.menu_feedback_missing_price -> {
feedbackMissingPrice()
true
}
R.id.menu_feedback_wrong_price -> {
feedbackWrongPrice()
true
}
else -> false
}
}
Expand Down Expand Up @@ -235,4 +247,30 @@ class ChargepriceFragment : Fragment() {
view.setBackgroundColor(MaterialColors.getColor(view, android.R.attr.windowBackground))
}

private fun feedbackMissingPrice() {
findNavController().navigate(
R.id.action_chargeprice_to_chargepriceFeedbackFragment,
ChargepriceFeedbackFragmentArgs(
ChargepriceFeedbackType.MISSING_PRICE,
vm.charger.value,
vm.vehicle.value,
vm.chargePricesForChargepoint.value?.data?.toTypedArray(),
vm.batteryRange.value?.toFloatArray()
).toBundle()
)
}

private fun feedbackWrongPrice() {
findNavController().navigate(
R.id.action_chargeprice_to_chargepriceFeedbackFragment,
ChargepriceFeedbackFragmentArgs(
ChargepriceFeedbackType.WRONG_PRICE,
vm.charger.value,
vm.vehicle.value,
vm.chargePricesForChargepoint.value?.data?.toTypedArray(),
vm.batteryRange.value?.toFloatArray()
).toBundle()
)
}

}
17 changes: 14 additions & 3 deletions app/src/main/java/net/vonforst/evmap/ui/BindingAdapters.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import android.graphics.drawable.LayerDrawable
import android.text.SpannableString
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
Expand Down Expand Up @@ -128,9 +130,18 @@ fun <T> setRecyclerViewData(recyclerView: RecyclerView, items: List<T>?) {
}

@BindingAdapter("data")
fun <T> setRecyclerViewData(recyclerView: ViewPager2, items: List<T>?) {
if (recyclerView.adapter is ListAdapter<*, *>) {
(recyclerView.adapter as ListAdapter<T, *>).submitList(items)
fun <T> setViewPager2Data(viewPager: ViewPager2, items: List<T>?) {
if (viewPager.adapter is ListAdapter<*, *>) {
(viewPager.adapter as ListAdapter<T, *>).submitList(items)
}
}

@BindingAdapter("data")
fun <T> setAutoCompleteTextViewData(atv: AutoCompleteTextView, items: List<T>?) {
if (atv.adapter is ArrayAdapter<*>) {
val arrayAdapter = atv.adapter as ArrayAdapter<T>
arrayAdapter.clear()
items?.let { arrayAdapter.addAll(it) }
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package net.vonforst.evmap.viewmodel

import android.app.Application
import androidx.lifecycle.*
import kotlinx.coroutines.launch
import net.vonforst.evmap.R
import net.vonforst.evmap.api.chargeprice.*
import net.vonforst.evmap.model.ChargeLocation
import net.vonforst.evmap.model.Chargepoint
import net.vonforst.evmap.storage.PreferenceDataSource
import net.vonforst.evmap.ui.currency
import java.io.IOException

enum class ChargepriceFeedbackType {
MISSING_PRICE, WRONG_PRICE, MISSING_VEHICLE
}

class ChargepriceFeedbackViewModel(
application: Application,
chargepriceApiKey: String,
chargepriceApiUrl: String
) :
AndroidViewModel(application) {
private var api = ChargepriceApi.create(chargepriceApiKey, chargepriceApiUrl)
private var prefs = PreferenceDataSource(application)

// data supplied through fragment args
val feedbackType = MutableLiveData<ChargepriceFeedbackType>()
val charger = MutableLiveData<ChargeLocation>()
val chargepoint = MutableLiveData<Chargepoint>()
val vehicle = MutableLiveData<ChargepriceCar>()
val chargePrices = MutableLiveData<List<ChargePrice>>()
val batteryRange = MutableLiveData<List<Float>>()

// data input by user
val tariff = MutableLiveData<String>()
val price = MutableLiveData<String>()
val notes = MutableLiveData<String>()
val email = MutableLiveData<String>()

val loading = MutableLiveData<Boolean>().apply { value = false }

val chargePricesStrings = chargePrices.map {
it.map {
val name = if (!it.tariffName.lowercase().startsWith(it.provider.lowercase())) {
"${it.provider} ${it.tariffName}"
} else it.tariffName
val price = application.getString(
R.string.charge_price_format,
it.chargepointPrices[0].price,
currency(it.currency)
)
"$name: $price"
}.toList()
}

private val feedback = MediatorLiveData<ChargepriceUserFeedback>().apply {
listOf(
feedbackType,
charger,
chargepoint,
vehicle,
chargePrices,
tariff,
price,
notes,
email
).forEach {
addSource(it) {
try {
value = when (feedbackType.value!!) {
ChargepriceFeedbackType.MISSING_PRICE -> {
ChargepriceMissingPriceFeedback(
tariff.value ?: "",
charger.value?.network?.take(200) ?: "",
price.value ?: "",
charger.value?.let { ChargepriceApi.getPoiUrl(it) } ?: "",
notes.value ?: "",
email.value ?: "",
getChargepriceContext(),
ChargepriceApi.getChargepriceLanguage()
)
}
ChargepriceFeedbackType.WRONG_PRICE -> {
ChargepriceWrongPriceFeedback(
"", // TODO: dropdown value
charger.value?.network?.take(200) ?: "",
"", // TODO: dropdown value
price.value ?: "",
charger.value?.let { ChargepriceApi.getPoiUrl(it) } ?: "",
notes.value ?: "",
email.value ?: "",
getChargepriceContext(),
ChargepriceApi.getChargepriceLanguage()
)
}
ChargepriceFeedbackType.MISSING_VEHICLE -> {
TODO()
}
}
} catch (e: IllegalArgumentException) {
value = null
}
}
}
}

val formValid = feedback.map { it != null }

fun sendFeedback() {
val feedback = feedback.value ?: return
viewModelScope.launch {
loading.value = true
try {
api.userFeedback(feedback)
} catch (e: IOException) {

}
loading.value = false
}
}

private fun getChargepriceContext(): String {
val result = StringBuilder()
vehicle.value?.let { result.append("Vehicle: ${it.brand} ${it.name}\n") }
batteryRange.value?.let { result.append("Battery SOC: ${it[0]} to ${it[1]}\n") }
return result.toString()
}
}
22 changes: 19 additions & 3 deletions app/src/main/res/layout/fragment_chargeprice.xml
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/battery_range"
tools:itemCount="3"
tools:listitem="@layout/item_chargeprice" />
tools:listitem="@layout/item_chargeprice"
tools:visibility="invisible" />

<TextView
android:id="@+id/textView8"
Expand All @@ -179,10 +180,11 @@
android:gravity="center_horizontal"
android:text="@string/chargeprice_no_tariffs_found"
app:goneUnless="@{vm.chargePricesForChargepoint.status == Status.SUCCESS &amp;&amp; vm.chargePricesForChargepoint.data.size() == 0}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintBottom_toTopOf="@+id/btnFeedbackMissingPrice"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/charge_prices_list"
app:layout_constraintTop_toTopOf="@+id/charge_prices_list" />
app:layout_constraintTop_toTopOf="@+id/charge_prices_list"
app:layout_constraintVertical_chainStyle="packed" />

<TextView
android:id="@+id/textView9"
Expand Down Expand Up @@ -237,6 +239,20 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView3" />

<Button
android:id="@+id/btnFeedbackMissingPrice"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="@string/chargeprice_feedback_missing_price"
app:goneUnless="@{vm.chargePricesForChargepoint.status == Status.SUCCESS &amp;&amp; vm.chargePricesForChargepoint.data.size() == 0}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView8" />

</androidx.constraintlayout.widget.ConstraintLayout>

</androidx.core.widget.NestedScrollView>
Expand Down
Loading

0 comments on commit a22b347

Please sign in to comment.