Skip to content

Commit

Permalink
Version 2.8
Browse files Browse the repository at this point in the history
  • Loading branch information
corenting committed May 7, 2021
1 parent db75eed commit d299c80
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 159 deletions.
6 changes: 4 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ android {
applicationId "fr.corenting.convertisseureurofranc"
minSdkVersion 17
targetSdkVersion 30
versionCode 16
versionName "2.7"
versionCode 17
versionName "2.8"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

Expand Down Expand Up @@ -50,6 +50,8 @@ dependencies {
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'com.google.android.material:material:1.3.0'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"


// Tests
testImplementation 'androidx.test:core-ktx:1.3.0'
Expand Down
39 changes: 39 additions & 0 deletions app/src/main/java/ConverterViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import fr.corenting.convertisseureurofranc.converters.ConverterAbstract
import java.lang.IllegalStateException

class ConverterViewModelFactory(private val initConverter: ConverterAbstract) :
ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
ConverterViewModel(initConverter) as T
}

class ConverterViewModel(initConverter: ConverterAbstract) : ViewModel() {
private val converter = MutableLiveData<ConverterAbstract>().apply {
postValue(initConverter)
}

fun getConverter(): LiveData<ConverterAbstract> {
return converter
}

fun setConverter(newConverter: ConverterAbstract) {
converter.postValue(newConverter)
}

fun doConversion(yearOfOrigin: Int, yearOfResult: Int, amount: Float): Float {
val currentConverter = converter.value
if (currentConverter != null) {
return currentConverter.convertFunction(
yearOfOrigin,
yearOfResult,
amount
)
} else {
throw IllegalStateException()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import android.widget.ArrayAdapter
import android.widget.Filter


class AutocompleteAdapter<T>(context: Context, resource: Int, objects: List<T>) :
class AutoCompleteAdapter<T>(context: Context, resource: Int, objects: List<T>) :
ArrayAdapter<T>(context, resource, objects) {

override fun getFilter(): Filter {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package fr.corenting.convertisseureurofranc

import ConverterViewModel
import ConverterViewModelFactory
import android.os.Bundle
import android.view.KeyEvent
import android.view.MenuItem
import android.view.View
import android.widget.AdapterView
import android.widget.AutoCompleteTextView
import android.widget.EditText
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.ConfigurationCompat
import androidx.core.widget.doOnTextChanged
import fr.corenting.convertisseureurofranc.converters.ConverterAbstract
import fr.corenting.convertisseureurofranc.converters.FranceConverter
import fr.corenting.convertisseureurofranc.converters.USAConverter
Expand All @@ -20,18 +20,8 @@ import fr.corenting.convertisseureurofranc.utils.Utils
import java.util.*

class ConverterActivity : AppCompatActivity() {

companion object {
private const val converterBundleKey = "converter"
}

private lateinit var converter: ConverterAbstract
private lateinit var binding: ActivityConverterBinding


// Save position for config change
private var currentConverter: Int = 0

override fun onCreate(savedInstanceState: Bundle?) {
// Theme and creation
AppCompatDelegate.setDefaultNightMode(ThemeUtils.getThemeToUse(this))
Expand All @@ -42,50 +32,101 @@ class ConverterActivity : AppCompatActivity() {
val view = binding.root
setContentView(view)
binding.topAppBar.setOnMenuItemClickListener(this::onMenuItemClickListener)
binding.topAppBar.menu.findItem(ThemeUtils.getMenuIdForCurrentTheme(this)).isChecked = true

//Set currency spinner content
// Common values
val currenciesList = listOf(
getString(R.string.usa_currencies),
getString(R.string.france_currencies)
)
setAutoCompleteAdapter(binding.currencyAutoComplete, currenciesList)
binding.currencyAutoComplete.onItemClickListener =
AdapterView.OnItemClickListener { _, _, position, _ ->
onCurrencyItemClickListener(position)
}

// Set default currency
if (savedInstanceState == null) {
val currentLocale: Locale =
ConfigurationCompat.getLocales(resources.configuration).get(0)
if (currentLocale == Locale.FRANCE) {
binding.currencyAutoComplete.setText(getString(R.string.france_currencies), false)
onCurrencyItemClickListener(1)
} else {
binding.currencyAutoComplete.setText(getString(R.string.usa_currencies), false)
onCurrencyItemClickListener(0)
// Sum input
binding.sumToConvertText.doOnTextChanged { text, _, _, _ ->
try {
if (text.toString().toFloat() > 0) {
binding.sumToConvertInput.error = null
}
} catch (err: NumberFormatException) {
}
} else {
onCurrencyItemClickListener(savedInstanceState.getInt(converterBundleKey))
}

binding.topAppBar.menu.findItem(ThemeUtils.getMenuIdForCurrentTheme(this)).isChecked = true
initButtons()
// Viewmodel and converter setup
val converterViewModel: ConverterViewModel by viewModels {
ConverterViewModelFactory(Utils.getConverterForCurrentLocale(applicationContext))
}
converterViewModel.getConverter().observe(this, {
if (it != null) {
onConverterChange(it)

// Update selected in list (used at app opening)
var selectedCurrencyPosition = 0
if (it is FranceConverter) {
selectedCurrencyPosition = 1
}
binding.currencyAutoComplete.setText(currenciesList[selectedCurrencyPosition])
}
})

//Set currency spinner content
val adapter = AutoCompleteAdapter(this, R.layout.list_item, currenciesList)
binding.currencyAutoComplete.setAdapter(adapter)
binding.currencyAutoComplete.setOnItemClickListener { _, _, position, _ ->
when (position) {
0 -> converterViewModel.setConverter(USAConverter(applicationContext))
else -> converterViewModel.setConverter(FranceConverter(applicationContext))
}
}
initButtons(converterViewModel)
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
private fun onConverterChange(newConverter: ConverterAbstract) {
val latestYear = newConverter.latestYear
val firstYear = newConverter.firstYear

outState.putInt(converterBundleKey, currentConverter)
}
binding.yearOfOriginAutoComplete.setText(latestYear.toString())
binding.yearOfOriginInput.hint = getString(R.string.yearOfOrigin, firstYear, latestYear)
binding.yearOfOriginAutoComplete.doOnTextChanged { text, _, _, _ ->
YearInputTextHandler.doOnTextChanged(
applicationContext,
newConverter,
binding.yearOfOriginAutoComplete,
binding.yearOfOriginInput
)
try {
val year = text.toString().toInt()
binding.sumToConvertInput.hint = getString(
R.string.sumToConvert, newConverter.getCurrencyFromYear(year)
)
} catch (exc: NumberFormatException) {
}
}
binding.sumToConvertInput.hint = getString(
R.string.sumToConvert,
newConverter.getCurrencyFromYear(latestYear)
)

private fun onCurrencyItemClickListener(position: Int) {
converter = when (position) {
0 -> USAConverter(applicationContext)
else -> FranceConverter(applicationContext)
binding.yearOfResultAutoComplete.setText(latestYear.toString())
binding.yearOfResultInput.hint = getString(R.string.yearOfOrigin, firstYear, latestYear)
binding.yearOfResultAutoComplete.doOnTextChanged { text, _, _, _ ->
YearInputTextHandler.doOnTextChanged(
applicationContext,
newConverter,
binding.yearOfResultAutoComplete,
binding.yearOfResultInput
)
try {
val year = text.toString().toInt()
binding.resultInput.hint = getString(
R.string.resultText,
newConverter.getCurrencyFromYear(year)
)
} catch (exc: NumberFormatException) {
}
}
currentConverter = position
initInputFields()
binding.resultInput.hint = getString(
R.string.resultText,
newConverter.getCurrencyFromYear(latestYear)
)
}

private fun onMenuItemClickListener(menuItem: MenuItem): Boolean {
Expand All @@ -104,72 +145,7 @@ class ConverterActivity : AppCompatActivity() {
}
}

private fun initInputFields() {
// Set years inputs
val yearFields = listOf(
Triple(
binding.yearOfOriginAutoComplete,
binding.yearOfOriginInput,
R.string.yearOfOrigin
),
Triple(
binding.yearOfResultAutoComplete,
binding.yearOfResultInput,
R.string.yearOfResult
)
)

val yearsList = (converter.latestYear downTo converter.firstYear).toList().map {
it.toString()
}
for ((yearInputField, yearInputLayout, hintStringId) in yearFields) {
yearInputField.setText(yearsList.first().toString())
yearInputLayout.hint =
getString(hintStringId, converter.firstYear, converter.latestYear)
yearInputField.onFocusChangeListener = View.OnFocusChangeListener { v, _ ->
val textContent = (v as EditText).text.toString()
val errorString = getString(
R.string.invalid_year_error
)
if (textContent.isBlank()) {
yearInputLayout.error = errorString
}

try {
val year = textContent.toInt()
when {
year < converter.firstYear || year > converter.latestYear -> {
yearInputLayout.error = errorString
}
else -> {
yearInputLayout.error = null
}
}
} catch (err: NumberFormatException) {
yearInputLayout.error = errorString
}

// TODO: refresh corresponding currency input to handle currency change
}
}

refreshCurrencyInputs(converter.latestYear, converter.latestYear - 2)
}

private fun refreshCurrencyInputs(fromYear: Int, toYear: Int) {
val currencyFields = listOf(
Triple(binding.sumToConvertInput, R.string.sumToConvert, fromYear),
Triple(binding.resultInput, R.string.resultText, toYear)
)
for ((sumInput, hintStringId, year) in currencyFields) {
sumInput.hint = getString(
hintStringId,
converter.getCurrencyFromYear(year)
)
}
}

private fun initButtons() {
private fun initButtons(converterViewModel: ConverterViewModel) {
//Convert when the button is clicked
binding.convertButton.setImeActionLabel(
getString(R.string.convertButton),
Expand All @@ -187,38 +163,34 @@ class ConverterActivity : AppCompatActivity() {

//Setup listeners
binding.convertButton.setOnClickListener { v ->
try {
binding.sumToConvertInput.error = null
Utils.hideSoftKeyboard(v)
val yearOfOrigin =
Integer.parseInt(binding.yearOfOriginAutoComplete.text.toString())
val yearOfResult =
Integer.parseInt(binding.yearOfResultAutoComplete.text.toString())
val amount = java.lang.Float.parseFloat(binding.sumToConvertText.text.toString())
binding.resultText.setText(
Utils.formatNumber(
this,
converter.convertFunction(yearOfOrigin, yearOfResult, amount)
)
)
} catch (e: Exception) {
if (binding.sumToConvertText.text == null || binding.sumToConvertText.text.toString() == "") {
binding.sumToConvertInput.error = getString(R.string.no_amount_entered)
}
val errorToast = Toast.makeText(
this, getString(R.string.errorToast),
Toast.LENGTH_SHORT
)
errorToast.show()
}
binding.convertButton.requestFocus()
binding.sumToConvertInput.clearFocus()
binding.yearOfOriginAutoComplete.clearFocus()
binding.yearOfResultAutoComplete.clearFocus()
Utils.hideSoftKeyboard(v)
doConversion(converterViewModel)
}
}

private fun setAutoCompleteAdapter(
autoCompleteTextView: AutoCompleteTextView,
items: List<String>
) {
val adapter = AutocompleteAdapter(this, R.layout.list_item, items)
autoCompleteTextView.setAdapter(adapter)
private fun doConversion(converterViewModel: ConverterViewModel) {
try {
binding.sumToConvertInput.error = null
val yearOfOrigin =
Integer.parseInt(binding.yearOfOriginAutoComplete.text.toString())
val yearOfResult =
Integer.parseInt(binding.yearOfResultAutoComplete.text.toString())
val amount = java.lang.Float.parseFloat(binding.sumToConvertText.text.toString())
val convertedAmount =
converterViewModel.doConversion(yearOfOrigin, yearOfResult, amount)
binding.resultText.setText(Utils.formatNumber(this, convertedAmount))
} catch (e: Exception) {
if (binding.sumToConvertText.text == null || binding.sumToConvertText.text.toString() == "") {
binding.sumToConvertInput.error = getString(R.string.no_amount_entered)
}
Toast.makeText(
this, getString(R.string.errorToast),
Toast.LENGTH_SHORT
).show()
}
}
}
Loading

0 comments on commit d299c80

Please sign in to comment.