Viewmodel updated to use live data

Main activity dialogs changed
code clean up
This commit is contained in:
2020-05-16 21:33:39 +01:00
parent c580f35eda
commit f1a4f9cd52
8 changed files with 122 additions and 90 deletions

View File

@@ -34,9 +34,7 @@ android {
def ccApiKey = properties.getProperty("cc_api_key", "")
it.buildConfigField 'String', "CC_API_KEY", ccApiKey
it.resValue 'string', "api_key", ccApiKey
}
}

View File

@@ -5,7 +5,6 @@ import com.appttude.h_mal.easycc.mvvm.data.network.response.ResponseObject
/**
* Main entry point for accessing currency data.
*/
interface Repository {
suspend fun getData(fromCurrency: String, toCurrency: String): ResponseObject

View File

@@ -7,14 +7,12 @@ import android.text.Editable
import android.text.TextWatcher
import android.view.WindowManager
import android.widget.ArrayAdapter
import android.widget.TextView
import com.appttude.h_mal.easycc.R
import kotlinx.android.synthetic.main.custom_dialog.*
class CustomDialogClass(
context: Context,
val textView: TextView,
val viewModel: MainViewModel
private val clickListener: ClickListener
) : Dialog(context) {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -24,7 +22,11 @@ class CustomDialogClass(
window!!.setBackgroundDrawableResource(android.R.color.transparent)
window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
val arrayAdapter = ArrayAdapter.createFromResource(context, R.array.currency_arrays, android.R.layout.simple_list_item_1)
val arrayAdapter =
ArrayAdapter.createFromResource(
context, R.array.currency_arrays,
android.R.layout.simple_list_item_1)
list_view.adapter = arrayAdapter
search_text.addTextChangedListener(object : TextWatcher {
@@ -36,15 +38,12 @@ class CustomDialogClass(
})
list_view.setOnItemClickListener{ adapterView, _, i, _ ->
if (textView.tag == "top"){
viewModel.rateIdFrom = adapterView.getItemAtPosition(i).toString()
}else{
viewModel.rateIdTo = adapterView.getItemAtPosition(i).toString()
}
textView.text = adapterView.getItemAtPosition(i).toString()
viewModel.getExchangeRate()
clickListener.onText(adapterView.getItemAtPosition(i).toString())
dismiss()
}
}
}
interface ClickListener{
fun onText(currencyName: String)
}

View File

@@ -3,12 +3,12 @@ package com.appttude.h_mal.easycc.mvvm.ui.app
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.View
import android.view.WindowManager
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.appttude.h_mal.easycc.R
import com.appttude.h_mal.easycc.databinding.ActivityMainBinding
@@ -20,42 +20,56 @@ import org.kodein.di.KodeinAware
import org.kodein.di.android.kodein
import org.kodein.di.generic.instance
class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClickListener{
class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClickListener {
override val kodein by kodein()
private val factory : MainViewModelFactory by instance()
// Retrieve MainViewModelFactory via dependency injection
private val factory: MainViewModelFactory by instance()
companion object{
companion object {
lateinit var viewModel: MainViewModel
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Keyboard is not overlapping views
this.window.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN or
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
this.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN or WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
viewModel = ViewModelProviders.of(this, factory)
.get(MainViewModel::class.java)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
viewModel = ViewModelProviders.of(this, factory).get(MainViewModel::class.java)
binding.viewmodel = viewModel
binding.lifecycleOwner = this
viewModel.rateListener = this
intent.extras?.apply {
val itemOne = getString("parse_1")
val itemTwo = getString("parse_2")
if (!itemOne.isNullOrEmpty() && !itemTwo.isNullOrEmpty()){
viewModel.rateIdTo = itemOne
viewModel.rateIdFrom = itemTwo
viewModel.getExchangeRate()
}
// Bind viewmodel to layout with view binding
DataBindingUtil
.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
.apply {
viewmodel = viewModel
lifecycleOwner = this@MainActivity
}
viewModel.start()
viewModel.initiate(intent.extras)
setUpListeners()
setUpObservers()
}
private fun setUpObservers() {
viewModel.operationStartedListener.observe(this, Observer {
progressBar.hideView(false)
})
viewModel.operationFinishedListener.observe(this, Observer { pair ->
progressBar.hideView(true)
if (pair.first){
bottomInsertValues.clearEditText()
topInsertValue.clearEditText()
}else{
pair.second?.let { DisplayToast(it) }
}
})
}
private fun setUpListeners(){
topInsertValue.addTextChangedListener(textWatcherClass)
bottomInsertValues.addTextChangedListener(textWatcherClass2)
@@ -63,8 +77,14 @@ class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClic
currency_two.setOnClickListener(this)
}
private fun showCustomDialog(view: View?){
val dialogClass = CustomDialogClass(this, view as TextView, viewModel)
private fun showCustomDialog(view: View?) {
val dialogClass = CustomDialogClass(this, object : ClickListener {
override fun onText(currencyName: String) {
(view as TextView).text = currencyName
viewModel.setCurrencyName(view.tag, currencyName)
}
})
dialogClass.show()
}
@@ -90,17 +110,13 @@ class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClic
private val textWatcherClass: TextWatcher = object : TextWatcher {
override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) {
bottomInsertValues.removeTextChangedListener(textWatcherClass2)
if (topInsertValue.text.isNullOrEmpty()) {
if (topInsertValue.text.isNullOrEmpty())
bottomInsertValues.setText("")
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable) {
try {
viewModel.setBottomValue(s.toString(), bottomInsertValues)
} catch (e: NumberFormatException) {
Log.e(this.javaClass.simpleName, "no numbers inserted")
}
bottomInsertValues.setText(viewModel.getConversion(s.toString()))
bottomInsertValues.addTextChangedListener(textWatcherClass2)
}
}
@@ -108,17 +124,13 @@ class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClic
private val textWatcherClass2: TextWatcher = object : TextWatcher {
override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) {
topInsertValue.removeTextChangedListener(textWatcherClass)
if (bottomInsertValues.text.isNullOrEmpty()) {
if (bottomInsertValues.text.isNullOrEmpty())
topInsertValue.clearEditText()
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable) {
try {
viewModel.setTopValue(s.toString(), topInsertValue)
} catch (e: NumberFormatException) {
Log.e(this.javaClass.simpleName, "no numbers inserted")
}
topInsertValue.setText(viewModel.getReciprocalConversion(s.toString()))
topInsertValue.addTextChangedListener(textWatcherClass)
}
}

View File

@@ -1,79 +1,105 @@
package com.appttude.h_mal.easycc.mvvm.ui.app
import android.os.Bundle
import android.util.Log
import android.widget.EditText
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.appttude.h_mal.easycc.mvvm.data.repository.Repository
import com.appttude.h_mal.easycc.mvvm.utils.toTwoDp
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.IOException
import java.text.DecimalFormat
private const val TAG = "MainViewModel"
class MainViewModel(
private val repository: Repository
) : ViewModel(){
private val defaultValue by lazy { repository.getArrayList()[0] }
private val conversionPairs by lazy { repository.getConversionPair() }
var rateIdFrom: String? = conversionPairs.first ?: defaultValue
var rateIdTo: String? = conversionPairs.second ?: defaultValue
var topVal: String? = null
var bottomVal: String? = null
var rateListener: RateListener? = null
var rateIdFrom: String? = null
var rateIdTo: String? = null
//operation results livedata based on outcome of operation
val operationSuccess = MutableLiveData<Pair<Boolean, String>>()
val currencyRate = MutableLiveData<Double>()
val operationStartedListener = MutableLiveData<Boolean>()
val operationFinishedListener = MutableLiveData<Pair<Boolean, String?>>()
private var conversionRate: Double = 0.00
fun getExchangeRate(){
rateListener?.onStarted()
operationStartedListener.postValue(false)
if (rateIdFrom.isNullOrEmpty() || rateIdTo.isNullOrEmpty()){
rateListener?.onFailure("Select currencies")
operationFinishedListener.postValue(Pair(false, "Select currencies"))
return
}
CoroutineScope(Dispatchers.Main).launch {
if (rateIdFrom == rateIdTo){
conversionRate = 1.00
operationFinishedListener.postValue(Pair(true, null))
return
}
CoroutineScope(Dispatchers.IO).launch {
try {
val exchangeResponse = repository.getData(rateIdFrom!!, rateIdTo!!)
repository.setConversionPair(rateIdFrom!!, rateIdTo!!)
exchangeResponse.results?.iterator()?.next()?.value?.let {
rateListener?.onSuccess()
operationFinishedListener.postValue(Pair(true, null))
conversionRate = it.value
return@launch
}
}catch(e: IOException){
rateListener?.onFailure(e.message ?: "Currency Retrieval failed")
operationFinishedListener.postValue(Pair(false, e.message ?: "Currency Retrieval failed"))
return@launch
}
rateListener?.onFailure("Failed to retrieve rate")
operationFinishedListener.postValue(Pair(false, "Failed to retrieve rate"))
}
}
fun setBottomValue(fromValue : String, editText: EditText) {
val fromValDouble = fromValue.toDouble()
val bottomVal1 = (fromValDouble * conversionRate).toTwoDp()
editText.setText(bottomVal1.toBigDecimal().toPlainString())
}
fun setTopValue(toValue : String, editText: EditText) {
val toDoubleVal = toValue.toDouble()
val newTopVal = toDoubleVal.times((1/conversionRate)).toTwoDp()
editText.setText(newTopVal.toBigDecimal().toPlainString())
}
fun start(){
if (rateIdFrom != rateIdTo){
getExchangeRate()
fun getConversion(fromValue: String): String? {
return try {
val fromValDouble = fromValue.toDouble()
val bottomVal1 = (fromValDouble * conversionRate).toTwoDp()
bottomVal1.toBigDecimal().toPlainString()
}catch (e: NumberFormatException) {
Log.e(TAG, "no numbers inserted")
null
}
}
fun getReciprocalConversion(toValue: String): String? {
return try {
val toDoubleVal = toValue.toDouble()
val newTopVal = toDoubleVal.times((1/conversionRate)).toTwoDp()
newTopVal.toBigDecimal().toPlainString()
} catch (e: NumberFormatException) {
Log.e(TAG, "no numbers inserted")
null
}
}
fun setCurrencyName(tag: Any?, currencyName: String){
if (tag.toString() == "top"){
rateIdFrom = currencyName
}else{
rateIdTo = currencyName
}
getExchangeRate()
}
fun initiate(extras: Bundle?) {
rateIdFrom = extras?.getString("parse_1") ?: conversionPairs.first
rateIdTo = extras?.getString("parse_2") ?: conversionPairs.second
getExchangeRate()
}
}

View File

@@ -58,7 +58,6 @@
android:layout_marginTop="6dp"
android:background="@drawable/round_edit_text"
android:ems="10"
android:text="@={viewmodel.topVal}"
android:hint="insert value one"
android:textColorHighlight="#608d91"
android:inputType="numberDecimal"
@@ -96,7 +95,6 @@
android:layout_weight="7"
android:background="@drawable/round_edit_text"
android:ems="10"
android:text="@={viewmodel.bottomVal}"
android:hint="insert value two"
android:textColorHighlight="#608d91"
android:inputType="numberDecimal"