Skip to content

Commit

Permalink
Merge branch 'master' into 5050-res-downloaded-dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
Okuro3499 committed Jan 20, 2025
2 parents 7316a7d + eedf082 commit 0f804f6
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 69 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ android {
applicationId "org.ole.planet.myplanet"
minSdkVersion 26
targetSdkVersion 34
versionCode 2213
versionName "0.22.13"
versionCode 2220
versionName "0.22.20"
ndkVersion '21.3.6528147'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/java/org/ole/planet/myplanet/MainApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.ole.planet.myplanet.MainApplication
import org.ole.planet.myplanet.base.BaseResourceFragment.Companion.backgroundDownload
import org.ole.planet.myplanet.base.BaseResourceFragment.Companion.getAllLibraryList
import org.ole.planet.myplanet.callback.TeamPageListener
Expand Down Expand Up @@ -64,7 +63,6 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
var isCollectionSwitchOn = false
var showDownload = false
var isSyncRunning = false
var showHealthDialog = true
var listener: TeamPageListener? = null
val androidId: String get() {
try {
Expand Down
74 changes: 63 additions & 11 deletions app/src/main/java/org/ole/planet/myplanet/datamanager/ApiClient.kt
Original file line number Diff line number Diff line change
@@ -1,33 +1,85 @@
package org.ole.planet.myplanet.datamanager

import com.google.gson.GsonBuilder
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.io.IOException
import java.lang.reflect.Modifier
import java.util.concurrent.TimeUnit
import kotlin.math.pow

object ApiClient {
private const val BASE_URL = "https://vi.media.mit.edu/"
private var retrofit: Retrofit? = null
@JvmStatic
val client: Retrofit?
get() {
val client = OkHttpClient.Builder().connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).build()
val client = OkHttpClient.Builder().connectTimeout(1, TimeUnit.MINUTES).readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS).addInterceptor { chain ->
val request = chain.request().newBuilder()
.addHeader("Accept-Encoding", "gzip").build()
chain.proceed(request)
}
.retryOnConnectionFailure(true).addInterceptor(RetryInterceptor()).build()
if (retrofit == null) {
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(
GsonConverterFactory.create(
GsonBuilder()
.excludeFieldsWithModifiers(Modifier.FINAL, Modifier.TRANSIENT, Modifier.STATIC)
.serializeNulls()
.create()
.baseUrl(BASE_URL).client(client)
.addConverterFactory(GsonConverterFactory.create(
GsonBuilder()
.excludeFieldsWithModifiers(Modifier.FINAL, Modifier.TRANSIENT, Modifier.STATIC)
.serializeNulls()
.create()
)
).build()
}
return retrofit
}
}

class RetryInterceptor : Interceptor {
val maxRetryCount = 3
val retryDelayMillis = 1000L

override fun intercept(chain: Interceptor.Chain): Response {
var attempt = 0
var response: Response? = null
val request = chain.request()
val url = request.url().toString()

while (true) {
try {
response?.close()
response = chain.proceed(request)

when (response.code()) {
in 200..299 -> {
return response
}
404 -> {
return response
}
401, 403 -> {
response.close()
throw IOException("Authentication failed: ${response.code()}")
}
else -> {
response.close()
throw IOException("Response unsuccessful: ${response.code()}")
}
}
} catch (e: IOException) {
attempt++

if (attempt >= maxRetryCount) {
throw IOException("Failed after $maxRetryCount attempts: $url", e)
}

val delayMillis = retryDelayMillis * 2.0.pow((attempt - 1).toDouble()).toLong()
Thread.sleep(delayMillis)
}
}
}
}
}
140 changes: 109 additions & 31 deletions app/src/main/java/org/ole/planet/myplanet/service/SyncManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class SyncManager private constructor(private val context: Context) {
settings.edit().putString("LastWifiSSID", wifiInfo.ssid).apply()
}
isSyncing = true
create(context, R.mipmap.ic_launcher, " Syncing data", "Please wait...")
create(context, R.mipmap.ic_launcher, "Syncing data", "Please wait...")
mRealm = dbService.realmInstance
TransactionSyncManager.syncDb(mRealm, "tablet_users")
myLibraryTransactionSync()
Expand Down Expand Up @@ -144,50 +144,111 @@ class SyncManager private constructor(private val context: Context) {
}
}

@Throws(IOException::class)
private fun syncResource(dbClient: ApiInterface?) {
val newIds: MutableList<String?> = ArrayList()
val allDocs = dbClient?.getJsonObject(Utilities.header, Utilities.getUrl() + "/resources/_all_docs?include_doc=false")
val all = allDocs?.execute()
val rows = getJsonArray("rows", all?.body())
val keys: MutableList<String> = ArrayList()
for (i in 0 until rows.size()) {
val `object` = rows[i].asJsonObject
if (!TextUtils.isEmpty(getString("id", `object`))) keys.add(getString("key", `object`))
if (i == rows.size() - 1 || keys.size == 1000) {
val obj = JsonObject()
obj.add("keys", Gson().fromJson(Gson().toJson(keys), JsonArray::class.java))
val response = dbClient?.findDocs(Utilities.header, "application/json", Utilities.getUrl() + "/resources/_all_docs?include_docs=true", obj)?.execute()
if (response?.body() != null) {
val ids: List<String?> = save(getJsonArray("rows", response.body()), mRealm)
newIds.addAll(ids)
try {
val allDocs = dbClient?.getJsonObject(Utilities.header, "${Utilities.getUrl()}/resources/_all_docs?include_doc=false")
val all = allDocs?.execute()
if (all?.isSuccessful != true) {
return
}

val rows = getJsonArray("rows", all.body())
val keys: MutableList<String> = ArrayList()
val failedIds: MutableList<String> = ArrayList()

for (i in 0 until rows.size()) {
val `object` = rows[i].asJsonObject
if (!TextUtils.isEmpty(getString("id", `object`))) {
keys.add(getString("key", `object`))
}

if (i == rows.size() - 1 || keys.size == 1000) {
val obj = JsonObject()
obj.add("keys", Gson().fromJson(Gson().toJson(keys), JsonArray::class.java))
val response = dbClient.findDocs(Utilities.header, "application/json", "${Utilities.getUrl()}/resources/_all_docs?include_docs=true", obj).execute()

when {
response.isSuccessful == true -> {
response.body()?.let { body ->
val ids: List<String?> = save(getJsonArray("rows", body), mRealm)
newIds.addAll(ids)
}
}
response.code() == 404 -> {
failedIds.addAll(keys)
}
else -> {
val errorMessage = "Failed to sync resources: ${response.code()}"
handleException(errorMessage)

when (response.code()) {
in 500..599 -> {
addToRetryQueue(keys)
}
401, 403 -> {
handleAuthenticationError()
}
else -> {
failedIds.addAll(keys)
}
}
}
}
keys.clear()
}
keys.clear()
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
removeDeletedResource(newIds, mRealm)
} catch (e: Exception) {
e.printStackTrace()
}
}
removeDeletedResource(newIds, mRealm)
}

private fun addToRetryQueue(keys: List<String>) {
settings.edit().apply {
val existingQueue = settings.getStringSet("retry_queue", setOf()) ?: setOf()
putStringSet("retry_queue", existingQueue + keys)
apply()
}
}

private fun handleAuthenticationError() {
settings.edit().remove("credentials").apply()
handleException("Authentication failed.")
}

private fun myLibraryTransactionSync() {
val apiInterface = client?.create(ApiInterface::class.java)
try {
val res = apiInterface?.getDocuments(Utilities.header, Utilities.getUrl() + "/shelf/_all_docs")?.execute()?.body()
for (i in res?.rows!!.indices) {
shelfDoc = res.rows!![i]
populateShelfItems(apiInterface)
val response = apiInterface?.getDocuments(Utilities.header, "${Utilities.getUrl()}/shelf/_all_docs")?.execute()

val res = response?.body()
res?.rows?.let { rows ->
for (i in rows.indices) {
shelfDoc = rows[i]
populateShelfItems(apiInterface)
}
}
} catch (e: IOException) {
e.printStackTrace()
}
}

private fun populateShelfItems(apiInterface: ApiInterface) {
private fun populateShelfItems(apiInterface: ApiInterface?) {
try {
val jsonDoc = apiInterface.getJsonObject(Utilities.header, Utilities.getUrl() + "/shelf/" + shelfDoc?.id).execute().body()
for (i in Constants.shelfDataList.indices) {
val shelfData = Constants.shelfDataList[i]
val array = getJsonArray(shelfData.key, jsonDoc)
memberShelfData(array, shelfData)
val response = apiInterface?.getJsonObject(Utilities.header, "${Utilities.getUrl()}/shelf/${shelfDoc?.id}")?.execute()

response?.body()?.let { jsonDoc ->
for (i in Constants.shelfDataList.indices) {
val shelfData = Constants.shelfDataList[i]
val array = getJsonArray(shelfData.key, jsonDoc)
memberShelfData(array, shelfData)
}
}
} catch (err: Exception) {
err.printStackTrace()
Expand Down Expand Up @@ -217,10 +278,27 @@ class SyncManager private constructor(private val context: Context) {
}

private fun validateDocument(arrayCategoryIds: JsonArray, x: Int) {
val apiInterface = client!!.create(ApiInterface::class.java)
val apiInterface = client?.create(ApiInterface::class.java)
try {
val resourceDoc = apiInterface.getJsonObject(Utilities.header, Utilities.getUrl() + "/" + stringArray[2] + "/" + arrayCategoryIds[x].asString).execute().body()
resourceDoc?.let { triggerInsert(stringArray, it) }
val response = apiInterface?.getJsonObject(Utilities.header, "${Utilities.getUrl()}/${stringArray[2]}/${arrayCategoryIds[x].asString}")?.execute()

when {
response?.isSuccessful == true -> {
response.body()?.let { resourceDoc ->
triggerInsert(stringArray, resourceDoc)
}
}
response?.code() == 404 -> {
return
}
else -> {
val errorMessage = "Failed to validate document: ${response?.code()}"
handleException(errorMessage)
if (response?.code() in 500..599) {
throw IOException(errorMessage)
}
}
}
} catch (e: IOException) {
e.printStackTrace()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ class CoursesFragment : BaseRecyclerFragment<RealmMyCourse?>(), OnCourseItemSele
spnGrade.adapter = gradeAdapter

val subjectAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.subject_level, R.layout.spinner_item)
subjectAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
subjectAdapter.setDropDownViewResource(R.layout.custom_simple_list_item_1)
spnSubject.adapter = subjectAdapter

spnGrade.onItemSelectedListener = itemSelectedListener
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,6 @@ open class BaseDashboardFragment : BaseDashboardFragmentPlugin(), NotificationCa
}

override fun syncKeyId() {
di = DialogUtils.CustomProgressDialog(requireContext())
di?.setText(getString(R.string.syncing_health_please_wait))
if (model?.getRoleAsString()?.contains("health") == true) {
settings?.let { TransactionSyncManager.syncAllHealthData(mRealm, it, this) }
} else {
Expand All @@ -400,12 +398,10 @@ open class BaseDashboardFragment : BaseDashboardFragmentPlugin(), NotificationCa

override fun onSyncComplete() {
di?.dismiss()
Utilities.toast(activity, getString(R.string.myhealth_synced_successfully))
}

override fun onSyncFailed(msg: String?) {
di?.dismiss()
Utilities.toast(activity, getString(R.string.myhealth_synced_failed))
}

override fun showTaskListDialog() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.ole.planet.myplanet.ui.dashboard

import android.content.DialogInterface
import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
Expand All @@ -19,7 +18,6 @@ import androidx.recyclerview.widget.RecyclerView
import io.realm.Case
import io.realm.Realm
import kotlinx.coroutines.*
import org.ole.planet.myplanet.MainApplication
import org.ole.planet.myplanet.R
import org.ole.planet.myplanet.databinding.FragmentHomeBellBinding
import org.ole.planet.myplanet.model.RealmCertification
Expand Down Expand Up @@ -66,13 +64,8 @@ class BellDashboardFragment : BaseDashboardFragment() {
showBadges()
checkPendingSurveys()

if (model?.id?.startsWith("guest") == false && TextUtils.isEmpty(model?.key) && MainApplication.showHealthDialog) {
AlertDialog.Builder(requireActivity(), R.style.CustomAlertDialog)
.setMessage(getString(R.string.health_record_not_available_sync_health_data))
.setPositiveButton(getString(R.string.sync)) { _: DialogInterface?, _: Int ->
syncKeyId()
MainApplication.showHealthDialog = false
}.setNegativeButton(getString(R.string.cancel), null).show()
if (model?.id?.startsWith("guest") == false && TextUtils.isEmpty(model?.key)) {
syncKeyId()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,12 @@ class MySubmissionFragment : Fragment(), CompoundButton.OnCheckedChangeListener

val adapter = AdapterMySubmission(requireActivity(), submissions, exams)
val itemCount = adapter.itemCount
showNoData(fragmentMySubmissionBinding.tvMessage, itemCount, "submission")

if (itemCount == 0) {
fragmentMySubmissionBinding.llSearch.visibility = View.GONE
fragmentMySubmissionBinding.title.visibility = View.GONE
if(s.isNullOrEmpty()){
showNoData(fragmentMySubmissionBinding.tvMessage, itemCount, "submission")
if (itemCount == 0) {
fragmentMySubmissionBinding.llSearch.visibility = View.GONE
fragmentMySubmissionBinding.title.visibility = View.GONE
}
}
adapter.setmRealm(mRealm)
adapter.setType(type)
Expand Down
Loading

0 comments on commit 0f804f6

Please sign in to comment.