mirror of
https://github.com/hmalik144/EasyCC_Master.git
synced 2026-01-31 02:41:47 +00:00
- lint checks done
- added service intent Took 1 hour 7 minutes
This commit is contained in:
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
Binary file not shown.
@@ -22,7 +22,7 @@ import org.kodein.di.generic.singleton
|
||||
|
||||
class AppClass : Application(), KodeinAware {
|
||||
|
||||
// Kodein Dependecy Injection singletons and providers created
|
||||
// KODEIN DI components declaration
|
||||
override val kodein by Kodein.lazy {
|
||||
import(androidXModule(this@AppClass))
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.appttude.h_mal.easycc.data.network
|
||||
|
||||
import android.util.Log
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import retrofit2.Response
|
||||
@@ -10,7 +9,6 @@ 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> responseUnwrap(
|
||||
@@ -45,10 +43,6 @@ abstract class SafeApiRequest {
|
||||
}
|
||||
print(log)
|
||||
|
||||
// Log.e("Api Response Error", log)
|
||||
|
||||
//return error message
|
||||
//if null return error code
|
||||
return errorMessageString ?: errorCode
|
||||
}
|
||||
|
||||
|
||||
@@ -11,18 +11,16 @@ import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
/**
|
||||
* Retrofit2 Network class to create network requests
|
||||
* Retrofit Network class to currency api calls
|
||||
*/
|
||||
interface BackupCurrencyApi {
|
||||
|
||||
// Get rate from server with arguments passed in Repository
|
||||
@GET("latest?")
|
||||
suspend fun getCurrencyRate(
|
||||
@Query("from") currencyFrom: String,
|
||||
@Query("to") currencyTo: String
|
||||
): Response<CurrencyResponse>
|
||||
|
||||
// interface invokation to be used in application class
|
||||
companion object {
|
||||
operator fun invoke(
|
||||
networkConnectionInterceptor: NetworkConnectionInterceptor,
|
||||
@@ -34,7 +32,6 @@ interface BackupCurrencyApi {
|
||||
.addNetworkInterceptor(networkConnectionInterceptor)
|
||||
.build()
|
||||
|
||||
// Build retrofit
|
||||
return Retrofit.Builder()
|
||||
.client(okkHttpclient)
|
||||
.baseUrl("https://api.frankfurter.app/")
|
||||
@@ -44,6 +41,4 @@ interface BackupCurrencyApi {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
/**
|
||||
* Retrofit2 Network class to create network requests
|
||||
* Retrofit Network class to currency api calls
|
||||
*/
|
||||
interface CurrencyApi {
|
||||
|
||||
@@ -28,14 +28,12 @@ interface CurrencyApi {
|
||||
interceptor: HttpLoggingInterceptor
|
||||
): CurrencyApi {
|
||||
|
||||
// okkHttpclient with injected interceptors
|
||||
val okkHttpclient = OkHttpClient.Builder()
|
||||
.addInterceptor(interceptor)
|
||||
.addInterceptor(queryInterceptor)
|
||||
.addNetworkInterceptor(networkConnectionInterceptor)
|
||||
.build()
|
||||
|
||||
// Build retrofit
|
||||
return Retrofit.Builder()
|
||||
.client(okkHttpclient)
|
||||
.baseUrl("https://free.currencyconverterapi.com/api/v3/")
|
||||
|
||||
@@ -3,14 +3,17 @@ package com.appttude.h_mal.easycc.data.network.interceptors
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Interceptor used in [CurrencyApi] to intercept network status
|
||||
* Interceptor used in network classes to check network status
|
||||
*
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
class NetworkConnectionInterceptor(
|
||||
context: Context
|
||||
) : Interceptor {
|
||||
@@ -29,6 +32,7 @@ class NetworkConnectionInterceptor(
|
||||
val connectivityManager =
|
||||
applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
|
||||
connectivityManager?.let {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
it.getNetworkCapabilities(connectivityManager.activeNetwork)?.apply {
|
||||
result = when {
|
||||
hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||
@@ -36,6 +40,16 @@ class NetworkConnectionInterceptor(
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
it.activeNetworkInfo?.run {
|
||||
result = when (type) {
|
||||
ConnectivityManager.TYPE_WIFI -> true
|
||||
ConnectivityManager.TYPE_MOBILE -> true
|
||||
ConnectivityManager.TYPE_ETHERNET -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.appttude.h_mal.easycc.data.network.interceptors
|
||||
|
||||
import android.content.Context
|
||||
import com.appttude.h_mal.easycc.BuildConfig
|
||||
import com.appttude.h_mal.easycc.R
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.Interceptor
|
||||
|
||||
@@ -18,8 +18,8 @@ class PreferenceProvider(context: Context) {
|
||||
private val appContext = context.applicationContext
|
||||
|
||||
// Instance of Shared preferences
|
||||
private val preference: SharedPreferences
|
||||
= PreferenceManager.getDefaultSharedPreferences(appContext)
|
||||
private val preference: SharedPreferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(appContext)
|
||||
|
||||
// Lazy declaration of default rate if no rate is retrieved from
|
||||
private val defaultRate: String by lazy {
|
||||
@@ -50,8 +50,10 @@ class PreferenceProvider(context: Context) {
|
||||
}
|
||||
|
||||
// Save currency pairs for widget
|
||||
fun saveWidgetConversionPair(fromString: String,
|
||||
toString: String, appWidgetId: Int) {
|
||||
fun saveWidgetConversionPair(
|
||||
fromString: String,
|
||||
toString: String, appWidgetId: Int
|
||||
) {
|
||||
|
||||
preference.edit()
|
||||
.putString("${appWidgetId}_$CURRENCY_ONE", fromString)
|
||||
@@ -68,7 +70,8 @@ class PreferenceProvider(context: Context) {
|
||||
}
|
||||
|
||||
private fun getWidgetConversionString(
|
||||
appWidgetId: Int, conversionName: String): String? {
|
||||
appWidgetId: Int, conversionName: String
|
||||
): String? {
|
||||
return preference
|
||||
.getString("${appWidgetId}_$conversionName", defaultRate)
|
||||
}
|
||||
@@ -78,7 +81,6 @@ class PreferenceProvider(context: Context) {
|
||||
.remove("${id}_$CURRENCY_ONE")
|
||||
.remove("${id}_$CURRENCY_TWO")
|
||||
.apply()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,8 +49,10 @@ class RepositoryImpl (
|
||||
override fun getWidgetConversionPairs(appWidgetId: Int): Pair<String?, String?> =
|
||||
prefs.getWidgetConversionPair(appWidgetId)
|
||||
|
||||
override fun setWidgetConversionPairs(fromCurrency: String,
|
||||
toCurrency: String, appWidgetId: Int) {
|
||||
override fun setWidgetConversionPairs(
|
||||
fromCurrency: String,
|
||||
toCurrency: String, appWidgetId: Int
|
||||
) {
|
||||
return prefs.saveWidgetConversionPair(fromCurrency, toCurrency, appWidgetId)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.appttude.h_mal.easycc.helper
|
||||
|
||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||
import com.appttude.h_mal.easycc.models.CurrencyModelInterface
|
||||
import java.lang.Exception
|
||||
|
||||
class CurrencyDataHelper(
|
||||
val repository: Repository
|
||||
|
||||
@@ -4,10 +4,8 @@ import com.appttude.h_mal.easycc.data.repository.Repository
|
||||
import com.appttude.h_mal.easycc.models.CurrencyModel
|
||||
import com.appttude.h_mal.easycc.utils.trimToThree
|
||||
|
||||
import kotlin.Exception
|
||||
|
||||
class WidgetHelper(
|
||||
val helper: CurrencyDataHelper,
|
||||
private val helper: CurrencyDataHelper,
|
||||
val repository: Repository
|
||||
) {
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import kotlinx.android.synthetic.main.custom_dialog.*
|
||||
/**
|
||||
* Custom dialog when selecting currencies from list with filter
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
class CustomDialogClass(
|
||||
context: Context,
|
||||
private val clickListener: ClickListener
|
||||
@@ -31,7 +32,8 @@ class CustomDialogClass(
|
||||
val arrayAdapter =
|
||||
ArrayAdapter.createFromResource(
|
||||
context, R.array.currency_arrays,
|
||||
android.R.layout.simple_list_item_1)
|
||||
android.R.layout.simple_list_item_1
|
||||
)
|
||||
|
||||
list_view.adapter = arrayAdapter
|
||||
|
||||
@@ -41,6 +43,7 @@ class CustomDialogClass(
|
||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
|
||||
arrayAdapter.filter.filter(charSequence)
|
||||
}
|
||||
|
||||
override fun afterTextChanged(editable: Editable) {}
|
||||
})
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.kodein.di.KodeinAware
|
||||
import org.kodein.di.android.kodein
|
||||
import org.kodein.di.generic.instance
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class MainActivity : AppCompatActivity(), KodeinAware, View.OnClickListener {
|
||||
|
||||
// Retrieve MainViewModelFactory via dependency injection
|
||||
@@ -54,10 +55,10 @@ class MainActivity : AppCompatActivity(), KodeinAware, View.OnClickListener {
|
||||
}
|
||||
|
||||
private fun setUpObservers() {
|
||||
viewModel.operationStartedListener.observe(this, Observer {
|
||||
viewModel.operationStartedListener.observe(this, {
|
||||
progressBar.hideView(false)
|
||||
})
|
||||
viewModel.operationFinishedListener.observe(this, Observer { pair ->
|
||||
viewModel.operationFinishedListener.observe(this, { pair ->
|
||||
// hide progress bar
|
||||
progressBar.hideView(true)
|
||||
if (pair.first) {
|
||||
|
||||
@@ -98,7 +98,9 @@ class MainViewModel(
|
||||
when (tag.toString()) {
|
||||
"top" -> rateIdFrom = currencyName
|
||||
"bottom" -> rateIdTo = currencyName
|
||||
else -> { return }
|
||||
else -> {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
getExchangeRate()
|
||||
|
||||
@@ -25,7 +25,8 @@ import org.kodein.di.generic.instance
|
||||
/**
|
||||
* The configuration screen for the [CurrencyAppWidgetKotlin] AppWidget.
|
||||
*/
|
||||
class CurrencyAppWidgetConfigureActivityKotlin : AppCompatActivity(), KodeinAware, View.OnClickListener {
|
||||
class CurrencyAppWidgetConfigureActivityKotlin : AppCompatActivity(), KodeinAware,
|
||||
View.OnClickListener {
|
||||
|
||||
override val kodein by kodein()
|
||||
private val factory: WidgetViewModelFactory by instance()
|
||||
@@ -47,7 +48,8 @@ class CurrencyAppWidgetConfigureActivityKotlin : AppCompatActivity(), KodeinAwar
|
||||
val extras = intent.extras
|
||||
if (extras != null) {
|
||||
mAppWidgetId = extras.getInt(
|
||||
AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID)
|
||||
AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID
|
||||
)
|
||||
}
|
||||
|
||||
// If this activity was started with an intent without an app widget ID, finish with an error.
|
||||
@@ -72,7 +74,7 @@ class CurrencyAppWidgetConfigureActivityKotlin : AppCompatActivity(), KodeinAwar
|
||||
}
|
||||
|
||||
private fun setupObserver() {
|
||||
viewModel.operationFinishedListener.observe(this, Observer {
|
||||
viewModel.operationFinishedListener.observe(this, {
|
||||
|
||||
// it.first is a the success of the operation
|
||||
if (it.first) {
|
||||
@@ -136,7 +138,8 @@ class CurrencyAppWidgetConfigureActivityKotlin : AppCompatActivity(), KodeinAwar
|
||||
fun sendUpdateIntent() {
|
||||
// It is the responsibility of the configuration activity to update the app widget
|
||||
// Send update broadcast to widget app class
|
||||
Intent(this@CurrencyAppWidgetConfigureActivityKotlin,
|
||||
Intent(
|
||||
this@CurrencyAppWidgetConfigureActivityKotlin,
|
||||
CurrencyAppWidgetKotlin::class.java
|
||||
).apply {
|
||||
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.appttude.h_mal.easycc.ui.widget
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.WindowManager
|
||||
import android.widget.ArrayAdapter
|
||||
import com.appttude.h_mal.easycc.R
|
||||
import kotlinx.android.synthetic.main.custom_dialog.*
|
||||
|
||||
/*
|
||||
widget for when submitting the completed selections
|
||||
*/
|
||||
class WidgetItemSelectDialog(
|
||||
context: Context,
|
||||
private val dialogResult: DialogResult
|
||||
) :Dialog(context){
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.custom_dialog)
|
||||
|
||||
window!!.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
||||
|
||||
val arrayAdapter = ArrayAdapter.createFromResource(context, R.array.currency_arrays, android.R.layout.simple_list_item_1)
|
||||
list_view.adapter = arrayAdapter
|
||||
|
||||
search_text.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
|
||||
arrayAdapter.filter.filter(charSequence)
|
||||
}
|
||||
override fun afterTextChanged(editable: Editable) {}
|
||||
})
|
||||
|
||||
list_view.setOnItemClickListener{ adapterView, _, i, _ ->
|
||||
dialogResult.result(adapterView.getItemAtPosition(i).toString())
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface DialogResult{
|
||||
fun result(result : String)
|
||||
}
|
||||
@@ -23,8 +23,7 @@ class WidgetViewModel(
|
||||
// Set default values for text views
|
||||
fun initiate(appId: Int) {
|
||||
appWidgetId = appId
|
||||
val widgetString
|
||||
= repository.getWidgetConversionPairs(appId)
|
||||
val widgetString = repository.getWidgetConversionPairs(appId)
|
||||
|
||||
rateIdFrom = widgetString.first ?: defaultCurrency
|
||||
rateIdTo = widgetString.second ?: defaultCurrency
|
||||
@@ -61,7 +60,9 @@ class WidgetViewModel(
|
||||
when (tag.toString()) {
|
||||
"top" -> rateIdFrom = currencyName
|
||||
"bottom" -> rateIdTo = currencyName
|
||||
else -> { return }
|
||||
else -> {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.appttude.h_mal.easycc.utils
|
||||
|
||||
import java.lang.Double.valueOf
|
||||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
|
||||
fun transformIntToArray(int: Int): IntArray{
|
||||
return intArrayOf(int)
|
||||
|
||||
@@ -10,7 +10,11 @@ fun EditText.clearEditText(){
|
||||
}
|
||||
|
||||
fun View.hideView(vis: Boolean) {
|
||||
visibility = if (vis){ View.GONE } else { View.VISIBLE }
|
||||
visibility = if (vis) {
|
||||
View.GONE
|
||||
} else {
|
||||
View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.displayToast(message: String) {
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
package com.appttude.h_mal.easycc.widget
|
||||
|
||||
import android.app.Activity
|
||||
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 com.appttude.h_mal.easycc.R
|
||||
import com.appttude.h_mal.easycc.helper.WidgetHelper
|
||||
import com.appttude.h_mal.easycc.ui.main.MainActivity
|
||||
import com.appttude.h_mal.easycc.utils.transformIntToArray
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import com.appttude.h_mal.easycc.widget.WidgetServiceIntent.Companion.enqueueWork
|
||||
import org.kodein.di.KodeinAware
|
||||
import org.kodein.di.LateInitKodein
|
||||
import org.kodein.di.generic.instance
|
||||
@@ -23,9 +13,8 @@ import org.kodein.di.generic.instance
|
||||
|
||||
/**
|
||||
* Implementation of App Widget functionality.
|
||||
* App Widget Configuration implemented in [CurrencyAppWidgetConfigureActivityKotlin]
|
||||
* App Widget Configuration implemented in [CurrencyAppWidgetKotlin]
|
||||
*/
|
||||
private const val TAG = "CurrencyAppWidgetKotlin"
|
||||
|
||||
class CurrencyAppWidgetKotlin : AppWidgetProvider() {
|
||||
|
||||
@@ -39,12 +28,7 @@ class CurrencyAppWidgetKotlin : AppWidgetProvider() {
|
||||
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)
|
||||
}
|
||||
loadWidget(context)
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||
}
|
||||
|
||||
@@ -58,113 +42,13 @@ class CurrencyAppWidgetKotlin : AppWidgetProvider() {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
loadWidget(context)
|
||||
super.onEnabled(context)
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context) {
|
||||
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
|
||||
// Enter relevant functionality for when the last widget is disabled
|
||||
}
|
||||
|
||||
|
||||
private fun updateAppWidget(
|
||||
context: Context,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
appWidgetId: Int
|
||||
) {
|
||||
// Construct the RemoteViews object
|
||||
val views = RemoteViews(context.packageName, R.layout.currency_app_widget)
|
||||
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val exchangeResponse = repository.getWidgetData()
|
||||
|
||||
exchangeResponse?.let {
|
||||
val titleString = "${it.from}${it.to}"
|
||||
views.setTextViewText(R.id.exchangeName, titleString)
|
||||
views.setTextViewText(R.id.exchangeRate, it.rate.toString())
|
||||
|
||||
setUpdateIntent(context, appWidgetId).let { intent ->
|
||||
//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, intent)
|
||||
}
|
||||
|
||||
val clickIntentTemplate = clickingIntent(context)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
): Intent {
|
||||
val pair = repository.repository.getConversionPair()
|
||||
val s1 = pair.first
|
||||
val s2 = pair.second
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T: Activity> clickingIntent(
|
||||
context: Context,
|
||||
activity: Class<T>,
|
||||
vararg argPairs: Pair<String, Any?>
|
||||
): Intent {
|
||||
|
||||
return Intent(context, activity::class.java).apply {
|
||||
action = Intent.ACTION_MAIN
|
||||
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
argPairs.forEach {
|
||||
putExtra(it.first, it.second)
|
||||
}
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T: Any> Intent.putExtra(s: String, second: T?) {
|
||||
when(second){
|
||||
is String -> putExtra(s,second)
|
||||
}
|
||||
private fun loadWidget(context: Context) {
|
||||
val mIntent = Intent(context, CurrencyAppWidgetKotlin::class.java)
|
||||
enqueueWork(context, mIntent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.appttude.h_mal.easycc.widget
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.appttude.h_mal.easycc.R
|
||||
import com.appttude.h_mal.easycc.helper.WidgetHelper
|
||||
import com.appttude.h_mal.easycc.ui.main.MainActivity
|
||||
import com.appttude.h_mal.easycc.utils.transformIntToArray
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kodein.di.LateInitKodein
|
||||
import org.kodein.di.generic.instance
|
||||
|
||||
class WidgetServiceIntent : JobIntentService() {
|
||||
|
||||
//DI with kodein to use in CurrencyAppWidgetKotlin
|
||||
private val kodein = LateInitKodein()
|
||||
private val repository: WidgetHelper by kodein.instance()
|
||||
|
||||
override fun onHandleWork(intent: Intent) {
|
||||
kodein.baseKodein = this.kodein
|
||||
|
||||
val appWidgetManager = AppWidgetManager.getInstance(this)
|
||||
val thisAppWidget = ComponentName(packageName, CurrencyAppWidgetKotlin::class.java.name)
|
||||
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
|
||||
|
||||
for (appWidgetId in appWidgetIds) {
|
||||
updateAppWidget(this, appWidgetManager, appWidgetId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAppWidget(
|
||||
context: Context,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
appWidgetId: Int
|
||||
) {
|
||||
// Construct the RemoteViews object
|
||||
val views = RemoteViews(context.packageName, R.layout.currency_app_widget)
|
||||
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val exchangeResponse = repository.getWidgetData()
|
||||
|
||||
exchangeResponse?.let {
|
||||
val titleString = "${it.from}${it.to}"
|
||||
views.setTextViewText(R.id.exchangeName, titleString)
|
||||
views.setTextViewText(R.id.exchangeRate, it.rate.toString())
|
||||
|
||||
setUpdateIntent(context, appWidgetId).let { intent ->
|
||||
//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, intent)
|
||||
}
|
||||
|
||||
val clickIntentTemplate = clickingIntent(context)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
): Intent {
|
||||
val pair = repository.repository.getConversionPair()
|
||||
val s1 = pair.first
|
||||
val s2 = pair.second
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Unique job ID for this service.
|
||||
*/
|
||||
private const val JOB_ID = 1000
|
||||
|
||||
/**
|
||||
* Convenience method for enqueuing work in to this service.
|
||||
*/
|
||||
fun enqueueWork(context: Context, work: Intent) {
|
||||
enqueueWork(context, WidgetServiceIntent::class.java, JOB_ID, work)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.appttude.h_mal.easycc;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
|
||||
@Test
|
||||
public void addition_isCorrect() throws Exception {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.appttude.h_mal.easycc.repository
|
||||
|
||||
import com.appttude.h_mal.easycc.data.network.api.BackupCurrencyApi
|
||||
import com.appttude.h_mal.easycc.data.network.api.CurrencyApi
|
||||
import com.appttude.h_mal.easycc.data.network.response.CurrencyResponse
|
||||
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
||||
import com.appttude.h_mal.easycc.data.prefs.PreferenceProvider
|
||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||
@@ -29,8 +28,10 @@ class RepositoryNetworkTest{
|
||||
|
||||
@Mock
|
||||
lateinit var api: CurrencyApi
|
||||
|
||||
@Mock
|
||||
lateinit var apiBackup: BackupCurrencyApi
|
||||
|
||||
@Mock
|
||||
lateinit var prefs: PreferenceProvider
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.appttude.h_mal.easycc.repository
|
||||
|
||||
import android.content.Context
|
||||
import com.appttude.h_mal.easycc.data.network.api.BackupCurrencyApi
|
||||
import com.appttude.h_mal.easycc.data.network.api.CurrencyApi
|
||||
import com.appttude.h_mal.easycc.data.prefs.PreferenceProvider
|
||||
@@ -19,8 +18,10 @@ class RepositoryStorageTest {
|
||||
|
||||
@Mock
|
||||
lateinit var api: CurrencyApi
|
||||
|
||||
@Mock
|
||||
lateinit var apiBackup: BackupCurrencyApi
|
||||
|
||||
@Mock
|
||||
lateinit var prefs: PreferenceProvider
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||
import com.appttude.h_mal.easycc.helper.CurrencyDataHelper
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import com.appttude.h_mal.easycc.utils.observeOnce
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
@@ -48,7 +48,8 @@ class MainViewModelTest {
|
||||
//WHEN
|
||||
Mockito.`when`(bundle.getString("parse_1")).thenReturn(currencyOne)
|
||||
Mockito.`when`(bundle.getString("parse_2")).thenReturn(currencyTwo)
|
||||
Mockito.`when`(repository.getDataFromApi(currencyOne, currencyTwo)).thenReturn(responseObject)
|
||||
Mockito.`when`(repository.getDataFromApi(currencyOne, currencyTwo))
|
||||
.thenReturn(responseObject)
|
||||
|
||||
//THEN
|
||||
viewModel.initiate(bundle)
|
||||
@@ -71,7 +72,8 @@ class MainViewModelTest {
|
||||
|
||||
//WHEN
|
||||
Mockito.`when`(repository.getConversionPair()).thenReturn(pair)
|
||||
Mockito.`when`(repository.getDataFromApi(currencyOne, currencyTwo)).thenReturn(responseObject)
|
||||
Mockito.`when`(repository.getDataFromApi(currencyOne, currencyTwo))
|
||||
.thenReturn(responseObject)
|
||||
|
||||
//THEN
|
||||
viewModel.initiate(null)
|
||||
@@ -128,7 +130,6 @@ class MainViewModelTest {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
fun setCurrencyName_validValues_successResponse() = runBlocking {
|
||||
//GIVEN
|
||||
@@ -139,7 +140,8 @@ class MainViewModelTest {
|
||||
val responseObject = mock(ResponseObject::class.java)
|
||||
|
||||
//WHEN
|
||||
Mockito.`when`(repository.getDataFromApi(currencyOne, currencyTwo)).thenReturn(responseObject)
|
||||
Mockito.`when`(repository.getDataFromApi(currencyOne, currencyTwo))
|
||||
.thenReturn(responseObject)
|
||||
|
||||
//THEN
|
||||
viewModel.setCurrencyName(tag, currencyOne)
|
||||
@@ -163,7 +165,8 @@ class MainViewModelTest {
|
||||
val responseObject = mock(ResponseObject::class.java)
|
||||
|
||||
//WHEN
|
||||
Mockito.`when`(repository.getDataFromApi(currencyOne, currencyTwo)).thenReturn(responseObject)
|
||||
Mockito.`when`(repository.getDataFromApi(currencyOne, currencyTwo))
|
||||
.thenReturn(responseObject)
|
||||
|
||||
//THEN
|
||||
viewModel.setCurrencyName(tag, currencyOne)
|
||||
@@ -185,7 +188,8 @@ class MainViewModelTest {
|
||||
val responseObject = mock(ResponseObject::class.java)
|
||||
|
||||
//WHEN
|
||||
Mockito.`when`(repository.getDataFromApi(currencyOne, currencyTwo)).thenReturn(responseObject)
|
||||
Mockito.`when`(repository.getDataFromApi(currencyOne, currencyTwo))
|
||||
.thenReturn(responseObject)
|
||||
|
||||
//THEN
|
||||
viewModel.setCurrencyName(tag, currencyOne)
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.appttude.h_mal.easycc.ui.widget
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||
import com.appttude.h_mal.easycc.utils.observeOnce
|
||||
import org.junit.Before
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
@@ -14,6 +14,7 @@ import org.mockito.MockitoAnnotations
|
||||
|
||||
private const val currencyOne = "AUD - Australian Dollar"
|
||||
private const val currencyTwo = "GBP - British Pound"
|
||||
|
||||
class WidgetViewModelTest {
|
||||
|
||||
// Run tasks synchronously
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.easycc
|
||||
package com.appttude.h_mal.easycc.utils
|
||||
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.appttude.h_mal.easycc.utils
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.appttude.h_mal.easycc.OneTimeObserver
|
||||
|
||||
fun <T> LiveData<T>.observeOnce(onChangeHandler: (T) -> Unit) {
|
||||
val observer = OneTimeObserver(handler = onChangeHandler)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.61'
|
||||
ext.kotlin_version = '1.4.10'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.1'
|
||||
classpath 'com.android.tools.build:gradle:4.0.2'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Thu Mar 05 18:27:33 UTC 2020
|
||||
#Sat Jun 12 22:27:25 BST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||
|
||||
Reference in New Issue
Block a user