mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-09 15:27:55 -04:00
Improved the caching mechanism for the language list.
* Improved the language list UI according to new approach. * Fixed: codeFactor and lint issues.
This commit is contained in:
parent
9d3db72aaa
commit
28523ca277
@ -26,7 +26,6 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Checkbox
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
@ -125,20 +125,30 @@ class LanguageViewModel @Inject constructor(
|
|||||||
|
|
||||||
private fun observeLanguages() = viewModelScope.launch {
|
private fun observeLanguages() = viewModelScope.launch {
|
||||||
state.value = Loading
|
state.value = Loading
|
||||||
if (connectivityBroadcastReceiver.networkStates.value == NetworkState.NOT_CONNECTED) {
|
|
||||||
actions.emit(Error(context.getString(R.string.no_network_connection)))
|
val cachedLanguageList = sharedPreferenceUtil.getCachedLanguageList()
|
||||||
|
val isOnline = connectivityBroadcastReceiver.networkStates.value != NetworkState.NOT_CONNECTED
|
||||||
|
|
||||||
|
if (LanguageSessionCache.hasFetched && !cachedLanguageList.isNullOrEmpty()) {
|
||||||
|
actions.emit(UpdateLanguages(cachedLanguageList))
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
val languages = fetchLanguages()
|
if (isOnline) {
|
||||||
if (languages?.isNotEmpty() == true) {
|
runCatching {
|
||||||
actions.emit(UpdateLanguages(languages))
|
val fetched = fetchLanguages()
|
||||||
} else {
|
if (!fetched.isNullOrEmpty()) {
|
||||||
Log.w("LanguageViewModel", "Fetched empty language list.")
|
sharedPreferenceUtil.saveLanguageList(fetched)
|
||||||
actions.emit(Error(context.getString(R.string.no_language_available)))
|
LanguageSessionCache.hasFetched = true
|
||||||
}
|
actions.emit(UpdateLanguages(fetched))
|
||||||
} catch (e: Exception) {
|
return@launch
|
||||||
Log.e("LanguageViewModel", "Error fetching languages", e)
|
}
|
||||||
|
}.onFailure { it.printStackTrace() }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cachedLanguageList.isNullOrEmpty()) {
|
||||||
|
actions.emit(UpdateLanguages(cachedLanguageList))
|
||||||
|
} else {
|
||||||
actions.emit(Error(context.getString(R.string.no_language_available)))
|
actions.emit(Error(context.getString(R.string.no_language_available)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,3 +232,7 @@ class LanguageViewModel @Inject constructor(
|
|||||||
.addNetworkInterceptor(UserAgentInterceptor(USER_AGENT))
|
.addNetworkInterceptor(UserAgentInterceptor(USER_AGENT))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object LanguageSessionCache {
|
||||||
|
var hasFetched: Boolean = false
|
||||||
|
}
|
||||||
|
@ -219,7 +219,7 @@ private fun OnlineLibraryList(state: OnlineLibraryScreenState, lazyListState: La
|
|||||||
val layoutInfo = lazyListState.layoutInfo
|
val layoutInfo = lazyListState.layoutInfo
|
||||||
val totalItems = layoutInfo.totalItemsCount
|
val totalItems = layoutInfo.totalItemsCount
|
||||||
val lastVisibleItemIndex = layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: ZERO
|
val lastVisibleItemIndex = layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: ZERO
|
||||||
(totalItems > ZERO && lastVisibleItemIndex >= (totalItems - FIVE)) to totalItems
|
(totalItems > ZERO && lastVisibleItemIndex >= totalItems.minus(FIVE)) to totalItems
|
||||||
}.value
|
}.value
|
||||||
}
|
}
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
|
@ -18,9 +18,9 @@
|
|||||||
|
|
||||||
package org.kiwix.kiwixmobile.zimManager
|
package org.kiwix.kiwixmobile.zimManager
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.ITEMS_PER_PAGE
|
|
||||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.OPDS_LIBRARY_ENDPOINT
|
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.OPDS_LIBRARY_ENDPOINT
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
||||||
import org.kiwix.kiwixmobile.core.entity.LibkiwixBook
|
import org.kiwix.kiwixmobile.core.entity.LibkiwixBook
|
||||||
@ -34,11 +34,11 @@ import java.io.StringReader
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Named
|
import javax.inject.Named
|
||||||
|
|
||||||
|
@Suppress("UnusedPrivateProperty")
|
||||||
class OnlineLibraryManager @Inject constructor(
|
class OnlineLibraryManager @Inject constructor(
|
||||||
@Named(ONLINE_BOOKS_LIBRARY) private val library: Library,
|
@Named(ONLINE_BOOKS_LIBRARY) private val library: Library,
|
||||||
@Named(ONLINE_BOOKS_MANAGER) private val manager: Manager,
|
@Named(ONLINE_BOOKS_MANAGER) private val manager: Manager,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var totalResult = 0
|
var totalResult = 0
|
||||||
suspend fun parseOPDSStreamAndGetBooks(
|
suspend fun parseOPDSStreamAndGetBooks(
|
||||||
content: String?,
|
content: String?,
|
||||||
@ -124,7 +124,10 @@ class OnlineLibraryManager @Inject constructor(
|
|||||||
*/
|
*/
|
||||||
fun getStartOffset(pageIndex: Int, pageSize: Int): Int = pageIndex * pageSize
|
fun getStartOffset(pageIndex: Int, pageSize: Int): Int = pageIndex * pageSize
|
||||||
|
|
||||||
private suspend fun extractTotalResults(xml: String): Int = withContext(Dispatchers.IO) {
|
private suspend fun extractTotalResults(
|
||||||
|
xml: String,
|
||||||
|
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||||
|
): Int = withContext(dispatcher) {
|
||||||
val factory = XmlPullParserFactory.newInstance()
|
val factory = XmlPullParserFactory.newInstance()
|
||||||
val parser = factory.newPullParser()
|
val parser = factory.newPullParser()
|
||||||
parser.setInput(StringReader(xml))
|
parser.setInput(StringReader(xml))
|
||||||
|
@ -38,7 +38,6 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.debounce
|
import kotlinx.coroutines.flow.debounce
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
@ -134,7 +133,7 @@ const val MAX_PROGRESS = 100
|
|||||||
const val THREE = 3
|
const val THREE = 3
|
||||||
const val FOUR = 4
|
const val FOUR = 4
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList", "LargeClass")
|
||||||
class ZimManageViewModel @Inject constructor(
|
class ZimManageViewModel @Inject constructor(
|
||||||
private val downloadDao: DownloadRoomDao,
|
private val downloadDao: DownloadRoomDao,
|
||||||
private val libkiwixBookOnDisk: LibkiwixBookOnDisk,
|
private val libkiwixBookOnDisk: LibkiwixBookOnDisk,
|
||||||
|
@ -45,4 +45,3 @@ class LanguageEntry {
|
|||||||
@Namespace(prefix = "thr", reference = "http://purl.org/syndication/thread/1.0")
|
@Namespace(prefix = "thr", reference = "http://purl.org/syndication/thread/1.0")
|
||||||
var count: Int = ZERO
|
var count: Int = ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,10 +30,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
import org.kiwix.kiwixmobile.core.DarkModeConfig
|
import org.kiwix.kiwixmobile.core.DarkModeConfig
|
||||||
import org.kiwix.kiwixmobile.core.DarkModeConfig.Mode.Companion.from
|
import org.kiwix.kiwixmobile.core.DarkModeConfig.Mode.Companion.from
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.core.R
|
||||||
import org.kiwix.kiwixmobile.core.extensions.isFileExist
|
import org.kiwix.kiwixmobile.core.extensions.isFileExist
|
||||||
|
import org.kiwix.kiwixmobile.core.zim_manager.Language
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -205,6 +208,40 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) {
|
|||||||
fun putPrefExternalLinkPopup(externalLinkPopup: Boolean) =
|
fun putPrefExternalLinkPopup(externalLinkPopup: Boolean) =
|
||||||
sharedPreferences.edit { putBoolean(PREF_EXTERNAL_LINK_POPUP, externalLinkPopup) }
|
sharedPreferences.edit { putBoolean(PREF_EXTERNAL_LINK_POPUP, externalLinkPopup) }
|
||||||
|
|
||||||
|
fun saveLanguageList(languages: List<Language>) {
|
||||||
|
runCatching {
|
||||||
|
val jsonArray = JSONArray()
|
||||||
|
languages.forEach { lang ->
|
||||||
|
val obj = JSONObject().apply {
|
||||||
|
put(KEY_LANGUAGE_CODE, lang.languageCode)
|
||||||
|
put(KEY_OCCURRENCES_OF_LANGUAGE, lang.occurencesOfLanguage)
|
||||||
|
put(KEY_LANGUAGE_ACTIVE, lang.active)
|
||||||
|
put(KEY_LANGUAGE_ID, lang.id)
|
||||||
|
}
|
||||||
|
jsonArray.put(obj)
|
||||||
|
}
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putString(CACHED_LANGUAGE_CODES, jsonArray.toString())
|
||||||
|
}
|
||||||
|
}.onFailure { it.printStackTrace() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCachedLanguageList(): List<Language>? =
|
||||||
|
runCatching {
|
||||||
|
val jsonString =
|
||||||
|
sharedPreferences.getString(CACHED_LANGUAGE_CODES, null) ?: return@runCatching null
|
||||||
|
val jsonArray = JSONArray(jsonString)
|
||||||
|
List(jsonArray.length()) { i ->
|
||||||
|
val obj = jsonArray.getJSONObject(i)
|
||||||
|
Language(
|
||||||
|
languageCode = obj.getString(KEY_LANGUAGE_CODE),
|
||||||
|
occurrencesOfLanguage = obj.getInt(KEY_OCCURRENCES_OF_LANGUAGE),
|
||||||
|
active = selectedOnlineContentLanguage == obj.getString(KEY_LANGUAGE_CODE),
|
||||||
|
id = obj.getLong(KEY_LANGUAGE_ID)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.onFailure { it.printStackTrace() }.getOrNull()
|
||||||
|
|
||||||
fun showIntro(): Boolean = sharedPreferences.getBoolean(PREF_SHOW_INTRO, true)
|
fun showIntro(): Boolean = sharedPreferences.getBoolean(PREF_SHOW_INTRO, true)
|
||||||
|
|
||||||
fun setIntroShown() = sharedPreferences.edit { putBoolean(PREF_SHOW_INTRO, false) }
|
fun setIntroShown() = sharedPreferences.edit { putBoolean(PREF_SHOW_INTRO, false) }
|
||||||
@ -372,5 +409,10 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) {
|
|||||||
const val PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS =
|
const val PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS =
|
||||||
"pref_last_donation_shown_in_milliseconds"
|
"pref_last_donation_shown_in_milliseconds"
|
||||||
private const val SELECTED_ONLINE_CONTENT_LANGUAGE = "selectedOnlineContentLanguage"
|
private const val SELECTED_ONLINE_CONTENT_LANGUAGE = "selectedOnlineContentLanguage"
|
||||||
|
private const val CACHED_LANGUAGE_CODES = "cachedLanguageCodes"
|
||||||
|
private const val KEY_LANGUAGE_CODE = "languageCode"
|
||||||
|
private const val KEY_OCCURRENCES_OF_LANGUAGE = "occurrencesOfLanguage"
|
||||||
|
private const val KEY_LANGUAGE_ACTIVE = "languageActive"
|
||||||
|
private const val KEY_LANGUAGE_ID = "languageId"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user