Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save enriquebautista/e689dc0585ed848114e12b5fe9194ca8 to your computer and use it in GitHub Desktop.
Save enriquebautista/e689dc0585ed848114e12b5fe9194ca8 to your computer and use it in GitHub Desktop.
Using CoroutineWorker
package io.coworkerbox.data.sync
import android.content.Context
import androidx.core.content.edit
import androidx.work.*
import io.coworkerbox.BuildConfig
import io.coworkerbox.data.ObjectBox
import io.coworkerbox.data.Prefs
import io.coworkerbox.data.Prefs.PREF_STATE_CITY_VERSION
import io.coworkerbox.data.model.City
import io.coworkerbox.data.model.State
import io.objectbox.kotlin.boxFor
import kotlinx.coroutines.asCoroutineDispatcher
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONException
import org.json.JSONObject
import org.json.JSONTokener
import timber.log.Timber
import java.io.IOException
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
class RefreshMainDataWorker(
context: Context,
workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
companion object {
private const val TAG = "RefreshMainDataWorker"
private const val DEFAULT_READ_TIMEOUT = 30L // in seconds
private const val DEFAULT_CONNECT_TIMEOUT = 15L // in seconds
private const val STATES_CITIES_URL = BuildConfig.HOST.plus("Estados_Ciudades.php")
private const val KEY_RESPONSE = "response"
private const val KEY_STATUS_CODE = "statuscode"
private const val KEY_STATUS_MESSAGE = "statusmessage"
private const val KEY_RESULTSET = "resultset"
private const val KEY_RESULT_JSON_ARRAY = "hospitales"
private const val KEY_CITIES_JSONA = "ciudades"
private const val KEY_INACTIVE = "inactivo"
private const val KEY_VERSION = "version"
// States JSON KEYS
private const val KEY_STATE_ID = "id_estado"
private const val KEY_STATE_NAME = "nombre"
// Cities JSON KEYS
private const val KEY_CITY_ID = "id_ciudad"
private const val KEY_CITY_NAME = "nombre"
internal fun enqueueLoad() {
val workManager = WorkManager.getInstance()
workManager.enqueueUniqueWork(
TAG,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequestBuilder<RefreshMainDataWorker>()
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build())
.build())
}
}
private val stateBox by lazy { ObjectBox.boxStore.boxFor<State>() }
private val cityBox by lazy { ObjectBox.boxStore.boxFor<City>() }
private val prefs by lazy { Prefs.getSharedPreferences(applicationContext) }
// Single threaded coroutine context used for all sync operations
private val syncSingleThreadContext by lazy {
Executors.newSingleThreadExecutor { target ->
Thread(target, "RefreshMainData")
}.asCoroutineDispatcher()
}
override val coroutineContext = syncSingleThreadContext
override suspend fun doWork(): Result {
return try {
val statesCitiesJson = fetchJsonObject(getVersionURL(STATES_CITIES_URL, PREF_STATE_CITY_VERSION))
storeStatesAndCities(statesCitiesJson)
Result.success()
} catch (ex: JSONException) {
Timber.e(ex)
Result.failure()
} catch (ex: IOException) {
Timber.e(ex)
Result.failure()
}
}
@Throws(JSONException::class)
private fun storeStatesAndCities(response: JSONObject) {
val statusCode = response.getInt(KEY_STATUS_CODE)
val statusMessage = response.getString(KEY_STATUS_MESSAGE)
val versionCode = response.getLong(KEY_VERSION)
var jsonObject: JSONObject?
if(statusCode == 0) { // Ok!
jsonObject = response.getJSONObject(KEY_RESULTSET)
val dataJsonArray = jsonObject.getJSONArray(KEY_RESULT_JSON_ARRAY)
if(dataJsonArray != null && dataJsonArray.length() > 0) {
// see https://docs.objectbox.io/transactions#transaction-costs
val statesList = mutableListOf<State>()
val citiesList = mutableListOf<City>()
for(i in 0 until dataJsonArray.length()) {
jsonObject = dataJsonArray.getJSONObject(i)
val stateId = jsonObject.getLong(KEY_STATE_ID)
val stateName = jsonObject.getString(KEY_STATE_NAME)
val stateActive = jsonObject.getInt(KEY_INACTIVE) == 0
val stateObj = State(stateId, stateName, stateActive)
val citiesJsonArray = jsonObject.getJSONArray(KEY_CITIES_JSONA)
for(j in 0 until citiesJsonArray.length()) {
jsonObject = citiesJsonArray.getJSONObject(j)
val cityId = jsonObject.getLong(KEY_CITY_ID)
val cityName = jsonObject.getString(KEY_CITY_NAME)
val cityActive = jsonObject.getInt(KEY_INACTIVE) == 0
val cityObj = City(cityId, cityName, cityActive).apply {
state.target = stateObj
}
citiesList.add(cityObj)
}
statesList.add(stateObj)
}
stateBox.put(statesList)
cityBox.put(citiesList)
}
prefs.edit { putLong(PREF_STATE_CITY_VERSION, versionCode) }
} else {
Timber.e("storeStatesAndCities result FAILURE [$statusMessage]")
}
}
@Throws(IOException::class, JSONException::class)
private fun fetchJsonObject(url:String): JSONObject {
val client = OkHttpClient.Builder()
.connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
.build()
val request = Request.Builder()
.url(url)
.build()
val response = client.newCall(request).execute()
if(!response.isSuccessful) throw IOException("Unexpected code $response")
val json = response.body()?.string() ?: throw IOException("Response was null")
val tokener = JSONTokener(json)
var jsonObject = tokener.nextValue() as? JSONObject ?: throw JSONException("Expected JSON object.")
jsonObject = jsonObject.getJSONObject(KEY_RESPONSE)
return jsonObject
}
private fun getVersionURL(baseUrl: String, prefsKey: String): String {
val versionCode = prefs.getLong(prefsKey, 0L)
Timber.e("lastVersionCode: $versionCode")
return baseUrl.plus("?version=$versionCode")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment