Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement Chargeprice feedback dialog #196

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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