Skip to content

Commit

Permalink
new feature: Foreign transaction fee + bump version to 1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
sal0max committed Nov 23, 2020
1 parent afebcea commit f391729
Show file tree
Hide file tree
Showing 17 changed files with 350 additions and 27 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ android {
minSdkVersion 26
targetSdkVersion 30
// SemVer
versionName = '1.0.1'
versionCode = 10001
versionName = '1.1.0'
versionCode = 10100
archivesBaseName = "$applicationId-v$versionCode"
}

Expand Down
26 changes: 26 additions & 0 deletions app/src/main/java/de/salomax/currencies/repository/Database.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import android.content.Context.MODE_PRIVATE
import android.content.SharedPreferences
import androidx.lifecycle.LiveData
import de.salomax.currencies.model.ExchangeRates
import de.salomax.currencies.util.SharedPreferenceBooleanLiveData
import de.salomax.currencies.util.SharedPreferenceExchangeRatesLiveData
import de.salomax.currencies.util.SharedPreferenceFloatLiveData

import java.time.LocalDate

Expand Down Expand Up @@ -78,6 +80,8 @@ class Database(context: Context) {
*/
private val prefs: SharedPreferences = context.getSharedPreferences("prefs", MODE_PRIVATE)

/* theme */

fun setTheme(theme: Int) {
prefs.apply {
edit().putInt("_theme", theme).apply()
Expand All @@ -88,4 +92,26 @@ class Database(context: Context) {
return prefs.getInt("_theme", 2)
}

/* fee */

fun setFeeEnabled(enabled: Boolean) {
prefs.apply {
edit().putBoolean("_feeEnabled", enabled).apply()
}
}

fun isFeeEnabled(): LiveData<Boolean> {
return SharedPreferenceBooleanLiveData(prefs, "_feeEnabled", false)
}

fun setFee(fee: Float) {
prefs.apply {
edit().putFloat("_fee", fee).apply()
}
}

fun getFee(): LiveData<Float> {
return SharedPreferenceFloatLiveData(prefs, "_fee", 2.2f)
}

}
18 changes: 18 additions & 0 deletions app/src/main/java/de/salomax/currencies/util/TextUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package de.salomax.currencies.util

import android.content.Context
import de.salomax.currencies.R
import java.lang.StringBuilder


fun Float.humanReadableFee(context: Context): String {
val sb = StringBuilder()
if (this >= 0)
sb.append("+ ")
sb.append(this.toString()
.replace(".", context.getString(R.string.decimal_separator))
.replace("-", "- ")
)
sb.append(" %")
return sb.toString()
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import androidx.core.text.HtmlCompat
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.snackbar.Snackbar
import de.salomax.currencies.R
import de.salomax.currencies.util.humanReadableFee
import de.salomax.currencies.view.preference.PreferenceActivity
import de.salomax.currencies.viewmodel.main.CurrentInputViewModel
import de.salomax.currencies.viewmodel.main.ExchangeRatesViewModel
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle


class MainActivity : AppCompatActivity() {

private lateinit var ratesModel: ExchangeRatesViewModel
Expand All @@ -35,6 +35,7 @@ class MainActivity : AppCompatActivity() {
private lateinit var spinnerFrom: Spinner
private lateinit var spinnerTo: Spinner
private lateinit var tvDate: TextView
private lateinit var tvFee: TextView

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -56,6 +57,7 @@ class MainActivity : AppCompatActivity() {
this.spinnerFrom = findViewById(R.id.spinnerFrom)
this.spinnerTo = findViewById(R.id.spinnerTo)
this.tvDate = findViewById(R.id.textRefreshed)
this.tvFee = findViewById(R.id.textFee)

// listeners & stuff
setListeners()
Expand Down Expand Up @@ -189,6 +191,18 @@ class MainActivity : AppCompatActivity() {
inputModel.getCurrencyTo().observe(this, {
tvCurrencyTo.text = it
})

// fee changed
inputModel.getFeeEnabled().observe(this, {
tvFee.visibility = if (it) View.VISIBLE else View.GONE
})
inputModel.getFee().observe(this, {
tvFee.text = it.humanReadableFee(this)
tvFee.setTextColor(
if (it >= 0) getColor(android.R.color.holo_red_light)
else getColor(R.color.dollarBill)
)
})
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import de.salomax.currencies.BuildConfig
import de.salomax.currencies.R
import de.salomax.currencies.util.humanReadableFee
import de.salomax.currencies.viewmodel.preference.PreferenceViewModel
import de.salomax.currencies.widget.EditTextSwitchPreference
import java.util.*

@Suppress("unused")
Expand All @@ -21,11 +23,24 @@ class PreferenceFragment: PreferenceFragmentCompat() {

// theme
val themePreference = findPreference<ListPreference>(getString(R.string.prefKey_theme))!!
themePreference.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _, newValue ->
viewModel.setTheme(newValue.toString().toInt())
true
}
themePreference.setOnPreferenceChangeListener { _, newValue ->
viewModel.setTheme(newValue.toString().toInt())
true
}

// fee
val feePreference = findPreference<EditTextSwitchPreference>(getString(R.string.prefKey_fee))!!
feePreference.setOnPreferenceChangeListener { _, newValue ->
if (newValue is String)
viewModel.setFee(newValue.toString().toFloat())
else if (newValue is Boolean)
viewModel.setFeeEnabled(newValue.toString().toBoolean())
true
}
viewModel.getFee().observe(this, {
feePreference.summary = it.humanReadableFee(requireContext())
feePreference.text = it.toString()
})

// about
val aboutPreference = findPreference<Preference>(getString(R.string.prefKey_about))!!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package de.salomax.currencies.viewmodel.main

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.*
import de.salomax.currencies.R
import de.salomax.currencies.repository.Database
import de.salomax.currencies.model.Rate
Expand All @@ -26,8 +23,8 @@ class CurrentInputViewModel(private val ctx: Application) : AndroidViewModel(ctx
MutableLiveData<String>("0")
}

private val currentValueConverted: MutableLiveData<String> by lazy {
MutableLiveData<String>("0")
private val currentValueConverted: MutableLiveData<Double> by lazy {
MutableLiveData<Double>(0.0)
}

private val currentCalculation: MutableLiveData<String?> by lazy {
Expand All @@ -41,8 +38,40 @@ class CurrentInputViewModel(private val ctx: Application) : AndroidViewModel(ctx
}

fun getCurrentInputConverted(): LiveData<String> {
return Transformations.map(currentValueConverted) {
it.humanReadable(ctx.getString(R.string.thousands_separator)[0], ctx.getString(R.string.decimal_separator)[0])
return MediatorLiveData<String>().apply {
var fee: Float? = null
var feeEnabled: Boolean? = null
var currentValue: Double? = null

fun update() {
if (fee != null && feeEnabled != null && currentValue != null)
this.value =
if (!feeEnabled!!) {
currentValue
} else {
currentValue!! + (currentValue!! * (fee!! / 100))
}.toString()
.scientificToNatural()
.humanReadable(
ctx.getString(R.string.thousands_separator)[0],
ctx.getString(R.string.decimal_separator)[0]
)
}

addSource(currentValueConverted) {
currentValue = it
update()
}

addSource(getFee()) {
fee = it
update()
}

addSource(getFeeEnabled()) {
feeEnabled = it
update()
}
}
}

Expand All @@ -64,6 +93,14 @@ class CurrentInputViewModel(private val ctx: Application) : AndroidViewModel(ctx
}
}

fun getFeeEnabled(): LiveData<Boolean> {
return Database.getInstance(getApplication()).isFeeEnabled()
}

fun getFee(): LiveData<Float> {
return Database.getInstance(getApplication()).getFee()
}

fun addNumber(value: String) {
// in calculation mode: add to upper row
if (isCalculating()) {
Expand Down Expand Up @@ -238,8 +275,6 @@ class CurrentInputViewModel(private val ctx: Application) : AndroidViewModel(ctx
?.toDouble()
?.div(rateFrom)
?.times(rateTo)
?.toString()
?.scientificToNatural()
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package de.salomax.currencies.viewmodel.preference
import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import de.salomax.currencies.repository.Database

class PreferenceViewModel(application: Application) : AndroidViewModel(application) {
Expand All @@ -19,4 +20,16 @@ class PreferenceViewModel(application: Application) : AndroidViewModel(applicati
)
}

fun setFee(fee: Float) {
Database.getInstance(getApplication()).setFee(fee)
}

fun getFee(): LiveData<Float> {
return Database.getInstance(getApplication()).getFee()
}

fun setFeeEnabled(enabled: Boolean) {
Database.getInstance(getApplication()).setFeeEnabled(enabled)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package de.salomax.currencies.widget;

import android.content.Context;
import android.util.AttributeSet;

import androidx.appcompat.widget.SwitchCompat;
import androidx.preference.EditTextPreference;
import androidx.preference.PreferenceDataStore;
import androidx.preference.PreferenceViewHolder;

import de.salomax.currencies.R;

@SuppressWarnings("unused")
public class EditTextSwitchPreference extends EditTextPreference {

private PreferenceViewHolder holder;

private final String KEY_SWITCH;

public EditTextSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setWidgetLayoutResource(R.layout.preference_edit_text_switch);
KEY_SWITCH = getKey() + "_switch";
}

public EditTextSwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setWidgetLayoutResource(R.layout.preference_edit_text_switch);
KEY_SWITCH = getKey() + "_switch";
}

public EditTextSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}

public EditTextSwitchPreference(Context context) {
this(context, null);
}

@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);

this.holder = holder;
SwitchCompat btnSwitch = (SwitchCompat) holder.findViewById(android.R.id.toggle);
// state stuff
btnSwitch.setChecked(isChecked());
setEditTextEnabled(isChecked());
btnSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> setChecked(isChecked));
}

@Override
protected void onSetInitialValue(Object defaultValue) {
setEditTextEnabled(isChecked());
}

private void setChecked(boolean checked) {
persistBoolean(checked);
setEditTextEnabled(checked);
}

private boolean isChecked() {
return getPersistedBoolean(false);
}

private void setEditTextEnabled(boolean enabled) {
if (holder != null) {
holder.itemView.setEnabled(enabled);
holder.findViewById(android.R.id.title).setEnabled(enabled);
holder.findViewById(android.R.id.summary).setEnabled(enabled);
}
}

@Override
protected boolean persistBoolean(boolean value) {
if (!shouldPersist()) {
return false;
}

// It's already there, so the same as persisting
if (value == isChecked()) {
return true;
}

PreferenceDataStore dataStore = getPreferenceDataStore();
if (dataStore != null) {
dataStore.putBoolean(KEY_SWITCH, value);
} else {
getPreferenceManager()
.getSharedPreferences()
.edit()
.putBoolean(KEY_SWITCH, value)
.apply();
}
callChangeListener(value);
return true;
}

@Override
protected boolean getPersistedBoolean(boolean defaultReturnValue) {
if (!shouldPersist()) {
return defaultReturnValue;
}

PreferenceDataStore dataStore = getPreferenceDataStore();
if (dataStore != null) {
return dataStore.getBoolean(KEY_SWITCH, defaultReturnValue);
} else {
return getPreferenceManager()
.getSharedPreferences()
.getBoolean(KEY_SWITCH, defaultReturnValue);
}
}

}
Loading

0 comments on commit f391729

Please sign in to comment.