forked from Louvorg/ReaderForSelfoss-multiplatform
Compare commits
13 Commits
v122123431
...
v122123483
Author | SHA1 | Date | |
---|---|---|---|
7420adeb5c | |||
316027ca3b | |||
9d58fba5c9 | |||
284c19ef89 | |||
7cfd17231a | |||
527830a5ae | |||
c4ed30f594 | |||
156c1681cf | |||
3593fbca78 | |||
430fc8e8cb | |||
4fce19bad4 | |||
49f5848e7b | |||
90452100a4 |
24
.drone.yml
24
.drone.yml
@ -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,9 +95,12 @@ 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=false\npushCache=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
|
||||||
- echo "---------------------------------------------------------"
|
- echo "---------------------------------------------------------"
|
||||||
- echo "Generate APK"
|
- echo "Generate APK"
|
||||||
- ./gradlew :androidApp:assembleGithubConfigRelease -P pushCache=false
|
- ./gradlew :androidApp:assembleGithubConfigRelease -P pushCache=false
|
||||||
|
@ -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 {
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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>
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
@ -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()
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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("&url=")) {
|
|
||||||
link.substringAfter("&url=")
|
|
||||||
} else {
|
|
||||||
this.link.replace("&", "&")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.link.replace("&", "&")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -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
5
sonar-project.properties
Normal 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/**
|
Reference in New Issue
Block a user