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

Binary file not shown.

2
.idea/misc.xml generated
View File

@@ -39,7 +39,7 @@
</value> </value>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@@ -34,9 +34,7 @@ android {
def ccApiKey = properties.getProperty("cc_api_key", "") def ccApiKey = properties.getProperty("cc_api_key", "")
it.buildConfigField 'String', "CC_API_KEY", ccApiKey it.buildConfigField 'String', "CC_API_KEY", ccApiKey
it.resValue 'string', "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. * Main entry point for accessing currency data.
*/ */
interface Repository { interface Repository {
suspend fun getData(fromCurrency: String, toCurrency: String): ResponseObject suspend fun getData(fromCurrency: String, toCurrency: String): ResponseObject

View File

@@ -7,14 +7,12 @@ import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.WindowManager import android.view.WindowManager
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.TextView
import com.appttude.h_mal.easycc.R import com.appttude.h_mal.easycc.R
import kotlinx.android.synthetic.main.custom_dialog.* import kotlinx.android.synthetic.main.custom_dialog.*
class CustomDialogClass( class CustomDialogClass(
context: Context, context: Context,
val textView: TextView, private val clickListener: ClickListener
val viewModel: MainViewModel
) : Dialog(context) { ) : Dialog(context) {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -24,7 +22,11 @@ class CustomDialogClass(
window!!.setBackgroundDrawableResource(android.R.color.transparent) window!!.setBackgroundDrawableResource(android.R.color.transparent)
window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) 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 list_view.adapter = arrayAdapter
search_text.addTextChangedListener(object : TextWatcher { search_text.addTextChangedListener(object : TextWatcher {
@@ -36,15 +38,12 @@ class CustomDialogClass(
}) })
list_view.setOnItemClickListener{ adapterView, _, i, _ -> list_view.setOnItemClickListener{ adapterView, _, i, _ ->
if (textView.tag == "top"){ clickListener.onText(adapterView.getItemAtPosition(i).toString())
viewModel.rateIdFrom = adapterView.getItemAtPosition(i).toString()
}else{
viewModel.rateIdTo = adapterView.getItemAtPosition(i).toString()
}
textView.text = adapterView.getItemAtPosition(i).toString()
viewModel.getExchangeRate()
dismiss() 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.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.util.Log
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.ViewModelProviders
import com.appttude.h_mal.easycc.R import com.appttude.h_mal.easycc.R
import com.appttude.h_mal.easycc.databinding.ActivityMainBinding 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.android.kodein
import org.kodein.di.generic.instance 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() 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 lateinit var viewModel: MainViewModel
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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) // Bind viewmodel to layout with view binding
viewModel = ViewModelProviders.of(this, factory).get(MainViewModel::class.java) DataBindingUtil
.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.viewmodel = viewModel .apply {
binding.lifecycleOwner = this viewmodel = viewModel
lifecycleOwner = this@MainActivity
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()
}
} }
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) topInsertValue.addTextChangedListener(textWatcherClass)
bottomInsertValues.addTextChangedListener(textWatcherClass2) bottomInsertValues.addTextChangedListener(textWatcherClass2)
@@ -63,8 +77,14 @@ class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClic
currency_two.setOnClickListener(this) currency_two.setOnClickListener(this)
} }
private fun showCustomDialog(view: View?){ private fun showCustomDialog(view: View?) {
val dialogClass = CustomDialogClass(this, view as TextView, viewModel)
val dialogClass = CustomDialogClass(this, object : ClickListener {
override fun onText(currencyName: String) {
(view as TextView).text = currencyName
viewModel.setCurrencyName(view.tag, currencyName)
}
})
dialogClass.show() dialogClass.show()
} }
@@ -90,17 +110,13 @@ class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClic
private val textWatcherClass: TextWatcher = object : TextWatcher { private val textWatcherClass: TextWatcher = object : TextWatcher {
override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) { override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) {
bottomInsertValues.removeTextChangedListener(textWatcherClass2) bottomInsertValues.removeTextChangedListener(textWatcherClass2)
if (topInsertValue.text.isNullOrEmpty()) { if (topInsertValue.text.isNullOrEmpty())
bottomInsertValues.setText("") bottomInsertValues.setText("")
} }
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable) { override fun afterTextChanged(s: Editable) {
try { bottomInsertValues.setText(viewModel.getConversion(s.toString()))
viewModel.setBottomValue(s.toString(), bottomInsertValues)
} catch (e: NumberFormatException) {
Log.e(this.javaClass.simpleName, "no numbers inserted")
}
bottomInsertValues.addTextChangedListener(textWatcherClass2) bottomInsertValues.addTextChangedListener(textWatcherClass2)
} }
} }
@@ -108,17 +124,13 @@ class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClic
private val textWatcherClass2: TextWatcher = object : TextWatcher { private val textWatcherClass2: TextWatcher = object : TextWatcher {
override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) { override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) {
topInsertValue.removeTextChangedListener(textWatcherClass) topInsertValue.removeTextChangedListener(textWatcherClass)
if (bottomInsertValues.text.isNullOrEmpty()) { if (bottomInsertValues.text.isNullOrEmpty())
topInsertValue.clearEditText() topInsertValue.clearEditText()
} }
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: Editable) { override fun afterTextChanged(s: Editable) {
try { topInsertValue.setText(viewModel.getReciprocalConversion(s.toString()))
viewModel.setTopValue(s.toString(), topInsertValue)
} catch (e: NumberFormatException) {
Log.e(this.javaClass.simpleName, "no numbers inserted")
}
topInsertValue.addTextChangedListener(textWatcherClass) topInsertValue.addTextChangedListener(textWatcherClass)
} }
} }

View File

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