Compare commits

...

12 Commits

Author SHA1 Message Date
316027ca3b Tag for build. 2022-12-14 21:26:33 +01:00
9d58fba5c9 Cleaning. 2022-12-14 21:07:03 +01:00
284c19ef89 More cleaning. 2022-12-14 20:54:48 +01:00
7cfd17231a Cleaning. 2022-12-13 22:22:31 +01:00
527830a5ae Merge branch 'sonar-qube' 2022-12-13 21:53:10 +01:00
c4ed30f594 Fixes #112. 2022-12-13 21:32:48 +01:00
156c1681cf Fixes #111. 2022-12-13 21:19:05 +01:00
3593fbca78 Sonar scanner. 2022-12-13 21:11:38 +01:00
430fc8e8cb Fixes #110. 2022-12-13 20:40:50 +01:00
4fce19bad4 Trying to set code coverage. 2022-12-13 20:29:51 +01:00
49f5848e7b Merge pull request 'Fixes #108.' (#109) from bug/lateinit into master
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/109
2022-12-12 20:15:44 +00:00
90452100a4 Fixes #108. 2022-12-12 21:11:26 +01:00
12 changed files with 154 additions and 623 deletions

View File

@ -3,27 +3,34 @@ type: docker
name: test name: test
steps: steps:
- name: AnylyseBuildTest - name: BuildAndTest
image: mingc/android-build-box:latest image: mingc/android-build-box:latest
commands: commands:
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- echo "Configure gradle..." - echo "Configure gradle..."
- mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\npushCache=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties - mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\npushCache=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- echo "Analysing..."
- ./gradlew sonarqube -Dsonar.projectKey=RFS2 -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN
- echo "---------------------------------------------------------"
- echo "Building..." - echo "Building..."
- ./gradlew build - ./gradlew build -x test
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- echo "Testing..." - echo "Testing..."
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- ./gradlew test - ./gradlew koverMergedXmlReport
environment: environment:
SONAR_HOST_URL: SONAR_HOST_URL:
from_secret: sonarScannerHostUrl from_secret: sonarScannerHostUrl
SONAR_LOGIN: SONAR_LOGIN:
from_secret: sonarScannerLogin from_secret: sonarScannerLogin
- name: Analyse
image: kytay/sonar-node-plugin
settings:
sonar_host:
from_secret: sonarScannerHostUrl
sonar_token:
from_secret: sonarScannerLogin
use_node_version: 16.18.1
sonar_debug: true
sonar_project_settings: ./sonar-project.properties
trigger: trigger:
event: event:
- push - push
@ -88,6 +95,9 @@ steps:
- name: build - name: build
image: mingc/android-build-box:latest image: mingc/android-build-box:latest
commands: commands:
- echo "---------------------------------------------------------"
- echo "Fetch tags..."
- git fetch --tags
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- echo "Configure gradle..." - echo "Configure gradle..."
- mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\npushCache=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties - mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\npushCache=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties

View File

@ -8,6 +8,7 @@ plugins {
kotlin("android") kotlin("android")
kotlin("kapt") kotlin("kapt")
id("com.mikepenz.aboutlibraries.plugin") id("com.mikepenz.aboutlibraries.plugin")
id("org.jetbrains.kotlinx.kover") version "0.6.1"
} }
fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String { fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {

View File

@ -31,7 +31,6 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.ItemType import bou.amine.apps.readerforselfossv2.utils.ItemType
import bou.amine.apps.readerforselfossv2.utils.longHash
import com.ashokvarma.bottomnavigation.BottomNavigationBar import com.ashokvarma.bottomnavigation.BottomNavigationBar
import com.ashokvarma.bottomnavigation.BottomNavigationItem import com.ashokvarma.bottomnavigation.BottomNavigationItem
import com.ashokvarma.bottomnavigation.TextBadgeItem import com.ashokvarma.bottomnavigation.TextBadgeItem
@ -66,8 +65,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
private var fromTabShortcut: Boolean = false private var fromTabShortcut: Boolean = false
private lateinit var tagsBadge: Map<Long, Int>
private val settingsLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { private val settingsLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
appSettingsService.refreshUserSettings() appSettingsService.refreshUserSettings()
} }
@ -156,15 +153,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
adapter.handleItemAtIndex(position) adapter.handleItemAtIndex(position)
val tagHashes = i.tags.map { it.longHash() }
tagsBadge = tagsBadge.map {
if (tagHashes.contains(it.key)) {
(it.key to (it.value - 1))
} else {
(it.key to it.value)
}
}.toMap()
// Just load everythin // Just load everythin
if (items.size <= 0) { if (items.size <= 0) {
getElementsAccordingToTab() getElementsAccordingToTab()

View File

@ -70,7 +70,7 @@ class ArticleFragment : Fragment(), DIAware {
private lateinit var fab: FloatingActionButton private lateinit var fab: FloatingActionButton
private lateinit var textAlignment: String private lateinit var textAlignment: String
private var _binding: FragmentArticleBinding? = null private var _binding: FragmentArticleBinding? = null
private val binding get() = _binding!! private val binding get() = _binding
override val di : DI by closestDI() override val di : DI by closestDI()
private val repository: Repository by instance() private val repository: Repository by instance()
@ -113,13 +113,13 @@ class ArticleFragment : Fragment(), DIAware {
refreshAlignment() refreshAlignment()
fab = binding.fab fab = binding!!.fab
fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent)) fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent))
fab.rippleColor = resources.getColor(R.color.colorAccentDark) fab.rippleColor = resources.getColor(R.color.colorAccentDark)
val floatingToolbar: FloatingToolbar = binding.floatingToolbar val floatingToolbar: FloatingToolbar = binding!!.floatingToolbar
floatingToolbar.attachFab(fab) floatingToolbar.attachFab(fab)
floatingToolbar.background = ColorDrawable(resources.getColor(R.color.colorAccent)) floatingToolbar.background = ColorDrawable(resources.getColor(R.color.colorAccent))
@ -167,35 +167,35 @@ class ArticleFragment : Fragment(), DIAware {
floatingToolbar.show() floatingToolbar.show()
} }
binding.source.text = contentSource binding!!.source.text = contentSource
if (typeface != null) { if (typeface != null) {
binding.source.typeface = typeface binding!!.source.typeface = typeface
} }
if (contentText.isEmptyOrNullOrNullString()) { if (contentText.isEmptyOrNullOrNullString()) {
getContentFromMercury() getContentFromMercury()
} else { } else {
binding.titleView.text = contentTitle binding!!.titleView.text = contentTitle
if (typeface != null) { if (typeface != null) {
binding.titleView.typeface = typeface binding!!.titleView.typeface = typeface
} }
htmlToWebview() htmlToWebview()
if (!contentImage.isEmptyOrNullOrNullString() && context != null) { if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
binding.imageView.visibility = View.VISIBLE binding!!.imageView.visibility = View.VISIBLE
Glide Glide
.with(requireContext()) .with(requireContext())
.asBitmap() .asBitmap()
.load(contentImage) .load(contentImage)
.apply(RequestOptions.fitCenterTransform()) .apply(RequestOptions.fitCenterTransform())
.into(binding.imageView) .into(binding!!.imageView)
} else { } else {
binding.imageView.visibility = View.GONE binding!!.imageView.visibility = View.GONE
} }
} }
binding.nestedScrollView.setOnScrollChangeListener( binding!!.nestedScrollView.setOnScrollChangeListener(
NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY -> NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
if (scrollY > oldScrollY) { if (scrollY > oldScrollY) {
floatingToolbar.hide() floatingToolbar.hide()
@ -224,7 +224,7 @@ class ArticleFragment : Fragment(), DIAware {
.show() .show()
} }
return binding.root return binding!!.root
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -242,16 +242,16 @@ class ArticleFragment : Fragment(), DIAware {
private fun getContentFromMercury() { private fun getContentFromMercury() {
if (repository.isNetworkAvailable()) { if (repository.isNetworkAvailable()) {
binding.progressBar.visibility = View.VISIBLE binding!!.progressBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
try { try {
val response = mercuryApi.query(url) val response = mercuryApi.query(url)
if (response.success && response.data != null && !response.data?.content.isNullOrEmpty()) { if (response.success && response.data != null && !response.data?.content.isNullOrEmpty()) {
binding.titleView.text = response.data!!.title.orEmpty() binding!!.titleView.text = response.data!!.title.orEmpty()
try { try {
if (typeface != null) { if (typeface != null) {
binding.titleView.typeface = typeface binding!!.titleView.typeface = typeface
} }
} catch (e: Exception) { } catch (e: Exception) {
e.sendSilentlyWithAcraWithName("getContentFromMercury > typeface") e.sendSilentlyWithAcraWithName("getContentFromMercury > typeface")
@ -275,7 +275,7 @@ class ArticleFragment : Fragment(), DIAware {
if (!response.data?.lead_image_url.isNullOrEmpty() && context != null) { if (!response.data?.lead_image_url.isNullOrEmpty() && context != null) {
try { try {
binding.imageView.visibility = View.VISIBLE binding!!.imageView.visibility = View.VISIBLE
try { try {
Glide Glide
.with(requireContext()) .with(requireContext())
@ -284,7 +284,7 @@ class ArticleFragment : Fragment(), DIAware {
response.data!!.lead_image_url.orEmpty() response.data!!.lead_image_url.orEmpty()
) )
.apply(RequestOptions.fitCenterTransform()) .apply(RequestOptions.fitCenterTransform())
.into(binding.imageView) .into(binding!!.imageView)
} catch (e: Exception) { } catch (e: Exception) {
e.sendSilentlyWithAcraWithName("getContentFromMercury > glide lead image") e.sendSilentlyWithAcraWithName("getContentFromMercury > glide lead image")
} }
@ -292,12 +292,12 @@ class ArticleFragment : Fragment(), DIAware {
e.sendSilentlyWithAcraWithName("getContentFromMercury > outside glide lead image") e.sendSilentlyWithAcraWithName("getContentFromMercury > outside glide lead image")
} }
} else { } else {
binding.imageView.visibility = View.GONE binding!!.imageView.visibility = View.GONE
} }
try { try {
binding.nestedScrollView.scrollTo(0, 0) binding!!.nestedScrollView.scrollTo(0, 0)
binding.progressBar.visibility = View.GONE binding!!.progressBar.visibility = View.GONE
} catch (e: Exception) { } catch (e: Exception) {
e.sendSilentlyWithAcraWithName("getContentFromMercury > scrollview") e.sendSilentlyWithAcraWithName("getContentFromMercury > scrollview")
} }
@ -320,8 +320,8 @@ class ArticleFragment : Fragment(), DIAware {
val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs) val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs)
binding.webcontent.settings.standardFontFamily = a.getString(0) binding!!.webcontent.settings.standardFontFamily = a.getString(0)
binding.webcontent.visibility = View.VISIBLE binding!!.webcontent.visibility = View.VISIBLE
val colorOnSurface = TypedValue() val colorOnSurface = TypedValue()
requireContext().theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true) requireContext().theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true)
@ -329,14 +329,14 @@ class ArticleFragment : Fragment(), DIAware {
val colorSurface = TypedValue() val colorSurface = TypedValue()
requireContext().theme.resolveAttribute(R.attr.colorSurface, colorSurface, true) requireContext().theme.resolveAttribute(R.attr.colorSurface, colorSurface, true)
binding.webcontent.settings.useWideViewPort = true binding!!.webcontent.settings.useWideViewPort = true
binding.webcontent.settings.loadWithOverviewMode = true binding!!.webcontent.settings.loadWithOverviewMode = true
binding.webcontent.settings.javaScriptEnabled = false binding!!.webcontent.settings.javaScriptEnabled = false
binding.webcontent.webViewClient = object : WebViewClient() { binding!!.webcontent.webViewClient = object : WebViewClient() {
@Deprecated("Deprecated in Java") @Deprecated("Deprecated in Java")
override fun shouldOverrideUrlLoading(view: WebView?, url : String): Boolean { override fun shouldOverrideUrlLoading(view: WebView?, url : String): Boolean {
if (binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { if (binding!!.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
} }
return true return true
@ -380,9 +380,9 @@ class ArticleFragment : Fragment(), DIAware {
} }
}) })
binding.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event)} binding!!.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event)}
binding.webcontent.settings.layoutAlgorithm = binding!!.webcontent.settings.layoutAlgorithm =
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
var baseUrl: String? = null var baseUrl: String? = null
@ -413,7 +413,7 @@ class ArticleFragment : Fragment(), DIAware {
"" ""
} }
binding.webcontent.loadDataWithBaseURL( binding!!.webcontent.loadDataWithBaseURL(
baseUrl, baseUrl,
"""<html> """<html>
|<head> |<head>
@ -466,17 +466,17 @@ class ArticleFragment : Fragment(), DIAware {
} }
fun scrollDown() { fun scrollDown() {
val height = binding.nestedScrollView.measuredHeight val height = binding!!.nestedScrollView.measuredHeight
binding.nestedScrollView.smoothScrollBy(0, height/2) binding!!.nestedScrollView.smoothScrollBy(0, height/2)
} }
fun scrollUp() { fun scrollUp() {
val height = binding.nestedScrollView.measuredHeight val height = binding!!.nestedScrollView.measuredHeight
binding.nestedScrollView.smoothScrollBy(0, -height/2) binding!!.nestedScrollView.smoothScrollBy(0, -height/2)
} }
private fun openInBrowserAfterFailing() { private fun openInBrowserAfterFailing() {
binding.progressBar.visibility = View.GONE binding!!.progressBar.visibility = View.GONE
requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item) requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item)
} }
@ -495,10 +495,10 @@ class ArticleFragment : Fragment(), DIAware {
} }
fun performClick(): Boolean { fun performClick(): Boolean {
if (binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE || if (binding!!.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { binding!!.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
val position : Int = allImages.indexOf(binding.webcontent.hitTestResult.extra) val position : Int = allImages.indexOf(binding!!.webcontent.hitTestResult.extra)
val intent = Intent(activity, ImageActivity::class.java) val intent = Intent(activity, ImageActivity::class.java)
intent.putExtra("allImages", allImages) intent.putExtra("allImages", allImages)

View File

@ -105,7 +105,7 @@
<string name="new_items_notification_text">%1$d new items loaded.</string> <string name="new_items_notification_text">%1$d new items loaded.</string>
<string name="pref_switch_notify_new_items">Notify on new items synced.</string> <string name="pref_switch_notify_new_items">Notify on new items synced.</string>
<string name="shortcut_offline">Offline</string> <string name="shortcut_offline">Offline</string>
<string name="pref_api_timeout">Api Timeout</string> <string name="pref_api_timeout">Api Timeout (seconds)</string>
<string name="pref_header_experimental">Experimental</string> <string name="pref_header_experimental">Experimental</string>
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string> <string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
<string name="webview_dialog_issue_title">Webview issue</string> <string name="webview_dialog_issue_title">Webview issue</string>

View File

@ -369,14 +369,7 @@ class RepositoryTest {
@Test @Test
fun get_tags() { fun get_tags() {
val tags = listOf( val (tags, tagsDB) = prepareTags()
SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0)
)
val tagsDB = listOf(
TAG("test_DB", "red", 6),
TAG("second_DB", "yellow", 0)
)
coEvery { api.tags() } returns StatusAndData(success = true, data = tags) coEvery { api.tags() } returns StatusAndData(success = true, data = tags)
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
@ -396,17 +389,7 @@ class RepositoryTest {
@Test @Test
fun get_tags_with_sources_update_disabled() { fun get_tags_with_sources_update_disabled() {
val tags = listOf( val (tags, tagsDB) = prepareTags()
SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0)
)
val tagsDB = listOf(
TAG("test_DB", "red", 6),
TAG("second_DB", "yellow", 0)
)
coEvery { api.tags() } returns StatusAndData(success = true, data = tags)
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
@ -426,17 +409,7 @@ class RepositoryTest {
@Test @Test
fun get_tags_with_items_caching_disabled() { fun get_tags_with_items_caching_disabled() {
val tags = listOf( val (tags, _) = prepareTags()
SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0)
)
val tagsDB = listOf(
TAG("test_DB", "red", 6),
TAG("second_DB", "yellow", 0)
)
coEvery { api.tags() } returns StatusAndData(success = true, data = tags)
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
every { appSettingsService.isUpdateSourcesEnabled() } returns true every { appSettingsService.isUpdateSourcesEnabled() } returns true
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
@ -453,17 +426,7 @@ class RepositoryTest {
@Test @Test
fun get_tags_with_sources_update_and_items_caching_disabled() { fun get_tags_with_sources_update_and_items_caching_disabled() {
val tags = listOf( val (tags, tagsDB) = prepareTags()
SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0)
)
val tagsDB = listOf(
TAG("test_DB", "red", 6),
TAG("second_DB", "yellow", 0)
)
coEvery { api.tags() } returns StatusAndData(success = true, data = tags)
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
@ -482,17 +445,7 @@ class RepositoryTest {
@Test @Test
fun get_tags_without_connection() { fun get_tags_without_connection() {
val tags = listOf( val (tags, tagsDB) = prepareTags()
SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0)
)
val tagsDB = listOf(
TAG("test_DB", "red", 6),
TAG("second_DB", "yellow", 0)
)
coEvery { api.tags() } returns StatusAndData(success = true, data = tags)
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
every { appSettingsService.isUpdateSourcesEnabled() } returns true every { appSettingsService.isUpdateSourcesEnabled() } returns true
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
@ -510,17 +463,7 @@ class RepositoryTest {
@Test @Test
fun get_tags_without_connection_and_items_caching_disabled() { fun get_tags_without_connection_and_items_caching_disabled() {
val tags = listOf( prepareTags()
SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0)
)
val tagsDB = listOf(
TAG("test_DB", "red", 6),
TAG("second_DB", "yellow", 0)
)
coEvery { api.tags() } returns StatusAndData(success = true, data = tags)
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
every { appSettingsService.isUpdateSourcesEnabled() } returns true every { appSettingsService.isUpdateSourcesEnabled() } returns true
@ -537,17 +480,7 @@ class RepositoryTest {
@Test @Test
fun get_tags_without_connection_and_sources_update_disabled() { fun get_tags_without_connection_and_sources_update_disabled() {
val tags = listOf( val (tags, tagsDB) = prepareTags()
SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0)
)
val tagsDB = listOf(
TAG("test_DB", "red", 6),
TAG("second_DB", "yellow", 0)
)
coEvery { api.tags() } returns StatusAndData(success = true, data = tags)
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
@ -565,17 +498,7 @@ class RepositoryTest {
@Test @Test
fun get_tags_without_connection_and_sources_update_and_items_caching_disabled() { fun get_tags_without_connection_and_sources_update_and_items_caching_disabled() {
val tags = listOf( val (_, tagsDB) = prepareTags()
SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0)
)
val tagsDB = listOf(
TAG("test_DB", "red", 6),
TAG("second_DB", "yellow", 0)
)
coEvery { api.tags() } returns StatusAndData(success = true, data = tags)
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
@ -590,8 +513,36 @@ class RepositoryTest {
verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() } verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
} }
private fun prepareTags(): Pair<List<SelfossModel.Tag>, List<TAG>> {
val tags = listOf(
SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0)
)
val tagsDB = listOf(
TAG("test_DB", "red", 6),
TAG("second_DB", "yellow", 0)
)
coEvery { api.tags() } returns StatusAndData(success = true, data = tags)
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
return Pair(tags, tagsDB)
}
@Test @Test
fun get_sources() { fun get_sources() {
val (sources, sourcesDB) = prepareSources()
initializeRepository()
var testSources: List<SelfossModel.Source>?
runBlocking {
testSources = repository.getSources()
}
assertSame(sources, testSources)
assertNotEquals(sourcesDB.map { it.toView() }, testSources)
coVerify(exactly = 1) { api.sources() }
}
private fun prepareSources(): Pair<ArrayList<SelfossModel.Source>, List<SOURCE>> {
val sources = arrayListOf( val sources = arrayListOf(
SelfossModel.Source( SelfossModel.Source(
1, 1,
@ -631,60 +582,15 @@ class RepositoryTest {
coEvery { api.sources() } returns StatusAndData(success = true, data = sources) coEvery { api.sources() } returns StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository() return Pair(sources, sourcesDB)
var testSources: List<SelfossModel.Source>?
runBlocking {
testSources = repository.getSources()
}
assertSame(sources, testSources)
assertNotEquals(sourcesDB.map { it.toView() }, testSources)
coVerify(exactly = 1) { api.sources() }
} }
@Test @Test
fun get_sources_with_sources_update_disabled() { fun get_sources_with_sources_update_disabled() {
val sources = arrayListOf( val (sources, sourcesDB) = prepareSources()
SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SelfossModel.Source(
2,
"Second DB source",
listOf("second"),
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
val sourcesDB = listOf(
SOURCE(
"1",
"First source",
"Test,second",
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SOURCE(
"2",
"Second source",
"second",
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
coEvery { api.sources() } returns StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository() initializeRepository()
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>?
runBlocking { runBlocking {
@ -701,47 +607,10 @@ class RepositoryTest {
@Test @Test
fun get_sources_with_items_caching_disabled() { fun get_sources_with_items_caching_disabled() {
val sources = arrayListOf( val (sources, _) = prepareSources()
SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SelfossModel.Source(
2,
"Second source",
listOf("second"),
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
val sourcesDB = listOf(
SOURCE(
"1",
"First source",
"Test,second",
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SOURCE(
"2",
"Second source",
"second",
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
every { appSettingsService.isUpdateSourcesEnabled() } returns true every { appSettingsService.isUpdateSourcesEnabled() } returns true
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
coEvery { api.sources() } returns StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository() initializeRepository()
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>?
runBlocking { runBlocking {
@ -755,47 +624,10 @@ class RepositoryTest {
@Test @Test
fun get_sources_with_sources_update_and_items_caching_disabled() { fun get_sources_with_sources_update_and_items_caching_disabled() {
val sources = arrayListOf( val (sources, _) = prepareSources()
SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SelfossModel.Source(
2,
"Second source",
listOf("second"),
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
val sourcesDB = listOf(
SOURCE(
"1",
"First source",
"Test,second",
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SOURCE(
"2",
"Second source",
"second",
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
coEvery { api.sources() } returns StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository() initializeRepository()
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>?
runBlocking { runBlocking {
@ -809,45 +641,7 @@ class RepositoryTest {
@Test @Test
fun get_sources_without_connection() { fun get_sources_without_connection() {
val sources = arrayListOf( val (_, sourcesDB) = prepareSources()
SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SelfossModel.Source(
2,
"Second source",
listOf("second"),
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
val sourcesDB = listOf(
SOURCE(
"1",
"First DB source",
"Test,second",
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SOURCE(
"2",
"Second source",
"second",
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
coEvery { api.sources() } returns StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>?
runBlocking { runBlocking {
@ -861,47 +655,10 @@ class RepositoryTest {
@Test @Test
fun get_sources_without_connection_and_items_caching_disabled() { fun get_sources_without_connection_and_items_caching_disabled() {
val sources = arrayListOf( prepareSources()
SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SelfossModel.Source(
2,
"Second source",
listOf("second"),
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
val sourcesDB = listOf(
SOURCE(
"1",
"First DB source",
"Test,second",
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SOURCE(
"2",
"Second source",
"second",
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
every { appSettingsService.isUpdateSourcesEnabled() } returns true every { appSettingsService.isUpdateSourcesEnabled() } returns true
coEvery { api.sources() } returns StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>?
runBlocking { runBlocking {
@ -915,47 +672,10 @@ class RepositoryTest {
@Test @Test
fun get_sources_without_connection_and_sources_update_disabled() { fun get_sources_without_connection_and_sources_update_disabled() {
val sources = arrayListOf( val (_, sourcesDB) = prepareSources()
SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SelfossModel.Source(
2,
"Second source",
listOf("second"),
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
val sourcesDB = listOf(
SOURCE(
"1",
"First DB source",
"Test,second",
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SOURCE(
"2",
"Second source",
"second",
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false
coEvery { api.sources() } returns StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>?
runBlocking { runBlocking {
@ -969,47 +689,10 @@ class RepositoryTest {
@Test @Test
fun get_sources_without_connection_and_items_caching_and_sources_update_disabled() { fun get_sources_without_connection_and_items_caching_and_sources_update_disabled() {
val sources = arrayListOf( val (_, sourcesDB) = prepareSources()
SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SelfossModel.Source(
2,
"Second source",
listOf("second"),
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
val sourcesDB = listOf(
SOURCE(
"1",
"First DB source",
"Test,second",
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
),
SOURCE(
"2",
"Second source",
"second",
"spouts\\rss\\fulltextrss",
"",
"b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
)
)
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
every { appSettingsService.isUpdateSourcesEnabled() } returns false every { appSettingsService.isUpdateSourcesEnabled() } returns false
coEvery { api.sources() } returns StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>?
runBlocking { runBlocking {
@ -1302,16 +985,7 @@ class RepositoryTest {
) )
initializeRepository() initializeRepository()
repository.setTagFilter(SelfossModel.Tag("Tag", "read", 0)) prepareSearch()
repository.setSourceFilter(SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
))
repository.searchFilter = "search"
runBlocking { runBlocking {
repository.tryToCacheItemsAndGetNewOnes() repository.tryToCacheItemsAndGetNewOnes()
} }
@ -1325,16 +999,7 @@ class RepositoryTest {
StatusAndData(success = false, data = generateTestApiItem()) StatusAndData(success = false, data = generateTestApiItem())
initializeRepository() initializeRepository()
repository.setTagFilter(SelfossModel.Tag("Tag", "read", 0)) prepareSearch()
repository.setSourceFilter(SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
))
repository.searchFilter = "search"
runBlocking { runBlocking {
repository.tryToCacheItemsAndGetNewOnes() repository.tryToCacheItemsAndGetNewOnes()
} }
@ -1348,20 +1013,26 @@ class RepositoryTest {
StatusAndData(success = false, data = generateTestApiItem()) StatusAndData(success = false, data = generateTestApiItem())
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
repository.setTagFilter(SelfossModel.Tag("Tag", "read", 0)) prepareSearch()
repository.setSourceFilter(SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
))
repository.searchFilter = "search"
runBlocking { runBlocking {
repository.tryToCacheItemsAndGetNewOnes() repository.tryToCacheItemsAndGetNewOnes()
} }
coVerify(exactly = 0) { api.getItems(any(), 0, null, null, null, null, 200) } coVerify(exactly = 0) { api.getItems(any(), 0, null, null, null, null, 200) }
} }
private fun prepareSearch() {
repository.setTagFilter(SelfossModel.Tag("Tag", "read", 0))
repository.setSourceFilter(
SelfossModel.Source(
1,
"First source",
listOf("Test", "second"),
"spouts\\rss\\fulltextrss",
"",
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
)
)
repository.searchFilter = "search"
}
} }

View File

@ -11,12 +11,10 @@ plugins {
id("com.android.library").version("7.3.1").apply(false) id("com.android.library").version("7.3.1").apply(false)
kotlin("android").version("1.7.20").apply(false) kotlin("android").version("1.7.20").apply(false)
kotlin("multiplatform").version("1.7.20").apply(false) kotlin("multiplatform").version("1.7.20").apply(false)
id("org.sonarqube").version("3.4.0.2513").apply(false)
id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").apply(false) id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").apply(false)
id("org.jetbrains.kotlinx.kover") version "0.6.1"
} }
apply(plugin = "org.sonarqube")
allprojects { allprojects {
repositories { repositories {
google() google()
@ -30,3 +28,7 @@ allprojects {
tasks.register("clean", Delete::class) { tasks.register("clean", Delete::class) {
delete(rootProject.buildDir) delete(rootProject.buildDir)
} }
koverMerged {
enable()
}

View File

@ -10,6 +10,7 @@ plugins {
id("com.android.library") id("com.android.library")
id("com.squareup.sqldelight") id("com.squareup.sqldelight")
kotlin("plugin.serialization") version "1.4.10" kotlin("plugin.serialization") version "1.4.10"
id("org.jetbrains.kotlinx.kover") version "0.6.1"
} }
kotlin { kotlin {

View File

@ -1,15 +1,6 @@
package bou.amine.apps.readerforselfossv2.model package bou.amine.apps.readerforselfossv2.model
import bou.amine.apps.readerforselfossv2.utils.DateUtils
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.*
class MercuryModel { class MercuryModel {
@ -20,138 +11,4 @@ class MercuryModel {
val lead_image_url: String?, val lead_image_url: String?,
val url: String val url: String
) )
@Serializable
data class Tag(
val tag: String,
val color: String,
val unread: Int
)
@Serializable
class Stats(
val total: Int,
val unread: Int,
val starred: Int
)
@Serializable
data class Spout(
val name: String,
val description: String
)
@Serializable
data class ApiVersion(
val version: String?,
val apiversion: String?
) {
fun getApiMajorVersion() : Int {
var versionNumber = 0
if (apiversion != null) {
versionNumber = apiversion.substringBefore(".").toInt()
}
return versionNumber
}
}
@Serializable
data class Source(
val id: Int,
val title: String,
@Serializable(with = TagsListSerializer::class)
val tags: List<String>,
val spout: String,
val error: String,
val icon: String?
)
@Serializable
data class Item(
val id: Int,
val datetime: String,
val title: String,
val content: String,
@Serializable(with = BooleanSerializer::class)
var unread: Boolean,
@Serializable(with = BooleanSerializer::class)
var starred: Boolean,
val thumbnail: String?,
val icon: String?,
val link: String,
val sourcetitle: String,
@Serializable(with = TagsListSerializer::class)
val tags: List<String>
) {
// TODO: maybe find a better way to handle these kind of urls
fun getLinkDecoded(): String {
var stringUrl: String
stringUrl =
if (link.startsWith("http://news.google.com/news/") || link.startsWith("https://news.google.com/news/")) {
if (link.contains("&amp;url=")) {
link.substringAfter("&amp;url=")
} else {
this.link.replace("&amp;", "&")
}
} else {
this.link.replace("&amp;", "&")
}
// handle :443 => https
if (stringUrl.contains(":443")) {
stringUrl = stringUrl.replace(":443", "").replace("http://", "https://")
}
// handle url not starting with http
if (stringUrl.startsWith("//")) {
stringUrl = "http:$stringUrl"
}
return stringUrl
}
fun sourceAndDateText(): String =
this.sourcetitle.getHtmlDecoded() + DateUtils.parseRelativeDate(this.datetime)
fun toggleStar(): Item {
this.starred = !this.starred
return this
}
}
// TODO: this seems to be super slow.
object TagsListSerializer : KSerializer<List<String>> {
override fun deserialize(decoder: Decoder): List<String> {
return when(val json = ((decoder as JsonDecoder).decodeJsonElement())) {
is JsonArray -> json.toList().map { it.toString() }
else -> json.toString().split(",")
}
}
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: List<String>) {
TODO("Not yet implemented")
}
}
object BooleanSerializer : KSerializer<Boolean> {
override fun deserialize(decoder: Decoder): Boolean {
val json = ((decoder as JsonDecoder).decodeJsonElement()).jsonPrimitive
return if (json.booleanOrNull != null) {
json.boolean
} else {
json.int == 1
}
}
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("b", PrimitiveKind.BOOLEAN)
override fun serialize(encoder: Encoder, value: Boolean) {
TODO("Not yet implemented")
}
}
} }

View File

@ -229,36 +229,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
): SuccessResponse = ): SuccessResponse =
maybeResponse( maybeResponse(
if (appSettingsService.getApiVersion() > 1) { if (appSettingsService.getApiVersion() > 1) {
createSource2(title, url, spout, tags, filter) createSource("tags[]", title, url, spout, tags, filter)
} else { } else {
createSource(title, url, spout, tags, filter) createSource("tags", title, url, spout, tags, filter)
} }
) )
private suspend fun createSource( private suspend fun createSource(
title: String, tagsParamName: String,
url: String,
spout: String,
tags: String,
filter: String
): HttpResponse =
client.submitForm(
url = url("/source"),
formParameters = Parameters.build {
// TODO: test this
if (!shouldHavePostLogin()) {
append("username", appSettingsService.getUserName())
append("password", appSettingsService.getPassword())
}
append("title", title)
append("url", url)
append("spout", spout)
append("tags", tags)
append("filter", filter)
}
)
private suspend fun createSource2(
title: String, title: String,
url: String, url: String,
spout: String, spout: String,
@ -275,7 +253,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
append("title", title) append("title", title)
append("url", url) append("url", url)
append("spout", spout) append("spout", spout)
append("tags[]", tags) append(tagsParamName, tags)
append("filter", filter) append("filter", filter)
} }
) )

View File

@ -84,7 +84,13 @@ class AppSettingsService {
} }
private fun refreshItemsNumber() { private fun refreshItemsNumber() {
_itemsNumber = settings.getString(API_ITEMS_NUMBER, "20").toInt() _itemsNumber = try {
settings.getString(API_ITEMS_NUMBER, "20").toInt()
} catch (e: Exception) {
settings.remove(API_ITEMS_NUMBER)
20
}
} }
fun getApiTimeout(): Long { fun getApiTimeout(): Long {
@ -94,9 +100,21 @@ class AppSettingsService {
return _apiTimeout!! return _apiTimeout!!
} }
private fun secToMs(n: Long) = n * 1000
private fun refreshApiTimeout() { private fun refreshApiTimeout() {
val settingsTimeout = settings.getLong(API_TIMEOUT, HttpTimeout.INFINITE_TIMEOUT_MS) _apiTimeout = secToMs(try {
_apiTimeout = if (settingsTimeout > 0) settingsTimeout else HttpTimeout.INFINITE_TIMEOUT_MS val settingsTimeout = settings.getString(API_TIMEOUT, "60")
if (settingsTimeout.toLong() > 0) {
settingsTimeout.toLong()
} else {
settings.remove(API_TIMEOUT)
60
}
} catch (e: Exception) {
settings.remove(API_TIMEOUT)
60
})
} }
private fun refreshBaseUrl() { private fun refreshBaseUrl() {

5
sonar-project.properties Normal file
View File

@ -0,0 +1,5 @@
sonar.projectKey=RFS2
sonar.coverage.jacoco.xmlReportPaths=build/reports/kover/merged/xml/report.xml
sonar.sourceEncoding=UTF-8
sonar.sources=.
sonar.exclusions=shared/src/iosArm64Main/**, shared/src/iosX64Main/**, docs/**