forked from Louvorg/ReaderForSelfoss-multiplatform
		
	Compare commits
	
		
			11 Commits
		
	
	
		
			v122123461
			...
			v122123483
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 7420adeb5c | ||
|  | 316027ca3b | ||
|  | 9d58fba5c9 | ||
|  | 284c19ef89 | ||
|  | 7cfd17231a | ||
|  | 527830a5ae | ||
|  | c4ed30f594 | ||
|  | 156c1681cf | ||
|  | 3593fbca78 | ||
|  | 430fc8e8cb | ||
|  | 4fce19bad4 | 
							
								
								
									
										22
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								.drone.yml
									
									
									
									
									
								
							| @@ -3,27 +3,34 @@ type: docker | ||||
| name: test | ||||
|  | ||||
| steps: | ||||
|   - name: AnylyseBuildTest | ||||
|   - name: BuildAndTest | ||||
|     image: mingc/android-build-box:latest | ||||
|     commands: | ||||
|       - echo "---------------------------------------------------------" | ||||
|       - 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 | ||||
|       - echo "---------------------------------------------------------" | ||||
|       - echo "Analysing..." | ||||
|       - ./gradlew sonarqube -Dsonar.projectKey=RFS2 -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN | ||||
|       - echo "---------------------------------------------------------" | ||||
|       - echo "Building..." | ||||
|       - ./gradlew build -x test | ||||
|       - echo "---------------------------------------------------------" | ||||
|       - echo "Testing..." | ||||
|       - echo "---------------------------------------------------------" | ||||
|       - ./gradlew test | ||||
|       - ./gradlew koverMergedXmlReport | ||||
|     environment: | ||||
|       SONAR_HOST_URL: | ||||
|         from_secret: sonarScannerHostUrl | ||||
|       SONAR_LOGIN: | ||||
|         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: | ||||
|   event: | ||||
|     - push | ||||
| @@ -88,9 +95,12 @@ steps: | ||||
|   - name: build | ||||
|     image: mingc/android-build-box:latest | ||||
|     commands: | ||||
|       - echo "---------------------------------------------------------" | ||||
|       - echo "Fetch tags..." | ||||
|       - git fetch --tags | ||||
|       - echo "---------------------------------------------------------" | ||||
|       - 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 "Generate APK" | ||||
|       - ./gradlew :androidApp:assembleGithubConfigRelease  -P pushCache=false | ||||
|   | ||||
| @@ -8,6 +8,7 @@ plugins { | ||||
|     kotlin("android") | ||||
|     kotlin("kapt") | ||||
|     id("com.mikepenz.aboutlibraries.plugin") | ||||
|     id("org.jetbrains.kotlinx.kover") version "0.6.1" | ||||
| } | ||||
|  | ||||
| fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String { | ||||
|   | ||||
| @@ -70,7 +70,7 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|     private lateinit var fab: FloatingActionButton | ||||
|     private lateinit var textAlignment: String | ||||
|     private var _binding: FragmentArticleBinding? = null | ||||
|     private val binding get() = _binding!! | ||||
|     private val binding get() = _binding | ||||
|  | ||||
|     override val di : DI by closestDI() | ||||
|     private val repository: Repository by instance() | ||||
| @@ -113,13 +113,13 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|  | ||||
|             refreshAlignment() | ||||
|  | ||||
|             fab = binding.fab | ||||
|             fab = binding!!.fab | ||||
|  | ||||
|             fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent)) | ||||
|  | ||||
|             fab.rippleColor = resources.getColor(R.color.colorAccentDark) | ||||
|  | ||||
|             val floatingToolbar: FloatingToolbar = binding.floatingToolbar | ||||
|             val floatingToolbar: FloatingToolbar = binding!!.floatingToolbar | ||||
|             floatingToolbar.attachFab(fab) | ||||
|  | ||||
|             floatingToolbar.background = ColorDrawable(resources.getColor(R.color.colorAccent)) | ||||
| @@ -167,35 +167,35 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|                 floatingToolbar.show() | ||||
|             } | ||||
|  | ||||
|             binding.source.text = contentSource | ||||
|             binding!!.source.text = contentSource | ||||
|             if (typeface != null) { | ||||
|                 binding.source.typeface = typeface | ||||
|                 binding!!.source.typeface = typeface | ||||
|             } | ||||
|  | ||||
|             if (contentText.isEmptyOrNullOrNullString()) { | ||||
|                 getContentFromMercury() | ||||
|             } else { | ||||
|                 binding.titleView.text = contentTitle | ||||
|                 binding!!.titleView.text = contentTitle | ||||
|                 if (typeface != null) { | ||||
|                     binding.titleView.typeface = typeface | ||||
|                     binding!!.titleView.typeface = typeface | ||||
|                 } | ||||
|  | ||||
|                 htmlToWebview() | ||||
|  | ||||
|                 if (!contentImage.isEmptyOrNullOrNullString() && context != null) { | ||||
|                     binding.imageView.visibility = View.VISIBLE | ||||
|                     binding!!.imageView.visibility = View.VISIBLE | ||||
|                     Glide | ||||
|                         .with(requireContext()) | ||||
|                         .asBitmap() | ||||
|                         .load(contentImage) | ||||
|                         .apply(RequestOptions.fitCenterTransform()) | ||||
|                         .into(binding.imageView) | ||||
|                         .into(binding!!.imageView) | ||||
|                 } else { | ||||
|                     binding.imageView.visibility = View.GONE | ||||
|                     binding!!.imageView.visibility = View.GONE | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             binding.nestedScrollView.setOnScrollChangeListener( | ||||
|             binding!!.nestedScrollView.setOnScrollChangeListener( | ||||
|                 NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY -> | ||||
|                     if (scrollY > oldScrollY) { | ||||
|                         floatingToolbar.hide() | ||||
| @@ -224,7 +224,7 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|                 .show() | ||||
|         } | ||||
|  | ||||
|         return binding.root | ||||
|         return binding!!.root | ||||
|     } | ||||
|  | ||||
|     override fun onDestroyView() { | ||||
| @@ -242,16 +242,16 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|  | ||||
|     private fun getContentFromMercury() { | ||||
|         if (repository.isNetworkAvailable()) { | ||||
|             binding.progressBar.visibility = View.VISIBLE | ||||
|             binding!!.progressBar.visibility = View.VISIBLE | ||||
|  | ||||
|             CoroutineScope(Dispatchers.Main).launch { | ||||
|                 try { | ||||
|                     val response = mercuryApi.query(url) | ||||
|                     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 { | ||||
|                             if (typeface != null) { | ||||
|                                 binding.titleView.typeface = typeface | ||||
|                                 binding!!.titleView.typeface = typeface | ||||
|                             } | ||||
|                         } catch (e: Exception) { | ||||
|                             e.sendSilentlyWithAcraWithName("getContentFromMercury > typeface") | ||||
| @@ -275,7 +275,7 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|  | ||||
|                         if (!response.data?.lead_image_url.isNullOrEmpty() && context != null) { | ||||
|                             try { | ||||
|                                 binding.imageView.visibility = View.VISIBLE | ||||
|                                 binding!!.imageView.visibility = View.VISIBLE | ||||
|                                 try { | ||||
|                                     Glide | ||||
|                                         .with(requireContext()) | ||||
| @@ -284,7 +284,7 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|                                             response.data!!.lead_image_url.orEmpty() | ||||
|                                         ) | ||||
|                                         .apply(RequestOptions.fitCenterTransform()) | ||||
|                                         .into(binding.imageView) | ||||
|                                         .into(binding!!.imageView) | ||||
|                                 } catch (e: Exception) { | ||||
|                                     e.sendSilentlyWithAcraWithName("getContentFromMercury > glide lead image") | ||||
|                                 } | ||||
| @@ -292,12 +292,12 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|                                 e.sendSilentlyWithAcraWithName("getContentFromMercury > outside glide lead image") | ||||
|                             } | ||||
|                         } else { | ||||
|                             binding.imageView.visibility = View.GONE | ||||
|                             binding!!.imageView.visibility = View.GONE | ||||
|                         } | ||||
|  | ||||
|                         try { | ||||
|                             binding.nestedScrollView.scrollTo(0, 0) | ||||
|                             binding.progressBar.visibility = View.GONE | ||||
|                             binding!!.nestedScrollView.scrollTo(0, 0) | ||||
|                             binding!!.progressBar.visibility = View.GONE | ||||
|                         } catch (e: Exception) { | ||||
|                             e.sendSilentlyWithAcraWithName("getContentFromMercury > scrollview") | ||||
|                         } | ||||
| @@ -320,8 +320,8 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|         val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs) | ||||
|  | ||||
|  | ||||
|         binding.webcontent.settings.standardFontFamily = a.getString(0) | ||||
|         binding.webcontent.visibility = View.VISIBLE | ||||
|         binding!!.webcontent.settings.standardFontFamily = a.getString(0) | ||||
|         binding!!.webcontent.visibility = View.VISIBLE | ||||
|  | ||||
|         val colorOnSurface = TypedValue() | ||||
|         requireContext().theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true) | ||||
| @@ -329,14 +329,14 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|         val colorSurface = TypedValue() | ||||
|         requireContext().theme.resolveAttribute(R.attr.colorSurface, colorSurface, true) | ||||
|  | ||||
|         binding.webcontent.settings.useWideViewPort = true | ||||
|         binding.webcontent.settings.loadWithOverviewMode = true | ||||
|         binding.webcontent.settings.javaScriptEnabled = false | ||||
|         binding!!.webcontent.settings.useWideViewPort = true | ||||
|         binding!!.webcontent.settings.loadWithOverviewMode = true | ||||
|         binding!!.webcontent.settings.javaScriptEnabled = false | ||||
|  | ||||
|         binding.webcontent.webViewClient = object : WebViewClient() { | ||||
|         binding!!.webcontent.webViewClient = object : WebViewClient() { | ||||
|             @Deprecated("Deprecated in Java") | ||||
|             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))) | ||||
|                 } | ||||
|                 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 | ||||
|  | ||||
|         var baseUrl: String? = null | ||||
| @@ -413,7 +413,7 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|             "" | ||||
|         } | ||||
|  | ||||
|         binding.webcontent.loadDataWithBaseURL( | ||||
|         binding!!.webcontent.loadDataWithBaseURL( | ||||
|             baseUrl, | ||||
|             """<html> | ||||
|                 |<head> | ||||
| @@ -466,17 +466,17 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|     } | ||||
|  | ||||
|     fun scrollDown() { | ||||
|         val height = binding.nestedScrollView.measuredHeight | ||||
|         binding.nestedScrollView.smoothScrollBy(0, height/2) | ||||
|         val height = binding!!.nestedScrollView.measuredHeight | ||||
|         binding!!.nestedScrollView.smoothScrollBy(0, height/2) | ||||
|     } | ||||
|  | ||||
|     fun scrollUp() { | ||||
|         val height = binding.nestedScrollView.measuredHeight | ||||
|         binding.nestedScrollView.smoothScrollBy(0, -height/2) | ||||
|         val height = binding!!.nestedScrollView.measuredHeight | ||||
|         binding!!.nestedScrollView.smoothScrollBy(0, -height/2) | ||||
|     } | ||||
|  | ||||
|     private fun openInBrowserAfterFailing() { | ||||
|         binding.progressBar.visibility = View.GONE | ||||
|         binding!!.progressBar.visibility = View.GONE | ||||
|         requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item) | ||||
|     } | ||||
|  | ||||
| @@ -495,10 +495,10 @@ class ArticleFragment : Fragment(), DIAware { | ||||
|     } | ||||
|  | ||||
|     fun performClick(): Boolean { | ||||
|         if (binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE || | ||||
|                 binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { | ||||
|         if (binding!!.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_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) | ||||
|             intent.putExtra("allImages", allImages) | ||||
|   | ||||
| @@ -105,7 +105,7 @@ | ||||
|     <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="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="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> | ||||
|   | ||||
| @@ -369,14 +369,7 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_tags() { | ||||
|         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) | ||||
|         ) | ||||
|         val (tags, tagsDB) = prepareTags() | ||||
|  | ||||
|         coEvery { api.tags() } returns StatusAndData(success = true, data = tags) | ||||
|         coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB | ||||
| @@ -396,17 +389,7 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_tags_with_sources_update_disabled() { | ||||
|         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 | ||||
|         val (tags, tagsDB) = prepareTags() | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns false | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns true | ||||
|  | ||||
| @@ -426,17 +409,7 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_tags_with_items_caching_disabled() { | ||||
|         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 | ||||
|         val (tags, _) = prepareTags() | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns true | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns false | ||||
|  | ||||
| @@ -453,17 +426,7 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_tags_with_sources_update_and_items_caching_disabled() { | ||||
|         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 | ||||
|         val (tags, tagsDB) = prepareTags() | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns false | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns false | ||||
|  | ||||
| @@ -482,17 +445,7 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_tags_without_connection() { | ||||
|         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 | ||||
|         val (tags, tagsDB) = prepareTags() | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns true | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns true | ||||
|  | ||||
| @@ -510,17 +463,7 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_tags_without_connection_and_items_caching_disabled() { | ||||
|         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 | ||||
|         prepareTags() | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns false | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns true | ||||
|  | ||||
| @@ -537,17 +480,7 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_tags_without_connection_and_sources_update_disabled() { | ||||
|         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 | ||||
|         val (tags, tagsDB) = prepareTags() | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns false | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns true | ||||
|  | ||||
| @@ -565,17 +498,7 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_tags_without_connection_and_sources_update_and_items_caching_disabled() { | ||||
|         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 | ||||
|         val (_, tagsDB) = prepareTags() | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns false | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns false | ||||
|  | ||||
| @@ -590,8 +513,36 @@ class RepositoryTest { | ||||
|         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 | ||||
|     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( | ||||
|             SelfossModel.Source( | ||||
|                 1, | ||||
| @@ -631,60 +582,15 @@ class RepositoryTest { | ||||
|  | ||||
|         coEvery { api.sources() } returns StatusAndData(success = true, data = sources) | ||||
|         every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB | ||||
|         initializeRepository() | ||||
|         var testSources: List<SelfossModel.Source>? | ||||
|         runBlocking { | ||||
|             testSources = repository.getSources() | ||||
|         } | ||||
|  | ||||
|         assertSame(sources, testSources) | ||||
|         assertNotEquals(sourcesDB.map { it.toView() }, testSources) | ||||
|         coVerify(exactly = 1) { api.sources() } | ||||
|         return Pair(sources, sourcesDB) | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     fun get_sources_with_sources_update_disabled() { | ||||
|         val sources = arrayListOf( | ||||
|             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" | ||||
|             ) | ||||
|         ) | ||||
|         val (sources, sourcesDB) = prepareSources() | ||||
|  | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns false | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns true | ||||
|         coEvery { api.sources() } returns StatusAndData(success = true, data = sources) | ||||
|         every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB | ||||
|         initializeRepository() | ||||
|         var testSources: List<SelfossModel.Source>? | ||||
|         runBlocking { | ||||
| @@ -701,47 +607,10 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_sources_with_items_caching_disabled() { | ||||
|         val sources = arrayListOf( | ||||
|             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" | ||||
|             ) | ||||
|         ) | ||||
|         val (sources, _) = prepareSources() | ||||
|  | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns true | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns false | ||||
|         coEvery { api.sources() } returns StatusAndData(success = true, data = sources) | ||||
|         every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB | ||||
|         initializeRepository() | ||||
|         var testSources: List<SelfossModel.Source>? | ||||
|         runBlocking { | ||||
| @@ -755,47 +624,10 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_sources_with_sources_update_and_items_caching_disabled() { | ||||
|         val sources = arrayListOf( | ||||
|             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" | ||||
|             ) | ||||
|         ) | ||||
|         val (sources, _) = prepareSources() | ||||
|  | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns false | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns false | ||||
|         coEvery { api.sources() } returns StatusAndData(success = true, data = sources) | ||||
|         every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB | ||||
|         initializeRepository() | ||||
|         var testSources: List<SelfossModel.Source>? | ||||
|         runBlocking { | ||||
| @@ -809,45 +641,7 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_sources_without_connection() { | ||||
|         val sources = arrayListOf( | ||||
|             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 | ||||
|         val (_, sourcesDB) = prepareSources() | ||||
|         initializeRepository(MutableStateFlow(false)) | ||||
|         var testSources: List<SelfossModel.Source>? | ||||
|         runBlocking { | ||||
| @@ -861,47 +655,10 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_sources_without_connection_and_items_caching_disabled() { | ||||
|         val sources = arrayListOf( | ||||
|             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" | ||||
|             ) | ||||
|         ) | ||||
|         prepareSources() | ||||
|  | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns false | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns true | ||||
|         coEvery { api.sources() } returns StatusAndData(success = true, data = sources) | ||||
|         every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB | ||||
|         initializeRepository(MutableStateFlow(false)) | ||||
|         var testSources: List<SelfossModel.Source>? | ||||
|         runBlocking { | ||||
| @@ -915,47 +672,10 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_sources_without_connection_and_sources_update_disabled() { | ||||
|         val sources = arrayListOf( | ||||
|             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" | ||||
|             ) | ||||
|         ) | ||||
|         val (_, sourcesDB) = prepareSources() | ||||
|  | ||||
|         every { appSettingsService.isItemCachingEnabled() } returns true | ||||
|         every { appSettingsService.isUpdateSourcesEnabled() } returns false | ||||
|         coEvery { api.sources() } returns StatusAndData(success = true, data = sources) | ||||
|         every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB | ||||
|         initializeRepository(MutableStateFlow(false)) | ||||
|         var testSources: List<SelfossModel.Source>? | ||||
|         runBlocking { | ||||
| @@ -969,47 +689,10 @@ class RepositoryTest { | ||||
|  | ||||
|     @Test | ||||
|     fun get_sources_without_connection_and_items_caching_and_sources_update_disabled() { | ||||
|         val sources = arrayListOf( | ||||
|             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" | ||||
|             ) | ||||
|         ) | ||||
|         val (_, sourcesDB) = prepareSources() | ||||
|  | ||||
|         every { appSettingsService.isItemCachingEnabled() } 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)) | ||||
|         var testSources: List<SelfossModel.Source>? | ||||
|         runBlocking { | ||||
| @@ -1302,16 +985,7 @@ class RepositoryTest { | ||||
|         ) | ||||
|  | ||||
|         initializeRepository() | ||||
|         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" | ||||
|         prepareSearch() | ||||
|         runBlocking { | ||||
|             repository.tryToCacheItemsAndGetNewOnes() | ||||
|         } | ||||
| @@ -1325,16 +999,7 @@ class RepositoryTest { | ||||
|                 StatusAndData(success = false, data = generateTestApiItem()) | ||||
|  | ||||
|         initializeRepository() | ||||
|         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" | ||||
|         prepareSearch() | ||||
|         runBlocking { | ||||
|             repository.tryToCacheItemsAndGetNewOnes() | ||||
|         } | ||||
| @@ -1348,20 +1013,26 @@ class RepositoryTest { | ||||
|                 StatusAndData(success = false, data = generateTestApiItem()) | ||||
|  | ||||
|         initializeRepository(MutableStateFlow(false)) | ||||
|         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" | ||||
|         prepareSearch() | ||||
|         runBlocking { | ||||
|             repository.tryToCacheItemsAndGetNewOnes() | ||||
|         } | ||||
|  | ||||
|         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) | ||||
|     kotlin("android").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("org.jetbrains.kotlinx.kover") version "0.6.1" | ||||
| } | ||||
|  | ||||
| apply(plugin = "org.sonarqube") | ||||
|  | ||||
| allprojects { | ||||
|     repositories { | ||||
|         google() | ||||
| @@ -30,3 +28,7 @@ allprojects { | ||||
| tasks.register("clean", Delete::class) { | ||||
|     delete(rootProject.buildDir) | ||||
| } | ||||
|  | ||||
| koverMerged { | ||||
|     enable() | ||||
| } | ||||
| @@ -10,6 +10,7 @@ plugins { | ||||
|     id("com.android.library") | ||||
|     id("com.squareup.sqldelight") | ||||
|     kotlin("plugin.serialization") version "1.4.10" | ||||
|     id("org.jetbrains.kotlinx.kover") version "0.6.1" | ||||
| } | ||||
|  | ||||
| kotlin { | ||||
|   | ||||
| @@ -1,15 +1,6 @@ | ||||
| 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.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 { | ||||
|  | ||||
| @@ -20,138 +11,4 @@ class MercuryModel { | ||||
|         val lead_image_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 = | ||||
|         maybeResponse( | ||||
|             if (appSettingsService.getApiVersion() > 1) { | ||||
|                 createSource2(title, url, spout, tags, filter) | ||||
|                 createSource("tags[]", title, url, spout, tags, filter) | ||||
|             } else { | ||||
|                 createSource(title, url, spout, tags, filter) | ||||
|                 createSource("tags", title, url, spout, tags, filter) | ||||
|             } | ||||
|         ) | ||||
|  | ||||
|     private suspend fun createSource( | ||||
|         title: 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( | ||||
|         tagsParamName: String, | ||||
|         title: String, | ||||
|         url: String, | ||||
|         spout: String, | ||||
| @@ -275,7 +253,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { | ||||
|                 append("title", title) | ||||
|                 append("url", url) | ||||
|                 append("spout", spout) | ||||
|                 append("tags[]", tags) | ||||
|                 append(tagsParamName, tags) | ||||
|                 append("filter", filter) | ||||
|             } | ||||
|         ) | ||||
|   | ||||
| @@ -84,7 +84,13 @@ class AppSettingsService { | ||||
|     } | ||||
|  | ||||
|     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 { | ||||
| @@ -94,9 +100,21 @@ class AppSettingsService { | ||||
|         return _apiTimeout!! | ||||
|     } | ||||
|  | ||||
|     private fun secToMs(n: Long) = n * 1000 | ||||
|  | ||||
|     private fun refreshApiTimeout() { | ||||
|         val settingsTimeout = settings.getLong(API_TIMEOUT, HttpTimeout.INFINITE_TIMEOUT_MS) | ||||
|         _apiTimeout = if (settingsTimeout > 0) settingsTimeout else HttpTimeout.INFINITE_TIMEOUT_MS | ||||
|         _apiTimeout = secToMs(try { | ||||
|             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() { | ||||
|   | ||||
							
								
								
									
										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