Skip to content

Commit d4d87d2

Browse files
authored
Merge branch 'master' into 5060-filter-category-library
2 parents 017f7db + 2e7dc13 commit d4d87d2

File tree

5 files changed

+181
-50
lines changed

5 files changed

+181
-50
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,85 @@
11
package org.ole.planet.myplanet.datamanager
22

33
import com.google.gson.GsonBuilder
4+
import okhttp3.Interceptor
45
import okhttp3.OkHttpClient
6+
import okhttp3.Response
57
import retrofit2.Retrofit
68
import retrofit2.converter.gson.GsonConverterFactory
9+
import java.io.IOException
710
import java.lang.reflect.Modifier
811
import java.util.concurrent.TimeUnit
12+
import kotlin.math.pow
913

1014
object ApiClient {
1115
private const val BASE_URL = "https://vi.media.mit.edu/"
1216
private var retrofit: Retrofit? = null
1317
@JvmStatic
1418
val client: Retrofit?
1519
get() {
16-
val client = OkHttpClient.Builder().connectTimeout(1, TimeUnit.MINUTES)
17-
.readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).build()
20+
val client = OkHttpClient.Builder().connectTimeout(1, TimeUnit.MINUTES).readTimeout(30, TimeUnit.SECONDS)
21+
.writeTimeout(30, TimeUnit.SECONDS).addInterceptor { chain ->
22+
val request = chain.request().newBuilder()
23+
.addHeader("Accept-Encoding", "gzip").build()
24+
chain.proceed(request)
25+
}
26+
.retryOnConnectionFailure(true).addInterceptor(RetryInterceptor()).build()
1827
if (retrofit == null) {
1928
retrofit = Retrofit.Builder()
20-
.baseUrl(BASE_URL)
21-
.client(client)
22-
.addConverterFactory(
23-
GsonConverterFactory.create(
24-
GsonBuilder()
25-
.excludeFieldsWithModifiers(Modifier.FINAL, Modifier.TRANSIENT, Modifier.STATIC)
26-
.serializeNulls()
27-
.create()
29+
.baseUrl(BASE_URL).client(client)
30+
.addConverterFactory(GsonConverterFactory.create(
31+
GsonBuilder()
32+
.excludeFieldsWithModifiers(Modifier.FINAL, Modifier.TRANSIENT, Modifier.STATIC)
33+
.serializeNulls()
34+
.create()
2835
)
2936
).build()
3037
}
3138
return retrofit
3239
}
33-
}
40+
41+
class RetryInterceptor : Interceptor {
42+
val maxRetryCount = 3
43+
val retryDelayMillis = 1000L
44+
45+
override fun intercept(chain: Interceptor.Chain): Response {
46+
var attempt = 0
47+
var response: Response? = null
48+
val request = chain.request()
49+
val url = request.url().toString()
50+
51+
while (true) {
52+
try {
53+
response?.close()
54+
response = chain.proceed(request)
55+
56+
when (response.code()) {
57+
in 200..299 -> {
58+
return response
59+
}
60+
404 -> {
61+
return response
62+
}
63+
401, 403 -> {
64+
response.close()
65+
throw IOException("Authentication failed: ${response.code()}")
66+
}
67+
else -> {
68+
response.close()
69+
throw IOException("Response unsuccessful: ${response.code()}")
70+
}
71+
}
72+
} catch (e: IOException) {
73+
attempt++
74+
75+
if (attempt >= maxRetryCount) {
76+
throw IOException("Failed after $maxRetryCount attempts: $url", e)
77+
}
78+
79+
val delayMillis = retryDelayMillis * 2.0.pow((attempt - 1).toDouble()).toLong()
80+
Thread.sleep(delayMillis)
81+
}
82+
}
83+
}
84+
}
85+
}

app/src/main/java/org/ole/planet/myplanet/service/SyncManager.kt

+109-31
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class SyncManager private constructor(private val context: Context) {
9494
settings.edit().putString("LastWifiSSID", wifiInfo.ssid).apply()
9595
}
9696
isSyncing = true
97-
create(context, R.mipmap.ic_launcher, " Syncing data", "Please wait...")
97+
create(context, R.mipmap.ic_launcher, "Syncing data", "Please wait...")
9898
mRealm = dbService.realmInstance
9999
TransactionSyncManager.syncDb(mRealm, "tablet_users")
100100
myLibraryTransactionSync()
@@ -144,50 +144,111 @@ class SyncManager private constructor(private val context: Context) {
144144
}
145145
}
146146

147-
@Throws(IOException::class)
148147
private fun syncResource(dbClient: ApiInterface?) {
149148
val newIds: MutableList<String?> = ArrayList()
150-
val allDocs = dbClient?.getJsonObject(Utilities.header, Utilities.getUrl() + "/resources/_all_docs?include_doc=false")
151-
val all = allDocs?.execute()
152-
val rows = getJsonArray("rows", all?.body())
153-
val keys: MutableList<String> = ArrayList()
154-
for (i in 0 until rows.size()) {
155-
val `object` = rows[i].asJsonObject
156-
if (!TextUtils.isEmpty(getString("id", `object`))) keys.add(getString("key", `object`))
157-
if (i == rows.size() - 1 || keys.size == 1000) {
158-
val obj = JsonObject()
159-
obj.add("keys", Gson().fromJson(Gson().toJson(keys), JsonArray::class.java))
160-
val response = dbClient?.findDocs(Utilities.header, "application/json", Utilities.getUrl() + "/resources/_all_docs?include_docs=true", obj)?.execute()
161-
if (response?.body() != null) {
162-
val ids: List<String?> = save(getJsonArray("rows", response.body()), mRealm)
163-
newIds.addAll(ids)
149+
try {
150+
val allDocs = dbClient?.getJsonObject(Utilities.header, "${Utilities.getUrl()}/resources/_all_docs?include_doc=false")
151+
val all = allDocs?.execute()
152+
if (all?.isSuccessful != true) {
153+
return
154+
}
155+
156+
val rows = getJsonArray("rows", all.body())
157+
val keys: MutableList<String> = ArrayList()
158+
val failedIds: MutableList<String> = ArrayList()
159+
160+
for (i in 0 until rows.size()) {
161+
val `object` = rows[i].asJsonObject
162+
if (!TextUtils.isEmpty(getString("id", `object`))) {
163+
keys.add(getString("key", `object`))
164+
}
165+
166+
if (i == rows.size() - 1 || keys.size == 1000) {
167+
val obj = JsonObject()
168+
obj.add("keys", Gson().fromJson(Gson().toJson(keys), JsonArray::class.java))
169+
val response = dbClient.findDocs(Utilities.header, "application/json", "${Utilities.getUrl()}/resources/_all_docs?include_docs=true", obj).execute()
170+
171+
when {
172+
response.isSuccessful == true -> {
173+
response.body()?.let { body ->
174+
val ids: List<String?> = save(getJsonArray("rows", body), mRealm)
175+
newIds.addAll(ids)
176+
}
177+
}
178+
response.code() == 404 -> {
179+
failedIds.addAll(keys)
180+
}
181+
else -> {
182+
val errorMessage = "Failed to sync resources: ${response.code()}"
183+
handleException(errorMessage)
184+
185+
when (response.code()) {
186+
in 500..599 -> {
187+
addToRetryQueue(keys)
188+
}
189+
401, 403 -> {
190+
handleAuthenticationError()
191+
}
192+
else -> {
193+
failedIds.addAll(keys)
194+
}
195+
}
196+
}
197+
}
198+
keys.clear()
164199
}
165-
keys.clear()
200+
}
201+
} catch (e: Exception) {
202+
e.printStackTrace()
203+
} finally {
204+
try {
205+
removeDeletedResource(newIds, mRealm)
206+
} catch (e: Exception) {
207+
e.printStackTrace()
166208
}
167209
}
168-
removeDeletedResource(newIds, mRealm)
210+
}
211+
212+
private fun addToRetryQueue(keys: List<String>) {
213+
settings.edit().apply {
214+
val existingQueue = settings.getStringSet("retry_queue", setOf()) ?: setOf()
215+
putStringSet("retry_queue", existingQueue + keys)
216+
apply()
217+
}
218+
}
219+
220+
private fun handleAuthenticationError() {
221+
settings.edit().remove("credentials").apply()
222+
handleException("Authentication failed.")
169223
}
170224

171225
private fun myLibraryTransactionSync() {
172226
val apiInterface = client?.create(ApiInterface::class.java)
173227
try {
174-
val res = apiInterface?.getDocuments(Utilities.header, Utilities.getUrl() + "/shelf/_all_docs")?.execute()?.body()
175-
for (i in res?.rows!!.indices) {
176-
shelfDoc = res.rows!![i]
177-
populateShelfItems(apiInterface)
228+
val response = apiInterface?.getDocuments(Utilities.header, "${Utilities.getUrl()}/shelf/_all_docs")?.execute()
229+
230+
val res = response?.body()
231+
res?.rows?.let { rows ->
232+
for (i in rows.indices) {
233+
shelfDoc = rows[i]
234+
populateShelfItems(apiInterface)
235+
}
178236
}
179237
} catch (e: IOException) {
180238
e.printStackTrace()
181239
}
182240
}
183241

184-
private fun populateShelfItems(apiInterface: ApiInterface) {
242+
private fun populateShelfItems(apiInterface: ApiInterface?) {
185243
try {
186-
val jsonDoc = apiInterface.getJsonObject(Utilities.header, Utilities.getUrl() + "/shelf/" + shelfDoc?.id).execute().body()
187-
for (i in Constants.shelfDataList.indices) {
188-
val shelfData = Constants.shelfDataList[i]
189-
val array = getJsonArray(shelfData.key, jsonDoc)
190-
memberShelfData(array, shelfData)
244+
val response = apiInterface?.getJsonObject(Utilities.header, "${Utilities.getUrl()}/shelf/${shelfDoc?.id}")?.execute()
245+
246+
response?.body()?.let { jsonDoc ->
247+
for (i in Constants.shelfDataList.indices) {
248+
val shelfData = Constants.shelfDataList[i]
249+
val array = getJsonArray(shelfData.key, jsonDoc)
250+
memberShelfData(array, shelfData)
251+
}
191252
}
192253
} catch (err: Exception) {
193254
err.printStackTrace()
@@ -217,10 +278,27 @@ class SyncManager private constructor(private val context: Context) {
217278
}
218279

219280
private fun validateDocument(arrayCategoryIds: JsonArray, x: Int) {
220-
val apiInterface = client!!.create(ApiInterface::class.java)
281+
val apiInterface = client?.create(ApiInterface::class.java)
221282
try {
222-
val resourceDoc = apiInterface.getJsonObject(Utilities.header, Utilities.getUrl() + "/" + stringArray[2] + "/" + arrayCategoryIds[x].asString).execute().body()
223-
resourceDoc?.let { triggerInsert(stringArray, it) }
283+
val response = apiInterface?.getJsonObject(Utilities.header, "${Utilities.getUrl()}/${stringArray[2]}/${arrayCategoryIds[x].asString}")?.execute()
284+
285+
when {
286+
response?.isSuccessful == true -> {
287+
response.body()?.let { resourceDoc ->
288+
triggerInsert(stringArray, resourceDoc)
289+
}
290+
}
291+
response?.code() == 404 -> {
292+
return
293+
}
294+
else -> {
295+
val errorMessage = "Failed to validate document: ${response?.code()}"
296+
handleException(errorMessage)
297+
if (response?.code() in 500..599) {
298+
throw IOException(errorMessage)
299+
}
300+
}
301+
}
224302
} catch (e: IOException) {
225303
e.printStackTrace()
226304
}

app/src/main/java/org/ole/planet/myplanet/ui/courses/CoursesFragment.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ class CoursesFragment : BaseRecyclerFragment<RealmMyCourse?>(), OnCourseItemSele
217217
spnGrade.adapter = gradeAdapter
218218

219219
val subjectAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.subject_level, R.layout.spinner_item)
220-
subjectAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
220+
subjectAdapter.setDropDownViewResource(R.layout.custom_simple_list_item_1)
221221
spnSubject.adapter = subjectAdapter
222222

223223
spnGrade.onItemSelectedListener = itemSelectedListener

app/src/main/java/org/ole/planet/myplanet/ui/submission/MySubmissionFragment.kt

+6-5
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,12 @@ class MySubmissionFragment : Fragment(), CompoundButton.OnCheckedChangeListener
104104

105105
val adapter = AdapterMySubmission(requireActivity(), submissions, exams)
106106
val itemCount = adapter.itemCount
107-
showNoData(fragmentMySubmissionBinding.tvMessage, itemCount, "submission")
108-
109-
if (itemCount == 0) {
110-
fragmentMySubmissionBinding.llSearch.visibility = View.GONE
111-
fragmentMySubmissionBinding.title.visibility = View.GONE
107+
if(s.isNullOrEmpty()){
108+
showNoData(fragmentMySubmissionBinding.tvMessage, itemCount, "submission")
109+
if (itemCount == 0) {
110+
fragmentMySubmissionBinding.llSearch.visibility = View.GONE
111+
fragmentMySubmissionBinding.title.visibility = View.GONE
112+
}
112113
}
113114
adapter.setmRealm(mRealm)
114115
adapter.setType(type)

app/src/main/java/org/ole/planet/myplanet/utilities/JsonUtils.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ object JsonUtils {
9292
return try {
9393
if (jsonObject?.has(fieldName) == true) {
9494
val el: JsonElement = jsonObject.get(fieldName)
95-
if (el is JsonNull) 0 else el.asInt
95+
if (el is JsonNull || el.asString.isEmpty()) 0 else el.asInt
9696
} else {
9797
0
9898
}
@@ -107,7 +107,7 @@ object JsonUtils {
107107
return try {
108108
if (jsonObject?.has(fieldName) == true) {
109109
val el: JsonElement = jsonObject.get(fieldName)
110-
if (el is JsonNull) 0f else el.asFloat
110+
if (el is JsonNull || el.asString.isEmpty()) 0f else el.asFloat
111111
} else {
112112
getInt(fieldName, jsonObject).toFloat()
113113
}

0 commit comments

Comments
 (0)