diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml
index 6b96cf0..7944245 100644
--- a/.idea/assetWizardSettings.xml
+++ b/.idea/assetWizardSettings.xml
@@ -3,11 +3,49 @@
diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser
index ff025b7..f449c57 100644
Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ
diff --git a/app/build.gradle b/app/build.gradle
index b5f715d..fe82ac8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -53,6 +53,7 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
//Retrofit and GSON
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
@@ -72,6 +73,10 @@ dependencies {
implementation "org.kodein.di:kodein-di-generic-jvm:6.2.1"
implementation "org.kodein.di:kodein-di-framework-android-x:6.2.1"
+ //mockito and livedata testing
+ testImplementation 'org.mockito:mockito-inline:2.13.0'
+ testImplementation 'androidx.arch.core:core-testing:2.1.0'
+
//Android Room
implementation "androidx.room:room-runtime:2.2.0-rc01"
implementation "androidx.room:room-ktx:2.2.0-rc01"
@@ -83,5 +88,7 @@ dependencies {
implementation "androidx.preference:preference-ktx:1.1.0"
-
+ //mock websever for testing retrofit responses
+ testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
+ testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4287140..6d243cc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -13,7 +13,7 @@
android:anyDensity="true" />
+ android:resource="@xml/currency_app_widget_info" />
@@ -42,23 +42,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/AppClass.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/application/AppClass.kt
similarity index 62%
rename from app/src/main/java/com/appttude/h_mal/easycc/mvvm/AppClass.kt
rename to app/src/main/java/com/appttude/h_mal/easycc/mvvm/application/AppClass.kt
index 4997ebb..1e9e0b8 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/AppClass.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/application/AppClass.kt
@@ -1,9 +1,10 @@
-package com.appttude.h_mal.easycc.mvvm
+package com.appttude.h_mal.easycc.mvvm.application
import android.app.Application
-import com.appttude.h_mal.easycc.mvvm.data.Repository.Repository
+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.api.GetData
+import com.appttude.h_mal.easycc.mvvm.data.network.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
import com.appttude.h_mal.easycc.mvvm.ui.widget.WidgetViewModelFactory
@@ -15,19 +16,18 @@ import org.kodein.di.generic.instance
import org.kodein.di.generic.provider
import org.kodein.di.generic.singleton
-class AppClass : Application(), KodeinAware{
+class AppClass : Application(), KodeinAware {
- override val kodein = Kodein.lazy {
+ override val kodein by Kodein.lazy {
import(androidXModule(this@AppClass))
bind() from singleton { NetworkConnectionInterceptor(instance()) }
- bind() from singleton { GetData(instance()) }
+ bind() from singleton { QueryInterceptor() }
+ bind() from singleton { CurrencyApi(instance(),instance()) }
bind() from singleton { PreferenceProvider(instance()) }
- bind() from singleton { Repository(instance(), instance(), instance()) }
+ bind() from singleton { RepositoryImpl(instance(), instance(), instance()) }
bind() from provider { MainViewModelFactory(instance()) }
bind() from provider { WidgetViewModelFactory(instance()) }
}
-
-
}
\ 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
deleted file mode 100644
index 1dd5fc1..0000000
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/Repository/Repository.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-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
-import com.appttude.h_mal.easycc.mvvm.data.network.SafeApiRequest
-import com.appttude.h_mal.easycc.mvvm.data.network.api.GetData
-
-class Repository (
- private val api: GetData,
- private val prefs: PreferenceProvider,
- context: Context
-): SafeApiRequest(){
-
- var ccApiKey = BuildConfig.CC_API_KEY
-
- private val appContext = context.applicationContext
-
- suspend fun getData(s1: String, s2: String): ResponseObject?{
- return apiRequest{ api.getCurrencyRate(convertPairsListToString(s1, s2),ccApiKey)}
- }
-
- fun getConversionPair(): List {
- return prefs.getConversionPair()
- }
-
- fun setConversionPair(s1: String, s2: String){
- prefs.saveConversionPair(s1, s2)
- }
-
- private fun convertPairsListToString(s1: String, s2: String): String = "${s1.substring(0,3)}_${s2.substring(0,3)}"
-
- fun getArrayList(): Array = appContext.resources.getStringArray(R.array.currency_arrays)
-
- fun getWidgetConversionPairs(id: Int): List = prefs.getWidgetConversionPair(id)
-
- fun setWidgetConversionPairs(s1: String, s2: String, id: Int) = prefs.saveWidgetConversionPair(s1, s2, id)
-
- fun removeWidgetConversionPairs(id: Int) = prefs.removeWidgetConversion(id)
-
-}
\ No newline at end of file
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/QueryInterceptor.kt
new file mode 100644
index 0000000..5bd4f35
--- /dev/null
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/QueryInterceptor.kt
@@ -0,0 +1,30 @@
+package com.appttude.h_mal.easycc.mvvm.data.network
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
+import okhttp3.HttpUrl
+import okhttp3.Interceptor
+import okhttp3.Request
+import okhttp3.Response
+
+
+class QueryInterceptor() : Interceptor {
+
+ override fun intercept(chain: Interceptor.Chain): Response {
+ val original: Request = chain.request()
+ val originalHttpUrl: HttpUrl = original.url()
+
+ val url = originalHttpUrl.newBuilder()
+ .addQueryParameter("apikey", "a4f93cc2ff05dd772321")
+ .build()
+
+ // Add amended Url back to request
+ val requestBuilder: Request.Builder = original.newBuilder()
+ .url(url)
+
+ val request: Request = requestBuilder.build()
+ return chain.proceed(request)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/SafeApiRequest.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/SafeApiRequest.kt
index 78a6a2e..fc949eb 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/SafeApiRequest.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/SafeApiRequest.kt
@@ -1,29 +1,67 @@
package com.appttude.h_mal.easycc.mvvm.data.network
+import android.util.Log
import org.json.JSONException
import org.json.JSONObject
import retrofit2.Response
import java.io.IOException
+/**
+ * This abstract class extract objects from Retrofit [Response]
+ * or throws IOException if object does not exist
+ */
+private const val TAG = "SafeApiRequest"
abstract class SafeApiRequest {
- suspend fun apiRequest(call: suspend () -> Response) : T{
+ suspend fun responseUnwrap(
+ call: suspend () -> Response
+ ): T {
val response = call.invoke()
- if(response.isSuccessful){
- return response.body()!!
- }else{
- val error = response.errorBody()?.string()
- val message = StringBuilder()
- error?.let{
- try{
- message.append(JSONObject(it).getString("error"))
- }catch(e: JSONException){ }
- message.append("\n")
- }
- message.append("Error Code: ${response.code()}")
- throw IOException(message.toString())
+ if (response.isSuccessful) {
+ // return the object within the response body
+ return response.body()!!
+ } else {
+ // the response was unsuccessful
+ // throw IOException error
+ throw IOException(errorMessage(response))
}
}
+ private fun errorMessage(errorResponse: Response): String {
+ val errorBody = errorResponse.errorBody()?.string()
+ val errorCode = "Error Code: ${errorResponse.code()}"
+ val errorMessageString = errorBody.getError()
+
+ //build a log message to log in console
+ val log = if (errorMessageString.isNullOrEmpty()){
+ errorCode
+ }else{
+ StringBuilder()
+ .append(errorCode)
+ .append("\n")
+ .append(errorMessageString)
+ .toString()
+ }
+ Log.e("Api Response Error", log)
+
+ //return error message
+ //if null return error code
+ return errorMessageString ?: errorCode
+ }
+
+ @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
+ private fun String?.getError(): String? {
+ this?.let {
+ try {
+ //convert response to JSON
+ //extract ["error"] from error body
+ return JSONObject(it).getString("error")
+ } catch (e: JSONException) {
+ Log.e(TAG, e.message)
+ }
+ }
+ return null
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/api/GetData.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/api/CurrencyApi.kt
similarity index 72%
rename from app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/api/GetData.kt
rename to app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/api/CurrencyApi.kt
index 1952650..097eab1 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/api/GetData.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/api/CurrencyApi.kt
@@ -2,6 +2,7 @@ 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 okhttp3.OkHttpClient
import retrofit2.Response
import retrofit2.Retrofit
@@ -10,15 +11,17 @@ import retrofit2.http.GET
import retrofit2.http.Query
-interface GetData {
+interface CurrencyApi {
companion object{
operator fun invoke(
- networkConnectionInterceptor: NetworkConnectionInterceptor
- ) : GetData{
+ networkConnectionInterceptor: NetworkConnectionInterceptor,
+ queryInterceptor: QueryInterceptor
+ ) : CurrencyApi{
val okkHttpclient = OkHttpClient.Builder()
.addNetworkInterceptor(networkConnectionInterceptor)
+ .addInterceptor(queryInterceptor)
.build()
return Retrofit.Builder()
@@ -26,11 +29,11 @@ interface GetData {
.baseUrl("https://free.currencyconverterapi.com/api/v3/")
.addConverterFactory(GsonConverterFactory.create())
.build()
- .create(GetData::class.java)
+ .create(CurrencyApi::class.java)
}
}
@GET("convert?")
- suspend fun getCurrencyRate(@Query("q") currency: String, @Query("apiKey") api: String): Response
+ suspend fun getCurrencyRate(@Query("q") currency: String): Response
}
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/response/ResponseObject.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/response/ResponseObject.kt
index 2724399..6116763 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/response/ResponseObject.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/network/response/ResponseObject.kt
@@ -7,5 +7,5 @@ class ResponseObject(
@SerializedName("query")
var query : Any,
@SerializedName("results")
- var results : Map
+ var results : Map?
)
\ No newline at end of file
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 e609298..ce66276 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
@@ -32,11 +32,11 @@ class PreferenceProvider(
).apply()
}
- fun getConversionPair(): List {
+ fun getConversionPair(): Pair {
val s1 = getLastConversionOne()
val s2 = getLastConversionTwo()
- return listOf(s1,s2)
+ return Pair(s1,s2)
}
private fun getLastConversionOne(): String? {
@@ -57,11 +57,11 @@ class PreferenceProvider(
).apply()
}
- fun getWidgetConversionPair(id: Int): List {
+ fun getWidgetConversionPair(id: Int): Pair {
val s1 = getWidgetLastConversionOne(id)
val s2 = getWidgetLastConversionTwo(id)
- return listOf(s1,s2)
+ return Pair(s1, s2)
}
private fun getWidgetLastConversionOne(id: Int): String? {
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
new file mode 100644
index 0000000..c134440
--- /dev/null
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/repository/Repository.kt
@@ -0,0 +1,23 @@
+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
+
+interface Repository {
+
+ suspend fun getData(s1: String, s2: String): ResponseObject
+
+ fun getConversionPair(): Pair
+
+ fun setConversionPair(s1: String, s2: String)
+
+ fun getArrayList(): Array
+
+ fun getWidgetConversionPairs(id: Int): Pair
+
+ fun setWidgetConversionPairs(s1: String, s2: String, id: Int)
+
+ fun removeWidgetConversionPairs(id: Int)
+
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..3f603cf
--- /dev/null
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/data/repository/RepositoryImpl.kt
@@ -0,0 +1,48 @@
+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
+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
+
+
+class RepositoryImpl (
+ private val api: CurrencyApi,
+ private val prefs: PreferenceProvider,
+ context: Context
+):Repository, SafeApiRequest(){
+
+ private val appContext = context.applicationContext
+
+ override suspend fun getData(s1: String, s2: String
+ ): ResponseObject{
+ val currencyPair = convertPairsListToString(s1, s2)
+ 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 getArrayList(): Array =
+ appContext.resources.getStringArray(R.array.currency_arrays)
+
+ override fun getWidgetConversionPairs(id: Int): Pair =
+ prefs.getWidgetConversionPair(id)
+
+ override fun setWidgetConversionPairs(s1: String, s2: String, id: Int) =
+ prefs.saveWidgetConversionPair(s1, s2, id)
+
+ override fun removeWidgetConversionPairs(id: Int) =
+ prefs.removeWidgetConversion(id)
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainActivity.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainActivity.kt
index 1d9e2a5..e8d742c 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainActivity.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainActivity.kt
@@ -7,15 +7,14 @@ import android.util.Log
import android.view.View
import android.view.WindowManager
import android.widget.TextView
-import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProviders
import com.appttude.h_mal.easycc.R
import com.appttude.h_mal.easycc.databinding.ActivityMainBinding
-import com.appttude.h_mal.easycc.utils.DisplayToast
-import com.appttude.h_mal.easycc.utils.clearEditText
-import com.appttude.h_mal.easycc.utils.hideView
+import com.appttude.h_mal.easycc.mvvm.utils.DisplayToast
+import com.appttude.h_mal.easycc.mvvm.utils.clearEditText
+import com.appttude.h_mal.easycc.mvvm.utils.hideView
import kotlinx.android.synthetic.main.activity_main.*
import org.kodein.di.KodeinAware
import org.kodein.di.android.kodein
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 a01a61e..4ce454e 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
@@ -3,13 +3,12 @@ package com.appttude.h_mal.easycc.mvvm.ui.app
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.data.repository.Repository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.IOException
import java.text.DecimalFormat
-import java.text.NumberFormat
class MainViewModel(
private val repository: Repository
@@ -18,14 +17,20 @@ class MainViewModel(
private val defaultValue by lazy { repository.getArrayList()[0] }
private val conversionPairs by lazy { repository.getConversionPair() }
- var rateIdFrom: String? = conversionPairs[0] ?: defaultValue
- var rateIdTo: String? = conversionPairs[1] ?: defaultValue
+ var rateIdFrom: String? = conversionPairs.first ?: defaultValue
+ var rateIdTo: String? = conversionPairs.second ?: defaultValue
var topVal: String? = null
var bottomVal: String? = null
var rateListener: RateListener? = null
+ //operation results livedata based on outcome of operation
+ val operationSuccess = MutableLiveData()
+ val operationFailed = MutableLiveData()
+
+ val currencyRate = MutableLiveData()
+
private var conversionRate: Double = 0.00
fun getExchangeRate(){
@@ -40,15 +45,17 @@ class MainViewModel(
val exchangeResponse = repository.getData(rateIdFrom!!, rateIdTo!!)
repository.setConversionPair(rateIdFrom!!, rateIdTo!!)
- exchangeResponse?.results?.iterator()?.next()?.value?.let {
+ exchangeResponse.results?.iterator()?.next()?.value?.let {
rateListener?.onSuccess()
conversionRate = it.value
return@launch
}
- rateListener?.onFailure("Failed to retrieve rate")
+
}catch(e: IOException){
- rateListener?.onFailure(e.message!!)
+ rateListener?.onFailure(e.message ?: "Currency Retrieval failed")
+ return@launch
}
+ rateListener?.onFailure("Failed to retrieve rate")
}
}
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainViewModelFactory.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainViewModelFactory.kt
index 1eb26b6..49766a4 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainViewModelFactory.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/app/MainViewModelFactory.kt
@@ -2,11 +2,11 @@ package com.appttude.h_mal.easycc.mvvm.ui.app
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
-import com.appttude.h_mal.easycc.mvvm.data.Repository.Repository
+import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
@Suppress("UNCHECKED_CAST")
class MainViewModelFactory (
- private val repository: Repository
+ private val repository: RepositoryImpl
): ViewModelProvider.NewInstanceFactory(){
override fun create(modelClass: Class): T {
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/CurrencyAppWidgetConfigureActivityKotlin.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/CurrencyAppWidgetConfigureActivityKotlin.kt
index 52de88c..7a157a2 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/CurrencyAppWidgetConfigureActivityKotlin.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/CurrencyAppWidgetConfigureActivityKotlin.kt
@@ -8,7 +8,7 @@ import androidx.lifecycle.ViewModelProviders
import com.appttude.h_mal.easycc.R
import com.appttude.h_mal.easycc.databinding.CurrencyAppWidgetConfigureBinding
import com.appttude.h_mal.easycc.mvvm.ui.app.RateListener
-import com.appttude.h_mal.easycc.utils.DisplayToast
+import com.appttude.h_mal.easycc.mvvm.utils.DisplayToast
import org.kodein.di.KodeinAware
import org.kodein.di.android.kodein
import org.kodein.di.generic.instance
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/CurrencyAppWidgetKotlin.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/CurrencyAppWidgetKotlin.kt
index 6b99f48..5cf7fed 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/CurrencyAppWidgetKotlin.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/CurrencyAppWidgetKotlin.kt
@@ -3,16 +3,16 @@ package com.appttude.h_mal.easycc.mvvm.ui.widget
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
+import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.util.Log
import android.widget.RemoteViews
import android.widget.Toast
-import com.appttude.h_mal.easycc.legacy.MainActivityJava
import com.appttude.h_mal.easycc.R
-import com.appttude.h_mal.easycc.mvvm.data.Repository.Repository
-import com.appttude.h_mal.easycc.mvvm.data.network.NetworkConnectionInterceptor
-import com.appttude.h_mal.easycc.mvvm.data.network.api.GetData
-import com.appttude.h_mal.easycc.mvvm.data.prefs.PreferenceProvider
+import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
+import com.appttude.h_mal.easycc.mvvm.ui.app.MainActivity
+import com.appttude.h_mal.easycc.mvvm.utils.transformIntToArray
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -21,34 +21,51 @@ import org.kodein.di.LateInitKodein
import org.kodein.di.generic.instance
import java.io.IOException
+
/**
* Implementation of App Widget functionality.
* App Widget Configuration implemented in [CurrencyAppWidgetConfigureActivityKotlin]
*/
+private const val TAG = "CurrencyAppWidgetKotlin"
class CurrencyAppWidgetKotlin : AppWidgetProvider() {
+ //DI with kodein to use in CurrencyAppWidgetKotlin
private val kodein = LateInitKodein()
- private val repository : Repository by kodein.instance()
+ private val repository : RepositoryImpl by kodein.instance()
+ //update trigger either on timed update or from from first start
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
- kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
+ Log.i(TAG,"onUpdate() appWidgetIds = ${appWidgetIds.size}")
// There may be multiple widgets active, so update all of them
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
+ super.onUpdate(context, appWidgetManager, appWidgetIds)
}
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
- kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
// When the user deletes the widget, delete the preference associated with it.
for (appWidgetId in appWidgetIds) {
repository.removeWidgetConversionPairs(appWidgetId)
}
+ super.onDeleted(context, appWidgetIds)
}
override fun onEnabled(context: Context) {
- kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
// Enter relevant functionality for when the first widget is created
+ AppWidgetManager.getInstance(context).apply {
+ val thisAppWidget = ComponentName(context.packageName, CurrencyAppWidgetKotlin::class.java.name)
+ val appWidgetIds = getAppWidgetIds(thisAppWidget)
+ onUpdate(context, this, appWidgetIds)
+ }
+ super.onEnabled(context)
+ }
+
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (context == null){ return }
+ kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
+
+ super.onReceive(context, intent)
}
override fun onDisabled(context: Context) {
@@ -57,58 +74,75 @@ class CurrencyAppWidgetKotlin : AppWidgetProvider() {
}
- fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
- //todo: get value from repository
+ private fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
val stringList = repository.getWidgetConversionPairs(appWidgetId)
- val s1 = stringList[0]
- val s2 = stringList[1]
+ val s1 = stringList.first
+ val s2 = stringList.second
// Construct the RemoteViews object
val views = RemoteViews(context.packageName, R.layout.currency_app_widget)
- views.setTextViewText(R.id.exchangeName, "Rates")
- views.setTextViewText(R.id.exchangeRate, "not set")
- //todo: async task to get rate
CoroutineScope(Dispatchers.Main).launch {
try {
val response = repository.getData(s1!!.substring(0,3),s2!!.substring(0,3))
- response?.results?.iterator()?.next()?.value?.let {
+ response.results?.iterator()?.next()?.value?.let {
val titleString = "${it.fr}${it.to}"
views.setTextViewText(R.id.exchangeName, titleString)
views.setTextViewText(R.id.exchangeRate, it.value.toString())
}
}catch (io : IOException){
+ Log.i("WidgetClass",io.message ?: "Failed")
Toast.makeText(context,io.message, Toast.LENGTH_LONG).show()
}finally {
- // Instruct the widget manager to update the widget
- appWidgetManager.updateAppWidget(appWidgetId, views)
-
- val opacity = 0.3f //opacity = 0: fully transparent, opacity = 1: no transparancy
- val backgroundColor = 0x000000 //background color (here black)
-
- views.setInt(R.id.widget_view, "setBackgroundColor", (opacity * 0xFF).toInt() shl 24 or backgroundColor)
-
- val clickIntentTemplate = Intent(context, MainActivityJava::class.java).apply {
- action = Intent.ACTION_MAIN
- addCategory(Intent.CATEGORY_LAUNCHER)
- putExtra("parse_1", s1)
- putExtra("parse_2", s2)
- addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ setUpdateIntent(context, appWidgetId).let {
+ //set the pending intent to the icon
+ views.setImageViewResource(R.id.refresh_icon, R.drawable.ic_refresh_white_24dp)
+ views.setOnClickPendingIntent(R.id.refresh_icon, it)
}
- val configPendingIntent = PendingIntent.getActivity(context, 0, clickIntentTemplate, PendingIntent.FLAG_UPDATE_CURRENT)
+ val clickIntentTemplate = clickingIntent(context, s1, s2)
+
+ val configPendingIntent =
+ PendingIntent.getActivity(
+ context, appWidgetId, clickIntentTemplate,
+ PendingIntent.FLAG_UPDATE_CURRENT)
+
views.setOnClickPendingIntent(R.id.widget_view, configPendingIntent)
+
+ // Instruct the widget manager to update the widget
+ appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
- fun setupRepository(context: Context): Repository {
- val networkInterceptor = NetworkConnectionInterceptor(context)
- val getData = GetData(networkInterceptor)
- val prefs = PreferenceProvider(context)
- return Repository(getData,prefs,context)
+ private fun setUpdateIntent(context: Context, appWidgetId: Int): PendingIntent? {
+ //Create update intent for refresh icon
+ val updateIntent = Intent(
+ context, CurrencyAppWidgetKotlin::class.java).apply {
+ action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
+ putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, transformIntToArray(appWidgetId))
+ }
+ //add previous intent to this pending intent
+ return PendingIntent.getBroadcast(
+ context,
+ appWidgetId,
+ updateIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT)
+ }
+
+ private fun clickingIntent(
+ context: Context,
+ s1: String?, s2: String?
+ ): Intent {
+ return Intent(context, MainActivity::class.java).apply {
+ action = Intent.ACTION_MAIN
+ addCategory(Intent.CATEGORY_LAUNCHER)
+ putExtra("parse_1", s1)
+ putExtra("parse_2", s2)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ }
}
}
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetSubmitDialog.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetSubmitDialog.kt
index 4b2d8e8..e8f84de 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetSubmitDialog.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetSubmitDialog.kt
@@ -6,11 +6,13 @@ import android.appwidget.AppWidgetManager
import android.content.Intent
import android.os.Bundle
import com.appttude.h_mal.easycc.R
+import com.appttude.h_mal.easycc.mvvm.utils.transformIntToArray
import kotlinx.android.synthetic.main.confirm_dialog.*
-/*
-widget for when submitting the completed selections
+/**
+ * Dialog created when submitting the completed selections
+ * in [CurrencyAppWidgetConfigureActivityKotlin]
*/
class WidgetSubmitDialog(
private val activity: Activity,
@@ -21,26 +23,32 @@ class WidgetSubmitDialog(
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.confirm_dialog)
-
-// requestWindowFeature(Window.FEATURE_NO_TITLE)
+ // layer behind dialog to be transparent
window!!.setBackgroundDrawableResource(android.R.color.transparent)
+ // Dialog cannot be cancelled by clicking away
setCancelable(false)
- //todo: amend widget text
confirm_text.text = StringBuilder().append("Create widget for ")
.append(viewModel.getWidgetStringName())
.append("?").toString()
confirm_yes.setOnClickListener {
- viewModel.setWidgetStored()
+ // It is the responsibility of the configuration activity to update the app widget
+ // Send update broadcast to widget app class
+ Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE,
+ null,
+ context,
+ CurrencyAppWidgetKotlin::class.java).apply {
+ // Save current widget pairs
+ viewModel.setWidgetStored()
+ // Put current app widget ID into extras and send broadcast
+ putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, transformIntToArray(appWidgetId) )
+ activity.sendBroadcast(this)
+ }
- val intent = Intent(context, CurrencyAppWidgetKotlin::class.java)
- intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, IntArray(appWidgetId))
- context.sendBroadcast(intent)
// Make sure we pass back the original appWidgetId
- val resultValue = Intent()
+ val resultValue = activity.intent
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
activity.setResult(Activity.RESULT_OK, resultValue)
activity.finish()
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetViewModel.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetViewModel.kt
index 6d1fbe3..8576a2e 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetViewModel.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetViewModel.kt
@@ -4,11 +4,11 @@ import android.view.View
import android.widget.Toast
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.data.repository.RepositoryImpl
import com.appttude.h_mal.easycc.mvvm.ui.app.RateListener
class WidgetViewModel(
- private val repository: Repository
+ private val repository: RepositoryImpl
) : ViewModel(){
var rateListener: RateListener? = null
@@ -22,10 +22,9 @@ class WidgetViewModel(
appWidgetId = appId
val widgetString = getWidgetStored(appId)
- if (widgetString.isNotEmpty()){
- rateIdFrom.value = widgetString[0]
- rateIdTo.value = widgetString[1]
- }
+ rateIdFrom.value = widgetString.first
+ rateIdTo.value = widgetString.second
+
}
fun selectCurrencyOnClick(view: View){
@@ -80,15 +79,5 @@ class WidgetViewModel(
private fun String.trimToThree() = this.substring(0,3)
- private fun arrayEntry(s: String?): String? {
- val strings = repository.getArrayList()
- var returnString: String? = strings[0]
- for (string in strings) {
- if (s == string.substring(0, 3)) {
- returnString = string
- }
- }
- return returnString
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetViewModelFactory.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetViewModelFactory.kt
index a04e57a..5d90697 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetViewModelFactory.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/ui/widget/WidgetViewModelFactory.kt
@@ -2,11 +2,11 @@ package com.appttude.h_mal.easycc.mvvm.ui.widget
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
-import com.appttude.h_mal.easycc.mvvm.data.Repository.Repository
+import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
@Suppress("UNCHECKED_CAST")
class WidgetViewModelFactory (
- private val repository: Repository
+ private val repository: RepositoryImpl
): ViewModelProvider.NewInstanceFactory(){
override fun create(modelClass: Class): T {
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
new file mode 100644
index 0000000..085291d
--- /dev/null
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/utils/PrimitiveUtils.kt
@@ -0,0 +1,15 @@
+package com.appttude.h_mal.easycc.mvvm.utils
+
+fun transformIntToArray(int: Int): IntArray{
+ return intArrayOf(int)
+}
+
+fun String.trimToThree(): String{
+ if (this.length > 3){
+ return this.substring(0, 3)
+ }
+ return this
+}
+
+fun convertPairsListToString(s1: String, s2: String): String =
+ "${s1.trimToThree()}_${s2.trimToThree()}"
\ No newline at end of file
diff --git a/app/src/main/java/com/appttude/h_mal/easycc/utils/ViewUtils.kt b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/utils/ViewUtils.kt
similarity index 88%
rename from app/src/main/java/com/appttude/h_mal/easycc/utils/ViewUtils.kt
rename to app/src/main/java/com/appttude/h_mal/easycc/mvvm/utils/ViewUtils.kt
index bb9ab0c..448fd17 100644
--- a/app/src/main/java/com/appttude/h_mal/easycc/utils/ViewUtils.kt
+++ b/app/src/main/java/com/appttude/h_mal/easycc/mvvm/utils/ViewUtils.kt
@@ -1,4 +1,4 @@
-package com.appttude.h_mal.easycc.utils
+package com.appttude.h_mal.easycc.mvvm.utils
import android.content.Context
import android.view.View
@@ -15,4 +15,4 @@ fun View.hideView(vis : Boolean){
fun Context.DisplayToast(message: String){
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
-}
\ No newline at end of file
+}
diff --git a/app/src/main/res/drawable/ic_refresh_white_24dp.xml b/app/src/main/res/drawable/ic_refresh_white_24dp.xml
new file mode 100644
index 0000000..cc2d1e0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_refresh_white_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/confirm_dialog.xml b/app/src/main/res/layout/confirm_dialog.xml
index 1528257..a063840 100644
--- a/app/src/main/res/layout/confirm_dialog.xml
+++ b/app/src/main/res/layout/confirm_dialog.xml
@@ -15,15 +15,17 @@
@@ -37,7 +39,11 @@
android:id="@+id/confirm_yes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_margin="8dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:paddingLeft="12dp"
+ android:paddingRight="12dp"
+ android:textStyle="bold"
android:text="@android:string/yes"
android:textColor="@color/colour_five" />
@@ -46,6 +52,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
+ android:textStyle="bold"
android:text="@android:string/no"
android:textColor="@color/colour_five" />
diff --git a/app/src/main/res/layout/currency_app_widget.xml b/app/src/main/res/layout/currency_app_widget.xml
index 05edee8..42be33e 100644
--- a/app/src/main/res/layout/currency_app_widget.xml
+++ b/app/src/main/res/layout/currency_app_widget.xml
@@ -2,23 +2,38 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/widget_view"
android:layout_width="match_parent"
+ tools:layout_width="110dp"
android:layout_height="72dp"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:background="#4D000000">
-
-
+ android:layout_marginBottom="3dp">
+
+
+
+ tools:text="0.526462" />
\ No newline at end of file
diff --git a/app/src/main/res/xml/currency_app_widget_info.xml b/app/src/main/res/xml/currency_app_widget_info.xml
index b325a13..3d0ca83 100644
--- a/app/src/main/res/xml/currency_app_widget_info.xml
+++ b/app/src/main/res/xml/currency_app_widget_info.xml
@@ -1,6 +1,6 @@
\ No newline at end of file
diff --git a/app/src/main/res/xml/currency_kotlin_app_widget_info.xml b/app/src/main/res/xml/currency_kotlin_app_widget_info.xml
index ec7724a..8094031 100644
--- a/app/src/main/res/xml/currency_kotlin_app_widget_info.xml
+++ b/app/src/main/res/xml/currency_kotlin_app_widget_info.xml
@@ -7,5 +7,5 @@
android:minHeight="40dp"
android:previewImage="@drawable/example_appwidget_preview"
android:resizeMode="horizontal|vertical"
- android:updatePeriodMillis="86400000"
- android:widgetCategory="home_screen|keyguard">
\ No newline at end of file
+ android:updatePeriodMillis="3600000"
+ android:widgetCategory="home_screen|keyguard"/>
\ No newline at end of file
diff --git a/app/src/test/java/com/appttude/h_mal/easycc/repository/RepositoryNetworkTest.kt b/app/src/test/java/com/appttude/h_mal/easycc/repository/RepositoryNetworkTest.kt
new file mode 100644
index 0000000..ee3a4ec
--- /dev/null
+++ b/app/src/test/java/com/appttude/h_mal/easycc/repository/RepositoryNetworkTest.kt
@@ -0,0 +1,86 @@
+package com.appttude.h_mal.easycc.repository
+
+import android.content.Context
+import com.appttude.h_mal.easycc.BuildConfig
+import com.appttude.h_mal.easycc.mvvm.data.network.api.CurrencyApi
+import com.appttude.h_mal.easycc.mvvm.data.network.response.ResponseObject
+import com.appttude.h_mal.easycc.mvvm.data.prefs.PreferenceProvider
+import com.appttude.h_mal.easycc.mvvm.data.repository.Repository
+import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
+import com.appttude.h_mal.easycc.mvvm.utils.convertPairsListToString
+import kotlinx.coroutines.runBlocking
+import okhttp3.ResponseBody
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+import retrofit2.Response
+
+import java.io.IOException
+import kotlin.test.assertFailsWith
+
+
+class RepositoryNetworkTest{
+
+ lateinit var repository: Repository
+
+ @Mock
+ lateinit var api: CurrencyApi
+ @Mock
+ lateinit var prefs: PreferenceProvider
+ @Mock
+ lateinit var context: Context
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ repository = RepositoryImpl(api, prefs, context)
+ }
+
+ @Test
+ fun getRateFromApi_positiveResponse() = runBlocking {
+ //GIVEN - Create query string
+ val s1 = "AUD - Australian Dollar"
+ val s2 = "GBP - British Pound"
+ val query = convertPairsListToString(s1, s2)
+ //create a successful retrofit response
+ val mockCurrencyResponse = mock(ResponseObject::class.java)
+ val re = Response.success(mockCurrencyResponse)
+
+ //WHEN - loginApiRequest to return a successful response
+ Mockito.`when`(api.getCurrencyRate(query)).thenReturn(re)
+
+ //THEN - the unwrapped login response contains the correct values
+ val currencyResponse = repository.getData(s1,s2)
+ assertNotNull(currencyResponse)
+ assertEquals(currencyResponse, mockCurrencyResponse)
+ }
+
+ @Test
+ fun loginUser_negativeResponse() = runBlocking {
+ //GIVEN
+ val s1 = "AUD - Australian Dollar"
+ val s2 = "GBP - British Pound"
+ val query = convertPairsListToString(s1, s2)
+ //mock retrofit error response
+ val mockBody = mock(ResponseBody::class.java)
+ val mockRaw = mock(okhttp3.Response::class.java)
+ val re = Response.error(mockBody, mockRaw)
+
+ //WHEN
+ Mockito.`when`(api.getCurrencyRate(query)).thenAnswer { re }
+
+ //THEN - assert exception is not null
+ val ioExceptionReturned = assertFailsWith {
+ repository.getData(s1, s2)
+ }
+ assertNotNull(ioExceptionReturned)
+ assertNotNull(ioExceptionReturned.message)
+ }
+
+}
\ 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
new file mode 100644
index 0000000..4efce75
--- /dev/null
+++ b/app/src/test/java/com/appttude/h_mal/easycc/repository/RepositoryStorageTest.kt
@@ -0,0 +1,62 @@
+package com.appttude.h_mal.easycc.repository
+
+import android.content.Context
+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.data.repository.Repository
+import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import kotlin.test.assertEquals
+
+class RepositoryStorageTest {
+
+ lateinit var repository: Repository
+
+ @Mock
+ lateinit var api: CurrencyApi
+ @Mock
+ lateinit var prefs: PreferenceProvider
+ @Mock
+ lateinit var context: Context
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ repository = RepositoryImpl(api, prefs, context)
+ }
+
+ @Test
+ fun saveAndRetrieve_PositiveResponse() {
+ //GIVEN
+ val s1 = "AUD - Australian Dollar"
+ val s2 = "GBP - British Pound"
+ val pair = Pair(s1, s2)
+ repository.setConversionPair(s1, s2)
+
+ //WHEN
+ Mockito.`when`(prefs.getConversionPair()).thenReturn(pair)
+
+ //THEN
+ assertEquals(pair, repository.getConversionPair())
+ }
+
+ @Test
+ fun saveAndRetrieveCredentials_PositiveResponse() {
+ //GIVEN
+ val s1 = "AUD - Australian Dollar"
+ val s2 = "GBP - British Pound"
+ val id = 1234
+ val pair = Pair(s1, s2)
+ repository.setWidgetConversionPairs("forename", "Surname", id)
+
+ //WHEN
+ Mockito.`when`(prefs.getWidgetConversionPair(id)).thenReturn(pair)
+
+ //THEN
+ assertEquals(pair, repository.getWidgetConversionPairs(id))
+ }
+}
\ No newline at end of file