mirror of
https://github.com/hmalik144/EasyCC_Master.git
synced 2026-01-31 02:41:47 +00:00
Unit tests added
Query interceptor added Unit tests created - Repository test network - Repository test storage
This commit is contained in:
40
.idea/assetWizardSettings.xml
generated
40
.idea/assetWizardSettings.xml
generated
@@ -3,11 +3,49 @@
|
||||
<component name="WizardSettings">
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="vectorWizard">
|
||||
<entry key="imageWizard">
|
||||
<value>
|
||||
<PersistentState />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="vectorWizard">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="vectorAssetStep">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="clipartAsset">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="url" value="jar:file:/C:/Program%20Files/Android/Android%20Studio/plugins/android/lib/android.jar!/images/material_design_icons/navigation/ic_refresh_black_24dp.xml" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="ffffff" />
|
||||
<entry key="outputName" value="ic_refresh_white_24dp" />
|
||||
<entry key="sourceFile" value="C:\Users\h_mal" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
Binary file not shown.
@@ -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"
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
android:anyDensity="true" />
|
||||
|
||||
<application
|
||||
android:name=".mvvm.AppClass"
|
||||
android:name=".mvvm.application.AppClass"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/currency_kotlin_app_widget_info" />
|
||||
android:resource="@xml/currency_app_widget_info" />
|
||||
</receiver>
|
||||
|
||||
<activity android:name="com.appttude.h_mal.easycc.mvvm.ui.widget.CurrencyAppWidgetConfigureActivityKotlin">
|
||||
@@ -42,23 +42,7 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".legacy.MainActivityJava"></activity>
|
||||
|
||||
<receiver android:name=".legacy.CurrencyAppWidget">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/currency_app_widget_info" />
|
||||
</receiver>
|
||||
|
||||
<activity android:name=".legacy.CurrencyAppWidgetConfigureActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -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()) }
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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<String?> {
|
||||
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<String> = appContext.resources.getStringArray(R.array.currency_arrays)
|
||||
|
||||
fun getWidgetConversionPairs(id: Int): List<String?> = prefs.getWidgetConversionPair(id)
|
||||
|
||||
fun setWidgetConversionPairs(s1: String, s2: String, id: Int) = prefs.saveWidgetConversionPair(s1, s2, id)
|
||||
|
||||
fun removeWidgetConversionPairs(id: Int) = prefs.removeWidgetConversion(id)
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<T: Any> apiRequest(call: suspend () -> Response<T>) : T{
|
||||
suspend fun <T : Any> responseUnwrap(
|
||||
call: suspend () -> Response<T>
|
||||
): 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 <T> errorMessage(errorResponse: Response<T>): 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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<ResponseObject>
|
||||
suspend fun getCurrencyRate(@Query("q") currency: String): Response<ResponseObject>
|
||||
|
||||
}
|
||||
@@ -7,5 +7,5 @@ class ResponseObject(
|
||||
@SerializedName("query")
|
||||
var query : Any,
|
||||
@SerializedName("results")
|
||||
var results : Map<String, CurrencyObject>
|
||||
var results : Map<String, CurrencyObject>?
|
||||
)
|
||||
@@ -32,11 +32,11 @@ class PreferenceProvider(
|
||||
).apply()
|
||||
}
|
||||
|
||||
fun getConversionPair(): List<String?> {
|
||||
fun getConversionPair(): Pair<String?, String?> {
|
||||
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<String?> {
|
||||
fun getWidgetConversionPair(id: Int): Pair<String?, String?> {
|
||||
val s1 = getWidgetLastConversionOne(id)
|
||||
val s2 = getWidgetLastConversionTwo(id)
|
||||
|
||||
return listOf(s1,s2)
|
||||
return Pair(s1, s2)
|
||||
}
|
||||
|
||||
private fun getWidgetLastConversionOne(id: Int): String? {
|
||||
|
||||
@@ -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<String?, String?>
|
||||
|
||||
fun setConversionPair(s1: String, s2: String)
|
||||
|
||||
fun getArrayList(): Array<String>
|
||||
|
||||
fun getWidgetConversionPairs(id: Int): Pair<String?, String?>
|
||||
|
||||
fun setWidgetConversionPairs(s1: String, s2: String, id: Int)
|
||||
|
||||
fun removeWidgetConversionPairs(id: Int)
|
||||
|
||||
}
|
||||
@@ -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<String?, String?> {
|
||||
return prefs.getConversionPair()
|
||||
}
|
||||
|
||||
override fun setConversionPair(s1: String, s2: String){
|
||||
prefs.saveConversionPair(s1, s2)
|
||||
}
|
||||
|
||||
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 setWidgetConversionPairs(s1: String, s2: String, id: Int) =
|
||||
prefs.saveWidgetConversionPair(s1, s2, id)
|
||||
|
||||
override fun removeWidgetConversionPairs(id: Int) =
|
||||
prefs.removeWidgetConversion(id)
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<Boolean>()
|
||||
val operationFailed = MutableLiveData<String>()
|
||||
|
||||
val currencyRate = MutableLiveData<Double>()
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
|
||||
@@ -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()}"
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
5
app/src/main/res/drawable/ic_refresh_white_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_refresh_white_24dp.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
|
||||
</vector>
|
||||
@@ -15,15 +15,17 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="6dp"
|
||||
android:layout_margin="12dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/confirm_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:layout_margin="8dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="36dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="Create widget for AUDGBP?"
|
||||
android:textColor="@color/colour_five" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exchangeName"
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:layout_weight="1"
|
||||
android:autoSizeMaxTextSize="100sp"
|
||||
android:autoSizeMinTextSize="8sp"
|
||||
android:autoSizeStepGranularity="2sp"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:gravity="center"
|
||||
android:textColor="#ffffff"
|
||||
tools:text="AUDGBP" />
|
||||
android:layout_marginBottom="3dp">
|
||||
<TextView
|
||||
android:id="@+id/exchangeName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:autoSizeMaxTextSize="100sp"
|
||||
android:autoSizeMinTextSize="6sp"
|
||||
android:autoSizeStepGranularity="2sp"
|
||||
android:autoSizeTextType="uniform"
|
||||
android:textColor="#ffffff"
|
||||
android:text="Rate not set"
|
||||
tools:text="AUDGBP" />
|
||||
<ImageView
|
||||
android:id="@+id/refresh_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:src="@drawable/ic_refresh_white_24dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignBottom="@id/exchangeName"
|
||||
android:adjustViewBounds="true"/>
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exchangeRate"
|
||||
@@ -32,5 +47,5 @@
|
||||
android:gravity="center"
|
||||
android:textColor="#ffffff"
|
||||
android:textStyle="bold"
|
||||
tools:text="0.56" />
|
||||
tools:text="0.526462" />
|
||||
</LinearLayout>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:configure="com.appttude.h_mal.easycc.legacy.CurrencyAppWidgetConfigureActivity"
|
||||
android:configure="com.appttude.h_mal.easycc.mvvm.ui.widget.CurrencyAppWidgetConfigureActivityKotlin"
|
||||
android:initialKeyguardLayout="@layout/currency_app_widget"
|
||||
android:initialLayout="@layout/currency_app_widget"
|
||||
android:minHeight="40dp"
|
||||
@@ -9,7 +9,7 @@
|
||||
android:minResizeWidth="40dp"
|
||||
android:previewImage="@drawable/easyycc_widget_preview"
|
||||
android:resizeMode="horizontal"
|
||||
android:updatePeriodMillis="86400000"
|
||||
android:updatePeriodMillis="21600000"
|
||||
android:widgetCategory="home_screen">
|
||||
|
||||
</appwidget-provider>
|
||||
@@ -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"></appwidget-provider>
|
||||
android:updatePeriodMillis="3600000"
|
||||
android:widgetCategory="home_screen|keyguard"/>
|
||||
@@ -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<String>(mockBody, mockRaw)
|
||||
|
||||
//WHEN
|
||||
Mockito.`when`(api.getCurrencyRate(query)).thenAnswer { re }
|
||||
|
||||
//THEN - assert exception is not null
|
||||
val ioExceptionReturned = assertFailsWith<IOException> {
|
||||
repository.getData(s1, s2)
|
||||
}
|
||||
assertNotNull(ioExceptionReturned)
|
||||
assertNotNull(ioExceptionReturned.message)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user