From 9ca1597e674e49c1e0828419676963bc31ea5e0d Mon Sep 17 00:00:00 2001 From: hmalik144 Date: Sat, 16 May 2020 12:05:50 +0100 Subject: [PATCH] Unit tests added Query interceptor added Unit tests created - Repository test network - Repository test storage --- .../h_mal/easycc/mvvm/application/AppClass.kt | 4 +- .../mvvm/data/network/api/CurrencyApi.kt | 4 +- .../NetworkConnectionInterceptor.kt | 6 +- .../{ => interceptors}/QueryInterceptor.kt | 10 +- .../mvvm/data/prefs/PreferenceProvider.kt | 98 ++++++++++--------- .../easycc/mvvm/data/repository/Repository.kt | 14 +-- .../mvvm/data/repository/RepositoryImpl.kt | 27 ++--- .../h_mal/easycc/mvvm/ui/app/MainViewModel.kt | 10 +- .../h_mal/easycc/mvvm/utils/PrimitiveUtils.kt | 9 +- .../repository/RepositoryStorageTest.kt | 2 +- 10 files changed, 101 insertions(+), 83 deletions(-) rename app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/{ => interceptors}/NetworkConnectionInterceptor.kt (89%) rename app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/{ => interceptors}/QueryInterceptor.kt (73%) diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/application/AppClass.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/application/AppClass.kt index 1e9e0b8..c68e112 100644 --- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/application/AppClass.kt +++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/application/AppClass.kt @@ -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 diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/api/CurrencyApi.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/api/CurrencyApi.kt index 097eab1..c8367dd 100644 --- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/api/CurrencyApi.kt +++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/api/CurrencyApi.kt @@ -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 diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/NetworkConnectionInterceptor.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/interceptors/NetworkConnectionInterceptor.kt similarity index 89% rename from app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/NetworkConnectionInterceptor.kt rename to app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/interceptors/NetworkConnectionInterceptor.kt index f7b37b4..089d8ec 100644 --- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/NetworkConnectionInterceptor.kt +++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/interceptors/NetworkConnectionInterceptor.kt @@ -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 { diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/QueryInterceptor.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/interceptors/QueryInterceptor.kt similarity index 73% rename from app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/QueryInterceptor.kt rename to app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/interceptors/QueryInterceptor.kt index 5bd4f35..8927eb8 100644 --- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/QueryInterceptor.kt +++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/interceptors/QueryInterceptor.kt @@ -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 diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/prefs/PreferenceProvider.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/prefs/PreferenceProvider.kt index ce66276..be5b979 100644 --- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/prefs/PreferenceProvider.kt +++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/prefs/PreferenceProvider.kt @@ -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 { - 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 { + val fromString = getWidgetConversionString(appWidgetId, CURRENCY_ONE) + val toString = getWidgetConversionString(appWidgetId, CURRENCY_TWO) + + return Pair(fromString, toString) } - fun getWidgetConversionPair(id: Int): Pair { - 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/repository/Repository.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/repository/Repository.kt index c134440..907ed58 100644 --- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/repository/Repository.kt +++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/repository/Repository.kt @@ -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 - fun setConversionPair(s1: String, s2: String) + fun setConversionPair(fromCurrency: String, toCurrency: String) fun getArrayList(): Array - fun getWidgetConversionPairs(id: Int): Pair + fun getWidgetConversionPairs(appWidgetId: Int): Pair - fun setWidgetConversionPairs(s1: String, s2: String, id: Int) + fun setWidgetConversionPairs(fromCurrency: String, toCurrency: String, appWidgetId: Int) fun removeWidgetConversionPairs(id: Int) diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/repository/RepositoryImpl.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/repository/RepositoryImpl.kt index 3f603cf..659784f 100644 --- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/repository/RepositoryImpl.kt +++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/repository/RepositoryImpl.kt @@ -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 { 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 = appContext.resources.getStringArray(R.array.currency_arrays) - override fun getWidgetConversionPairs(id: Int): Pair = - prefs.getWidgetConversionPair(id) + override fun getWidgetConversionPairs(appWidgetId: Int): Pair = + 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) diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainViewModel.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainViewModel.kt index 4ce454e..96d277f 100644 --- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainViewModel.kt +++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainViewModel.kt @@ -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() - val operationFailed = MutableLiveData() - + val operationSuccess = MutableLiveData>() val currencyRate = MutableLiveData() 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() diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/utils/PrimitiveUtils.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/utils/PrimitiveUtils.kt index 085291d..0eab21e 100644 --- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/utils/PrimitiveUtils.kt +++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/utils/PrimitiveUtils.kt @@ -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()}" \ No newline at end of file + "${s1.trimToThree()}_${s2.trimToThree()}" + +fun Double.toTwoDp() = run { + val df = DecimalFormat("#.##") + java.lang.Double.valueOf(df.format(this)) +} \ No newline at end of file diff --git a/app/src/test/java/com/appttude/h_mal/easycc/repository/RepositoryStorageTest.kt b/app/src/test/java/com/appttude/h_mal/easycc/repository/RepositoryStorageTest.kt index 4efce75..d6ee5a6 100644 --- a/app/src/test/java/com/appttude/h_mal/easycc/repository/RepositoryStorageTest.kt +++ b/app/src/test/java/com/appttude/h_mal/easycc/repository/RepositoryStorageTest.kt @@ -45,7 +45,7 @@ class RepositoryStorageTest { } @Test - fun saveAndRetrieveCredentials_PositiveResponse() { + fun saveAndRetrieveWidgetPairs_PositiveResponse() { //GIVEN val s1 = "AUD - Australian Dollar" val s2 = "GBP - British Pound"