Unit tests added

Query interceptor added
Unit tests created
 - Repository test network
 - Repository test storage
This commit is contained in:
2020-05-16 12:05:50 +01:00
parent 7308d3f9df
commit 9ca1597e67
10 changed files with 101 additions and 83 deletions

View File

@@ -2,8 +2,8 @@ package com.appttude.h_mal.easycc.mvvm.application
import android.app.Application
import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
import com.appttude.h_mal.easycc.mvvm.data.network.NetworkConnectionInterceptor
import com.appttude.h_mal.easycc.mvvm.data.network.QueryInterceptor
import com.appttude.h_mal.easycc.mvvm.data.network.interceptors.NetworkConnectionInterceptor
import com.appttude.h_mal.easycc.mvvm.data.network.interceptors.QueryInterceptor
import com.appttude.h_mal.easycc.mvvm.data.network.api.CurrencyApi
import com.appttude.h_mal.easycc.mvvm.data.prefs.PreferenceProvider
import com.appttude.h_mal.easycc.mvvm.ui.app.MainViewModelFactory

View File

@@ -1,8 +1,8 @@
package com.appttude.h_mal.easycc.mvvm.data.network.api
import com.appttude.h_mal.easycc.mvvm.data.network.response.ResponseObject
import com.appttude.h_mal.easycc.mvvm.data.network.NetworkConnectionInterceptor
import com.appttude.h_mal.easycc.mvvm.data.network.QueryInterceptor
import com.appttude.h_mal.easycc.mvvm.data.network.interceptors.NetworkConnectionInterceptor
import com.appttude.h_mal.easycc.mvvm.data.network.interceptors.QueryInterceptor
import okhttp3.OkHttpClient
import retrofit2.Response
import retrofit2.Retrofit

View File

@@ -1,4 +1,4 @@
package com.appttude.h_mal.easycc.mvvm.data.network
package com.appttude.h_mal.easycc.mvvm.data.network.interceptors
import android.content.Context
import android.net.ConnectivityManager
@@ -7,6 +7,10 @@ import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
/**
* Interceptor used in [CurrencyApi] to intercept network status
*
*/
class NetworkConnectionInterceptor(
context: Context
) : Interceptor {

View File

@@ -1,14 +1,18 @@
package com.appttude.h_mal.easycc.mvvm.data.network
package com.appttude.h_mal.easycc.mvvm.data.network.interceptors
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import com.appttude.h_mal.easycc.BuildConfig
import okhttp3.HttpUrl
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
/**
* Interceptor used in CurrencyApi
* Adds apiKey to query parameters
*/
class QueryInterceptor() : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
@@ -16,7 +20,7 @@ class QueryInterceptor() : Interceptor {
val originalHttpUrl: HttpUrl = original.url()
val url = originalHttpUrl.newBuilder()
.addQueryParameter("apikey", "a4f93cc2ff05dd772321")
.addQueryParameter("apikey", BuildConfig.CC_API_KEY)
.build()
// Add amended Url back to request

View File

@@ -5,76 +5,80 @@ import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import com.appttude.h_mal.easycc.R
private const val CONVERSION_ONE = "conversion_one"
private const val CONVERSION_TWO = "conversion_two"
private const val CONVERSION_ONE_WIDGET = "conversion_one_widget"
private const val CONVERSION_TWO_WIDGET = "conversion_two_widget"
/**
* Shared prefs class used for storing conversion name values as pairs
* Then retrieving as pairs
*
*/
private const val CURRENCY_ONE = "conversion_one"
private const val CURRENCY_TWO = "conversion_two"
class PreferenceProvider(
context: Context
) {
class PreferenceProvider(context: Context) {
private val appContext = context.applicationContext
// Instance of Shared preferences
private val preference: SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(appContext)
private val defaultRate: String = context.resources.getStringArray(R.array.currency_arrays)[0]
= PreferenceManager.getDefaultSharedPreferences(appContext)
// Lazy declaration of default rate if no rate is retrieved from
private val defaultRate: String by lazy {
context.resources.getStringArray(R.array.currency_arrays)[0]
}
// Save currency pairs into prefs
fun saveConversionPair(s1: String, s2: String) {
preference.edit().putString(
CONVERSION_ONE,
s1
).putString(
CONVERSION_TWO,
s2
).apply()
preference.edit()
.putString(CURRENCY_ONE, s1)
.putString(CURRENCY_TWO, s2)
.apply()
}
// Retrieve Currency pairs from prefs
// Returns Pairs
fun getConversionPair(): Pair<String?, String?> {
val s1 = getLastConversionOne()
val s2 = getLastConversionTwo()
val fromString = getConversionString(CURRENCY_ONE)
val toString = getConversionString(CURRENCY_TWO)
return Pair(s1,s2)
return Pair(fromString, toString)
}
private fun getLastConversionOne(): String? {
return preference.getString(CONVERSION_ONE, defaultRate)
private fun getConversionString(conversionName: String): String? {
return preference
.getString(conversionName, defaultRate)
}
private fun getLastConversionTwo(): String? {
return preference.getString(CONVERSION_TWO, defaultRate)
// Save currency pairs for widget
fun saveWidgetConversionPair(fromString: String,
toString: String, appWidgetId: Int) {
preference.edit()
.putString("${appWidgetId}_$CURRENCY_ONE", fromString)
.putString("${appWidgetId}_$CURRENCY_TWO", toString)
.apply()
}
fun saveWidgetConversionPair(s1: String, s2: String, id: Int) {
preference.edit().putString(
"${id}_$CONVERSION_ONE",
s1
).putString(
"${id}_$CONVERSION_TWO",
s2
).apply()
// Retrieve currency pairs for widget
fun getWidgetConversionPair(appWidgetId: Int): Pair<String?, String?> {
val fromString = getWidgetConversionString(appWidgetId, CURRENCY_ONE)
val toString = getWidgetConversionString(appWidgetId, CURRENCY_TWO)
return Pair(fromString, toString)
}
fun getWidgetConversionPair(id: Int): Pair<String?, String?> {
val s1 = getWidgetLastConversionOne(id)
val s2 = getWidgetLastConversionTwo(id)
return Pair(s1, s2)
private fun getWidgetConversionString(
appWidgetId: Int, conversionName: String): String? {
return preference
.getString("${appWidgetId}_$conversionName", defaultRate)
}
private fun getWidgetLastConversionOne(id: Int): String? {
return preference.getString("${id}_$CONVERSION_ONE", defaultRate)
}
fun removeWidgetConversion(id: Int) {
preference.edit()
.remove("${id}_$CURRENCY_ONE")
.remove("${id}_$CURRENCY_TWO")
.apply()
private fun getWidgetLastConversionTwo(id: Int): String? {
return preference.getString("${id}_$CONVERSION_TWO", defaultRate)
}
fun removeWidgetConversion(id: Int){
preference.edit().remove("${id}_$CONVERSION_ONE").apply()
preference.edit().remove("${id}_$CONVERSION_TWO").apply()
}
}

View File

@@ -1,22 +1,24 @@
package com.appttude.h_mal.easycc.mvvm.data.repository
import com.appttude.h_mal.easycc.R
import com.appttude.h_mal.easycc.mvvm.data.network.response.ResponseObject
import com.appttude.h_mal.easycc.mvvm.utils.convertPairsListToString
/**
* Main entry point for accessing currency data.
*/
interface Repository {
suspend fun getData(s1: String, s2: String): ResponseObject
suspend fun getData(fromCurrency: String, toCurrency: String): ResponseObject
fun getConversionPair(): Pair<String?, String?>
fun setConversionPair(s1: String, s2: String)
fun setConversionPair(fromCurrency: String, toCurrency: String)
fun getArrayList(): Array<String>
fun getWidgetConversionPairs(id: Int): Pair<String?, String?>
fun getWidgetConversionPairs(appWidgetId: Int): Pair<String?, String?>
fun setWidgetConversionPairs(s1: String, s2: String, id: Int)
fun setWidgetConversionPairs(fromCurrency: String, toCurrency: String, appWidgetId: Int)
fun removeWidgetConversionPairs(id: Int)

View File

@@ -1,7 +1,6 @@
package com.appttude.h_mal.easycc.mvvm.data.repository
import android.content.Context
import com.appttude.h_mal.easycc.BuildConfig
import com.appttude.h_mal.easycc.mvvm.data.prefs.PreferenceProvider
import com.appttude.h_mal.easycc.R
import com.appttude.h_mal.easycc.mvvm.data.network.response.ResponseObject
@@ -9,7 +8,9 @@ import com.appttude.h_mal.easycc.mvvm.data.network.SafeApiRequest
import com.appttude.h_mal.easycc.mvvm.data.network.api.CurrencyApi
import com.appttude.h_mal.easycc.mvvm.utils.convertPairsListToString
/**
* Default implementation of [Repository]. Single entry point for managing currency' data.
*/
class RepositoryImpl (
private val api: CurrencyApi,
private val prefs: PreferenceProvider,
@@ -18,29 +19,31 @@ class RepositoryImpl (
private val appContext = context.applicationContext
override suspend fun getData(s1: String, s2: String
override suspend fun getData(fromCurrency: String, toCurrency: String
): ResponseObject{
val currencyPair = convertPairsListToString(s1, s2)
return responseUnwrap{
api.getCurrencyRate(currencyPair)}
// Set currency pairs as correct string for api query eg. AUD_GBP
val currencyPair = convertPairsListToString(fromCurrency, toCurrency)
return responseUnwrap{ api.getCurrencyRate(currencyPair)}
}
override fun getConversionPair(): Pair<String?, String?> {
return prefs.getConversionPair()
}
override fun setConversionPair(s1: String, s2: String){
prefs.saveConversionPair(s1, s2)
override fun setConversionPair(fromCurrency: String, toCurrency: String){
prefs.saveConversionPair(fromCurrency, toCurrency)
}
override fun getArrayList(): Array<String> =
appContext.resources.getStringArray(R.array.currency_arrays)
override fun getWidgetConversionPairs(id: Int): Pair<String?, String?> =
prefs.getWidgetConversionPair(id)
override fun getWidgetConversionPairs(appWidgetId: Int): Pair<String?, String?> =
prefs.getWidgetConversionPair(appWidgetId)
override fun setWidgetConversionPairs(s1: String, s2: String, id: Int) =
prefs.saveWidgetConversionPair(s1, s2, id)
override fun setWidgetConversionPairs(fromCurrency: String,
toCurrency: String, appWidgetId: Int) {
return prefs.saveWidgetConversionPair(fromCurrency, toCurrency, appWidgetId)
}
override fun removeWidgetConversionPairs(id: Int) =
prefs.removeWidgetConversion(id)

View File

@@ -4,6 +4,7 @@ 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.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -26,9 +27,7 @@ class MainViewModel(
var rateListener: RateListener? = null
//operation results livedata based on outcome of operation
val operationSuccess = MutableLiveData<Boolean>()
val operationFailed = MutableLiveData<String>()
val operationSuccess = MutableLiveData<Pair<Boolean, String>>()
val currencyRate = MutableLiveData<Double>()
private var conversionRate: Double = 0.00
@@ -71,11 +70,6 @@ class MainViewModel(
editText.setText(newTopVal.toBigDecimal().toPlainString())
}
private fun Double.toTwoDp() = run {
val df = DecimalFormat("#.##")
java.lang.Double.valueOf(df.format(this))
}
fun start(){
if (rateIdFrom != rateIdTo){
getExchangeRate()

View File

@@ -1,5 +1,7 @@
package com.appttude.h_mal.easycc.mvvm.utils
import java.text.DecimalFormat
fun transformIntToArray(int: Int): IntArray{
return intArrayOf(int)
}
@@ -12,4 +14,9 @@ fun String.trimToThree(): String{
}
fun convertPairsListToString(s1: String, s2: String): String =
"${s1.trimToThree()}_${s2.trimToThree()}"
"${s1.trimToThree()}_${s2.trimToThree()}"
fun Double.toTwoDp() = run {
val df = DecimalFormat("#.##")
java.lang.Double.valueOf(df.format(this))
}

View File

@@ -45,7 +45,7 @@ class RepositoryStorageTest {
}
@Test
fun saveAndRetrieveCredentials_PositiveResponse() {
fun saveAndRetrieveWidgetPairs_PositiveResponse() {
//GIVEN
val s1 = "AUD - Australian Dollar"
val s2 = "GBP - British Pound"