Compare commits

..

No commits in common. "af8969ce4acb25c3b1a0838afa2848daa0d065b6" and "6076eb1cee0156d0a86d9b86e6ad9afe0feadb19" have entirely different histories.

21 changed files with 230 additions and 221 deletions

View File

@ -6,19 +6,16 @@ steps:
- name: AnylyseBuildTest - name: AnylyseBuildTest
image: mingc/android-build-box:latest image: mingc/android-build-box:latest
commands: commands:
- echo "---------------------------------------------------------"
- echo "Configure gradle..."
- mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\nappLoginUrl=\"URL\"\nappLoginUsername=\"LOGIN\"\nappLoginPassword=\"PASS\"\npushCache=false" >> ~/.gradle/gradle.properties
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- echo "Analysing..." - echo "Analysing..."
- ./gradlew sonarqube -Dsonar.projectKey=RFS2 -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN - ./gradlew sonarqube -Dsonar.projectKey=RFS2 -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\""
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- echo "Building..." - echo "Building..."
- ./gradlew build - ./gradlew build -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- echo "Testing..." - echo "Testing..."
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- ./gradlew test - ./gradlew test -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
environment: environment:
SONAR_HOST_URL: SONAR_HOST_URL:
from_secret: sonarScannerHostUrl from_secret: sonarScannerHostUrl
@ -88,12 +85,8 @@ steps:
- name: build - name: build
image: mingc/android-build-box:latest image: mingc/android-build-box:latest
commands: commands:
- echo "---------------------------------------------------------"
- echo "Configure gradle..."
- mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\nappLoginUrl=\"URL\"\nappLoginUsername=\"LOGIN\"\nappLoginPassword=\"PASS\"\npushCache=false" >> ~/.gradle/gradle.properties
- echo "---------------------------------------------------------"
- echo "Generate APK" - echo "Generate APK"
- ./gradlew :androidApp:assembleGithubConfigRelease -P pushCache=false - ./gradlew :androidApp:assembleGithubConfigRelease -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
- echo "---------------------------------------------------------" - echo "---------------------------------------------------------"
- echo "Get Key" - echo "Get Key"
- wget https://amine-louveau.fr/key - wget https://amine-louveau.fr/key

View File

@ -54,13 +54,9 @@ fun versionNameFromGit(): String {
android { android {
compileOptions { compileOptions {
// Flag to enable support for the new language APIs // Flag to enable support for the new language APIs
sourceCompatibility = JavaVersion.VERSION_11 isCoreLibraryDesugaringEnabled = true
targetCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_1_8
} targetCompatibility = JavaVersion.VERSION_1_8
// For Kotlin projects
kotlinOptions {
jvmTarget = "11"
} }
compileSdk = 32 compileSdk = 32
buildToolsVersion = "31.0.0" buildToolsVersion = "31.0.0"
@ -134,6 +130,8 @@ dependencies {
implementation("androidx.constraintlayout:constraintlayout:2.1.3") implementation("androidx.constraintlayout:constraintlayout:2.1.3")
implementation("org.jsoup:jsoup:1.14.3") implementation("org.jsoup:jsoup:1.14.3")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
//multidex //multidex
implementation("androidx.multidex:multidex:2.0.1") implementation("androidx.multidex:multidex:2.0.1")
@ -142,6 +140,9 @@ dependencies {
implementation("com.mikepenz:aboutlibraries:8.9.4") implementation("com.mikepenz:aboutlibraries:8.9.4")
implementation("com.mikepenz:aboutlibraries-definitions:8.9.4") implementation("com.mikepenz:aboutlibraries-definitions:8.9.4")
// Async
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
// Retrofit + http logging + okhttp // Retrofit + http logging + okhttp
implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.3") implementation("com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.3")
@ -188,13 +189,12 @@ dependencies {
implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0") implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0")
// SQLDELIGHT // SQLDELIGHT
implementation("com.squareup.sqldelight:android-driver:1.5.4") implementation("com.squareup.sqldelight:android-driver:1.5.3")
//test //test
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk:1.12.0") testImplementation("io.mockk:mockk:1.12.0")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
} }
tasks.withType<Test> { tasks.withType<Test> {

View File

@ -94,7 +94,7 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
try { try {
val items = repository.getSpouts() val items = repository.getSpouts()
if (items.isNotEmpty()) { if (items != null) {
val itemsStrings = items.map { it.value.name } val itemsStrings = items.map { it.value.name }
for ((key, value) in items) { for ((key, value) in items) {
spoutsKV[value.name] = key spoutsKV[value.name] = key

View File

@ -101,7 +101,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
finish() finish()
} }
private fun preferenceError() { private fun preferenceError(t: Throwable) {
appSettingsService.resetLoginInformation() appSettingsService.resetLoginInformation()
binding.urlView.error = getString(R.string.wrong_infos) binding.urlView.error = getString(R.string.wrong_infos)
@ -169,7 +169,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
goToMain() goToMain()
} else { } else {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
preferenceError() preferenceError(Exception("Not success"))
} }
} }
} }

View File

@ -56,7 +56,7 @@ class SourcesActivity : AppCompatActivity(), DIAware {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
val response = repository.getSources() val response = repository.getSources()
if (response.isNotEmpty()) { if (response != null) {
items = response items = response
val mAdapter = SourcesListAdapter( val mAdapter = SourcesListAdapter(
this@SourcesActivity, items this@SourcesActivity, items

View File

@ -10,12 +10,9 @@ import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener import bou.amine.apps.readerforselfossv2.android.utils.*
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
import bou.amine.apps.readerforselfossv2.android.utils.shareLink
import bou.amine.apps.readerforselfossv2.model.SelfossModel 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
@ -62,7 +59,7 @@ class ItemCardAdapter(
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent)) binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
binding.sourceTitleAndDate.text = itm.sourceAndDateText() binding.sourceTitleAndDate.text = itm.sourceAndDateText(repository.dateUtils)
if (!appSettingsService.isFullHeightCardsEnabled()) { if (!appSettingsService.isFullHeightCardsEnabled()) {
binding.itemImage.maxHeight = imageMaxHeight binding.itemImage.maxHeight = imageMaxHeight

View File

@ -51,7 +51,7 @@ class ItemListAdapter(
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent)) binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
binding.sourceTitleAndDate.text = itm.sourceAndDateText() binding.sourceTitleAndDate.text = itm.sourceAndDateText(repository.dateUtils)
if (itm.getThumbnail(repository.baseUrl).isEmpty()) { if (itm.getThumbnail(repository.baseUrl).isEmpty()) {

View File

@ -101,7 +101,7 @@ class ArticleFragment : Fragment(), DIAware {
contentText = item.content contentText = item.content
contentTitle = item.title.getHtmlDecoded() contentTitle = item.title.getHtmlDecoded()
contentImage = item.getThumbnail(repository.baseUrl) contentImage = item.getThumbnail(repository.baseUrl)
contentSource = item.sourceAndDateText() contentSource = item.sourceAndDateText(repository.dateUtils)
allImages = item.getImages() allImages = item.getImages()
fontSize = appSettingsService.getFontSize() fontSize = appSettingsService.getFontSize()

View File

@ -1,33 +0,0 @@
package bou.amine.apps.readerforselfossv2.repository
import bou.amine.apps.readerforselfossv2.utils.DateUtils
import junit.framework.TestCase.assertEquals
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import org.junit.Test
class DatesTest {
private val v3Date = "2013-04-07T13:43:00+01:00"
private val v4Date = "2013-04-07 13:43:00"
@Test
fun v3_date_should_be_parsed() {
val date = DateUtils.parseDate(v3Date)
val expected = LocalDateTime(2013, 4, 7, 13, 43, 0, 0).toInstant(TimeZone.of("UTC+1")) .toEpochMilliseconds()
assertEquals(date, expected)
}
@Test
fun v4_date_should_be_parsed() {
val date = DateUtils.parseDate(v4Date)
val expected =
LocalDateTime(2013, 4, 7, 13, 43, 0, 0).toInstant(TimeZone.currentSystemDefault())
.toEpochMilliseconds()
assertEquals(date, expected)
}
}

View File

@ -1,5 +1,6 @@
package bou.amine.apps.readerforselfossv2.repository package bou.amine.apps.readerforselfossv2.repository
import bou.amine.apps.readerforselfossv2.dao.ITEM
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
import bou.amine.apps.readerforselfossv2.dao.SOURCE import bou.amine.apps.readerforselfossv2.dao.SOURCE
import bou.amine.apps.readerforselfossv2.dao.TAG import bou.amine.apps.readerforselfossv2.dao.TAG
@ -63,14 +64,14 @@ class RepositoryTest {
} }
@Test @Test
fun instantiate_repository() { fun Instantiate_repository() {
initializeRepository() initializeRepository()
coVerify(exactly = 1) { api.version() } coVerify(exactly = 1) { api.version() }
} }
@Test @Test
fun instantiate_repository_without_api_version() { fun Instantiate_repository_without_api_version() {
every { appSettingsService.getApiVersion() } returns -1 every { appSettingsService.getApiVersion() } returns -1
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
@ -80,7 +81,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_api_4_date_with_api_1_version_stored() { fun Get_api_4_date_with_api_1_version_stored() {
every { appSettingsService.getApiVersion() } returns 1 every { appSettingsService.getApiVersion() } returns 1
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
@ -96,7 +97,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_api_1_date_with_api_4_version_stored() { fun Get_api_1_date_with_api_4_version_stored() {
every { appSettingsService.getApiVersion() } returns 4 every { appSettingsService.getApiVersion() } returns 4
coEvery { api.version() } returns SelfossModel.StatusAndData(success = false, null) coEvery { api.version() } returns SelfossModel.StatusAndData(success = false, null)
val itemParameters = FakeItemParameters() val itemParameters = FakeItemParameters()
@ -116,7 +117,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_newer_items() { fun Get_newer_items() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
@ -131,7 +132,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_all_newer_items() { fun Get_all_newer_items() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
@ -147,7 +148,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_newer_starred_items() { fun Get_newer_starred_items() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
@ -163,7 +164,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_newer_items_without_connectivity() { fun Get_newer_items_without_connectivity() {
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
@ -177,7 +178,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_newer_items_without_connectivity_and_tag_filter() { fun Get_newer_items_without_connectivity_and_tag_filter() {
val itemParameter1 = FakeItemParameters() val itemParameter1 = FakeItemParameters()
val itemParameter2 = FakeItemParameters() val itemParameter2 = FakeItemParameters()
val itemParameter3 = FakeItemParameters() val itemParameter3 = FakeItemParameters()
@ -205,7 +206,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_newer_items_without_connectivity_and_source_filter() { fun Get_newer_items_without_connectivity_and_source_filter() {
val itemParameter1 = FakeItemParameters() val itemParameter1 = FakeItemParameters()
val itemParameter2 = FakeItemParameters() val itemParameter2 = FakeItemParameters()
val itemParameter3 = FakeItemParameters() val itemParameter3 = FakeItemParameters()
@ -240,7 +241,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_older_items() { fun Get_older_items() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
@ -256,7 +257,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_all_older_items() { fun Get_all_older_items() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
@ -273,7 +274,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_older_starred_items() { fun Get_older_starred_items() {
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
SelfossModel.StatusAndData(success = true, data = generateTestApiItem()) SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
@ -290,8 +291,8 @@ class RepositoryTest {
} }
@Test @Test
fun reload_badges() { fun Reload_badges() {
var success: Boolean var success = false
initializeRepository() initializeRepository()
runBlocking { runBlocking {
@ -307,10 +308,10 @@ class RepositoryTest {
} }
@Test @Test
fun reload_badges_without_response() { fun Reload_badges_without_response() {
coEvery { api.stats() } returns SelfossModel.StatusAndData(success = false, data = null) coEvery { api.stats() } returns SelfossModel.StatusAndData(success = false, data = null)
var success: Boolean var success = false
initializeRepository() initializeRepository()
runBlocking { runBlocking {
@ -326,11 +327,11 @@ class RepositoryTest {
} }
@Test @Test
fun reload_badges_without_connection() { fun Reload_badges_without_connection() {
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems() every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems()
var success: Boolean var success = false
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
runBlocking { runBlocking {
@ -346,11 +347,11 @@ class RepositoryTest {
} }
@Test @Test
fun reload_badges_without_connection_and_items_caching_disabled() { fun Reload_badges_without_connection_and_items_caching_disabled() {
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
every { appSettingsService.isUpdateSourcesEnabled() } returns true every { appSettingsService.isUpdateSourcesEnabled() } returns true
var success: Boolean var success = false
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
runBlocking { runBlocking {
@ -366,7 +367,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_tags() { fun Get_tags() {
val tags = listOf( val tags = listOf(
SelfossModel.Tag("test", "red", 6), SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0) SelfossModel.Tag("second", "yellow", 0)
@ -382,7 +383,7 @@ class RepositoryTest {
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
initializeRepository() initializeRepository()
var testTags: List<SelfossModel.Tag>? var testTags: List<SelfossModel.Tag>? = null
runBlocking { runBlocking {
testTags = repository.getTags() testTags = repository.getTags()
} }
@ -393,7 +394,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_tags_with_sources_update_disabled() { fun Get_tags_with_sources_update_disabled() {
val tags = listOf( val tags = listOf(
SelfossModel.Tag("test", "red", 6), SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0) SelfossModel.Tag("second", "yellow", 0)
@ -409,7 +410,7 @@ class RepositoryTest {
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
initializeRepository() initializeRepository()
var testTags: List<SelfossModel.Tag> var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking { runBlocking {
testTags = repository.getTags() testTags = repository.getTags()
// Tags will be fetched from the database on the second call, thus testTags != tags // Tags will be fetched from the database on the second call, thus testTags != tags
@ -423,7 +424,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_tags_with_items_caching_disabled() { fun Get_tags_with_items_caching_disabled() {
val tags = listOf( val tags = listOf(
SelfossModel.Tag("test", "red", 6), SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0) SelfossModel.Tag("second", "yellow", 0)
@ -439,7 +440,7 @@ class RepositoryTest {
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
initializeRepository() initializeRepository()
var testTags: List<SelfossModel.Tag> var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking { runBlocking {
testTags = repository.getTags() testTags = repository.getTags()
} }
@ -450,7 +451,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 = listOf(
SelfossModel.Tag("test", "red", 6), SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0) SelfossModel.Tag("second", "yellow", 0)
@ -466,7 +467,7 @@ class RepositoryTest {
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
initializeRepository() initializeRepository()
var testTags: List<SelfossModel.Tag> var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking { runBlocking {
testTags = repository.getTags() testTags = repository.getTags()
testTags = repository.getTags() testTags = repository.getTags()
@ -479,7 +480,7 @@ class RepositoryTest {
} }
@Test @Test
fun get_tags_without_connection() { fun Get_tags_without_connection() {
val tags = listOf( val tags = listOf(
SelfossModel.Tag("test", "red", 6), SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0) SelfossModel.Tag("second", "yellow", 0)
@ -495,7 +496,7 @@ class RepositoryTest {
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testTags: List<SelfossModel.Tag> var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking { runBlocking {
testTags = repository.getTags() testTags = repository.getTags()
} }
@ -507,7 +508,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( val tags = listOf(
SelfossModel.Tag("test", "red", 6), SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0) SelfossModel.Tag("second", "yellow", 0)
@ -523,7 +524,7 @@ class RepositoryTest {
every { appSettingsService.isUpdateSourcesEnabled() } returns true every { appSettingsService.isUpdateSourcesEnabled() } returns true
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testTags: List<SelfossModel.Tag> var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking { runBlocking {
testTags = repository.getTags() testTags = repository.getTags()
} }
@ -534,7 +535,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 = listOf(
SelfossModel.Tag("test", "red", 6), SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0) SelfossModel.Tag("second", "yellow", 0)
@ -550,7 +551,7 @@ class RepositoryTest {
every { appSettingsService.isItemCachingEnabled() } returns true every { appSettingsService.isItemCachingEnabled() } returns true
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testTags: List<SelfossModel.Tag> var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking { runBlocking {
testTags = repository.getTags() testTags = repository.getTags()
} }
@ -562,7 +563,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 tags = listOf(
SelfossModel.Tag("test", "red", 6), SelfossModel.Tag("test", "red", 6),
SelfossModel.Tag("second", "yellow", 0) SelfossModel.Tag("second", "yellow", 0)
@ -578,7 +579,7 @@ class RepositoryTest {
every { appSettingsService.isItemCachingEnabled() } returns false every { appSettingsService.isItemCachingEnabled() } returns false
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testTags: List<SelfossModel.Tag> var testTags: List<SelfossModel.Tag> = emptyList()
runBlocking { runBlocking {
testTags = repository.getTags() testTags = repository.getTags()
} }
@ -630,7 +631,7 @@ class RepositoryTest {
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository() initializeRepository()
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>? = null
runBlocking { runBlocking {
testSources = repository.getSources() testSources = repository.getSources()
} }
@ -684,7 +685,7 @@ class RepositoryTest {
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository() initializeRepository()
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>? = null
runBlocking { runBlocking {
testSources = repository.getSources() testSources = repository.getSources()
// Sources will be fetched from the database on the second call, thus testSources != sources // Sources will be fetched from the database on the second call, thus testSources != sources
@ -741,7 +742,7 @@ class RepositoryTest {
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository() initializeRepository()
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>? = null
runBlocking { runBlocking {
testSources = repository.getSources() testSources = repository.getSources()
} }
@ -795,7 +796,7 @@ class RepositoryTest {
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository() initializeRepository()
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>? = null
runBlocking { runBlocking {
testSources = repository.getSources() testSources = repository.getSources()
} }
@ -847,7 +848,7 @@ class RepositoryTest {
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>? = null
runBlocking { runBlocking {
testSources = repository.getSources() testSources = repository.getSources()
} }
@ -901,7 +902,7 @@ class RepositoryTest {
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>? = null
runBlocking { runBlocking {
testSources = repository.getSources() testSources = repository.getSources()
} }
@ -955,7 +956,7 @@ class RepositoryTest {
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>? = null
runBlocking { runBlocking {
testSources = repository.getSources() testSources = repository.getSources()
} }
@ -1009,7 +1010,7 @@ class RepositoryTest {
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources) coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var testSources: List<SelfossModel.Source>? var testSources: List<SelfossModel.Source>? = null
runBlocking { runBlocking {
testSources = repository.getSources() testSources = repository.getSources()
} }
@ -1025,7 +1026,7 @@ class RepositoryTest {
SelfossModel.SuccessResponse(true) SelfossModel.SuccessResponse(true)
initializeRepository() initializeRepository()
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.createSource( response = repository.createSource(
"test", "test",
@ -1055,7 +1056,7 @@ class RepositoryTest {
SelfossModel.SuccessResponse(false) SelfossModel.SuccessResponse(false)
initializeRepository() initializeRepository()
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.createSource( response = repository.createSource(
"test", "test",
@ -1085,7 +1086,7 @@ class RepositoryTest {
SelfossModel.SuccessResponse(true) SelfossModel.SuccessResponse(true)
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.createSource( response = repository.createSource(
"test", "test",
@ -1114,7 +1115,7 @@ class RepositoryTest {
coEvery { api.deleteSource(any()) } returns SelfossModel.SuccessResponse(true) coEvery { api.deleteSource(any()) } returns SelfossModel.SuccessResponse(true)
initializeRepository() initializeRepository()
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.deleteSource(5) response = repository.deleteSource(5)
} }
@ -1128,7 +1129,7 @@ class RepositoryTest {
coEvery { api.deleteSource(any()) } returns SelfossModel.SuccessResponse(false) coEvery { api.deleteSource(any()) } returns SelfossModel.SuccessResponse(false)
initializeRepository() initializeRepository()
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.deleteSource(5) response = repository.deleteSource(5)
} }
@ -1142,7 +1143,7 @@ class RepositoryTest {
coEvery { api.deleteSource(any()) } returns SelfossModel.SuccessResponse(false) coEvery { api.deleteSource(any()) } returns SelfossModel.SuccessResponse(false)
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.deleteSource(5) response = repository.deleteSource(5)
} }
@ -1159,7 +1160,7 @@ class RepositoryTest {
) )
initializeRepository() initializeRepository()
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.updateRemote() response = repository.updateRemote()
} }
@ -1176,7 +1177,7 @@ class RepositoryTest {
) )
initializeRepository() initializeRepository()
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.updateRemote() response = repository.updateRemote()
} }
@ -1193,7 +1194,7 @@ class RepositoryTest {
) )
initializeRepository() initializeRepository()
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.updateRemote() response = repository.updateRemote()
} }
@ -1210,7 +1211,7 @@ class RepositoryTest {
) )
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.updateRemote() response = repository.updateRemote()
} }
@ -1224,7 +1225,7 @@ class RepositoryTest {
coEvery { api.login() } returns SelfossModel.SuccessResponse(success = true) coEvery { api.login() } returns SelfossModel.SuccessResponse(success = true)
initializeRepository() initializeRepository()
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.login() response = repository.login()
} }
@ -1238,7 +1239,7 @@ class RepositoryTest {
coEvery { api.login() } returns SelfossModel.SuccessResponse(success = false) coEvery { api.login() } returns SelfossModel.SuccessResponse(success = false)
initializeRepository() initializeRepository()
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.login() response = repository.login()
} }
@ -1252,7 +1253,7 @@ class RepositoryTest {
coEvery { api.login() } returns SelfossModel.SuccessResponse(success = true) coEvery { api.login() } returns SelfossModel.SuccessResponse(success = true)
initializeRepository(MutableStateFlow(false)) initializeRepository(MutableStateFlow(false))
var response: Boolean var response = false
runBlocking { runBlocking {
response = repository.login() response = repository.login()
} }
@ -1365,4 +1366,56 @@ class RepositoryTest {
coVerify(exactly = 0) { api.getItems(any(), 0, null, null, null, null, 200) } coVerify(exactly = 0) { api.getItems(any(), 0, null, null, null, null, 200) }
} }
} }
fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<ITEM> {
return listOf(
ITEM(
id = item.id,
datetime = item.datetime,
title = item.title,
content = item.content,
unread = item.unread,
starred = item.starred,
thumbnail = item.thumbnail,
icon = item.icon,
link = item.link,
sourcetitle = item.sourcetitle,
tags = item.tags
)
)
}
fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<SelfossModel.Item> {
return listOf(
SelfossModel.Item(
id = item.id.toInt(),
datetime = item.datetime,
title = item.title,
content = item.content,
unread = item.unread,
starred = item.starred,
thumbnail = item.thumbnail,
icon = item.icon,
link = item.link,
sourcetitle = item.sourcetitle,
tags = item.tags.split(',')
)
)
}
class FakeItemParameters {
var id = "20"
var datetime = "2022-09-09T03:32:01-04:00"
val title = "Etica della ricerca sotto i riflettori."
val content =
"<p><strong>Luigi Campanella, già Presidente SCI</strong></p>\n<p>Letica della scienza è di certo ambito di cui continuiamo a scoprire nuovi aspetti e risvolti.</p>\n<p>Lultimo è quello delle intelligenze artificiali capaci di creare opere complesse basate su immagini e parole memorizzate con il rischio di fake news e di contenuti disturbanti.</p>\n<p>Per evitare che ciò accada si sta procedendo filtrando secondo criteri di autocensura i dati da cui lintelligenza artificiale parte.</p>\n<p>Comincia ad intravedersi un futuro prossimo di competizione fra autori umani ed artificiali nel quale sarà importante, quando i loro prodotti saranno indistinguibili, dichiararne lorigine.</p>\n<p>Come si comprende, si conferma che gli aspetti etici dellinnovazione e della ricerca si diversificato sempre di più.</p>\n<p>La biologia molecolare e la genetica già in passato hanno posto allattenzione comune aspetti di etica della scienza che hanno indotto a nuove riflessioni circa i limiti delle ricerche.</p>\n<p>Largomento, sempre attuale, torna sulle prime pagine a seguito della pubblicazione di una ricerca della Università di Cambridge che ha sviluppato una struttura cellulare di un topo con un cuore che batte regolarmente.</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image002-1.png?w=481\" alt=\"\" width=\"697\" height=\"430\" /><img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image003-1.png?w=906\" alt=\"\" /><p>Magdalena Zernicka-Goetz</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image004.jpg?w=474\" alt=\"\" width=\"622\" height=\"465\" /><p>Gianluca Amadei</p>\n<p>Del gruppo fa parte anche uno scienziato italiano Gianluca Amadei,che dinnanzi alle obiezioni di natura etica sulla realizzazione della vita artificiale si è affrettato a sostenere che non è creare nuove vite il fine primario della ricerca, ma quello di salvare quelle esistenti, di dare contributi essenziali alla medicina citando il caso del fallimento tuttora non interpretato di alcune gravidanze e di superare la sperimentazione animale, così contribuendo positivamente alla soluzione di un altro dilemma etico.</p>\n<p>Lembrione sintetico ha ovviamente come primo traguardo il contributo ai trapianti oggi drammaticamente carenti nellofferta rispetto alla domanda, con attese fino a 4 anni per i trapianti di cuore ed a 2 anni per quelli di fegato. Il lavoro dovrebbe adesso continuare presso lAteneo di Padova per creare nuovi organi e nuovi farmaci.</p>"
var unread = true
var starred = true
val thumbnail = null
val icon = "ba79e238383ce83c23a169929c8906ef.png"
val link =
"https://ilblogdellasci.wordpress.com/2022/09/09/etica-della-ricerca-sotto-i-riflettori/"
var sourcetitle = "La Chimica e la Società"
var tags = "Chimica, Testing"
}

View File

@ -1,57 +0,0 @@
package bou.amine.apps.readerforselfossv2.repository
import bou.amine.apps.readerforselfossv2.dao.ITEM
import bou.amine.apps.readerforselfossv2.model.SelfossModel
fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<ITEM> {
return listOf(
ITEM(
id = item.id,
datetime = item.datetime,
title = item.title,
content = item.content,
unread = item.unread,
starred = item.starred,
thumbnail = item.thumbnail,
icon = item.icon,
link = item.link,
sourcetitle = item.sourcetitle,
tags = item.tags
)
)
}
fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<SelfossModel.Item> {
return listOf(
SelfossModel.Item(
id = item.id.toInt(),
datetime = item.datetime,
title = item.title,
content = item.content,
unread = item.unread,
starred = item.starred,
thumbnail = item.thumbnail,
icon = item.icon,
link = item.link,
sourcetitle = item.sourcetitle,
tags = item.tags.split(',')
)
)
}
class FakeItemParameters {
var id = "20"
var datetime = "2022-09-09T03:32:01-04:00"
val title = "Etica della ricerca sotto i riflettori."
val content =
"<p><strong>Luigi Campanella, già Presidente SCI</strong></p>\n<p>Letica della scienza è di certo ambito di cui continuiamo a scoprire nuovi aspetti e risvolti.</p>\n<p>Lultimo è quello delle intelligenze artificiali capaci di creare opere complesse basate su immagini e parole memorizzate con il rischio di fake news e di contenuti disturbanti.</p>\n<p>Per evitare che ciò accada si sta procedendo filtrando secondo criteri di autocensura i dati da cui lintelligenza artificiale parte.</p>\n<p>Comincia ad intravedersi un futuro prossimo di competizione fra autori umani ed artificiali nel quale sarà importante, quando i loro prodotti saranno indistinguibili, dichiararne lorigine.</p>\n<p>Come si comprende, si conferma che gli aspetti etici dellinnovazione e della ricerca si diversificato sempre di più.</p>\n<p>La biologia molecolare e la genetica già in passato hanno posto allattenzione comune aspetti di etica della scienza che hanno indotto a nuove riflessioni circa i limiti delle ricerche.</p>\n<p>Largomento, sempre attuale, torna sulle prime pagine a seguito della pubblicazione di una ricerca della Università di Cambridge che ha sviluppato una struttura cellulare di un topo con un cuore che batte regolarmente.</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image002-1.png?w=481\" alt=\"\" width=\"697\" height=\"430\" /><img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image003-1.png?w=906\" alt=\"\" /><p>Magdalena Zernicka-Goetz</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image004.jpg?w=474\" alt=\"\" width=\"622\" height=\"465\" /><p>Gianluca Amadei</p>\n<p>Del gruppo fa parte anche uno scienziato italiano Gianluca Amadei,che dinnanzi alle obiezioni di natura etica sulla realizzazione della vita artificiale si è affrettato a sostenere che non è creare nuove vite il fine primario della ricerca, ma quello di salvare quelle esistenti, di dare contributi essenziali alla medicina citando il caso del fallimento tuttora non interpretato di alcune gravidanze e di superare la sperimentazione animale, così contribuendo positivamente alla soluzione di un altro dilemma etico.</p>\n<p>Lembrione sintetico ha ovviamente come primo traguardo il contributo ai trapianti oggi drammaticamente carenti nellofferta rispetto alla domanda, con attese fino a 4 anni per i trapianti di cuore ed a 2 anni per quelli di fegato. Il lavoro dovrebbe adesso continuare presso lAteneo di Padova per creare nuovi organi e nuovi farmaci.</p>"
var unread = true
var starred = true
val thumbnail = null
val icon = "ba79e238383ce83c23a169929c8906ef.png"
val link =
"https://ilblogdellasci.wordpress.com/2022/09/09/etica-della-ricerca-sotto-i-riflettori/"
var sourcetitle = "La Chimica e la Società"
var tags = "Chimica, Testing"
}

View File

@ -1,7 +1,7 @@
buildscript { buildscript {
dependencies { dependencies {
// SqlDelight // SqlDelight
classpath("com.squareup.sqldelight:gradle-plugin:1.5.4") classpath("com.squareup.sqldelight:gradle-plugin:1.5.3")
} }
} }

View File

@ -34,3 +34,4 @@ org.gradle.caching=true
ignoreGitVersion=false ignoreGitVersion=false
kotlin.native.cacheKind.iosX64=none kotlin.native.cacheKind.iosX64=none
pushCache=true pushCache=true

View File

@ -1,7 +1,7 @@
object SqlDelight { object SqlDelight {
const val runtime = "com.squareup.sqldelight:runtime:1.5.4" const val runtime = "com.squareup.sqldelight:runtime:1.5.3"
const val android = "com.squareup.sqldelight:android-driver:1.5.4" const val android = "com.squareup.sqldelight:android-driver:1.5.3"
const val native = "com.squareup.sqldelight:native-driver:1.5.4" const val native = "com.squareup.sqldelight:native-driver:1.5.3"
} }
@ -58,7 +58,6 @@ kotlin {
val androidMain by getting { val androidMain by getting {
dependencies { dependencies {
implementation("io.ktor:ktor-client-okhttp:2.1.1") implementation("io.ktor:ktor-client-okhttp:2.1.1")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
// Sql // Sql
implementation(SqlDelight.android) implementation(SqlDelight.android)

View File

@ -1,29 +1,49 @@
package bou.amine.apps.readerforselfossv2.utils package bou.amine.apps.readerforselfossv2.utils
import android.os.Build
import android.text.format.DateUtils import android.text.format.DateUtils
import kotlinx.datetime.* import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import java.time.Instant
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
actual class DateUtils actual constructor(actual val appSettingsService: AppSettingsService) {
actual class DateUtils { actual fun parseDate(dateString: String): Long {
actual companion object {
actual fun parseDate(dateString: String): Long { val FORMATTERV1 = "yyyy-MM-dd HH:mm:ss"
return try {
Instant.parse(dateString).toEpochMilliseconds() return if (appSettingsService.getApiVersion() >= 4) {
} catch (e: Exception) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
LocalDateTime.parse(dateString.replace(" ", "T")).toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds() OffsetDateTime.parse(dateString).toInstant().toEpochMilli()
} else {
TODO("VERSION.SDK_INT < O")
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
LocalDateTime.parse(dateString, DateTimeFormatter.ofPattern(FORMATTERV1)).toInstant(
ZoneOffset.UTC).toEpochMilli()
} else {
TODO("VERSION.SDK_INT < O")
} }
} }
}
actual fun parseRelativeDate(dateString: String): String { actual fun parseRelativeDate(dateString: String): String {
val date = parseDate(dateString) val date = parseDate(dateString)
return " " + DateUtils.getRelativeTimeSpanString( return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
" " + DateUtils.getRelativeTimeSpanString(
date, date,
Clock.System.now().toEpochMilliseconds(), Instant.now().toEpochMilli(),
DateUtils.MINUTE_IN_MILLIS, DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE DateUtils.FORMAT_ABBREV_RELATIVE
) )
} else {
TODO("VERSION.SDK_INT < O")
} }
} }
} }

View File

@ -108,8 +108,8 @@ class SelfossModel {
return stringUrl return stringUrl
} }
fun sourceAndDateText(): String = fun sourceAndDateText(dateUtils: DateUtils): String =
this.sourcetitle.getHtmlDecoded() + DateUtils.parseRelativeDate(this.datetime) this.sourcetitle.getHtmlDecoded() + dateUtils.parseRelativeDate(this.datetime)
fun toggleStar(): Item { fun toggleStar(): Item {
this.starred = !this.starred this.starred = !this.starred

View File

@ -11,6 +11,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class Repository(private val api: SelfossApi, private val appSettingsService: AppSettingsService, val isConnectionAvailable: MutableStateFlow<Boolean>, private val db: ReaderForSelfossDB) { class Repository(private val api: SelfossApi, private val appSettingsService: AppSettingsService, val isConnectionAvailable: MutableStateFlow<Boolean>, private val db: ReaderForSelfossDB) {
@ -18,6 +19,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
var connectionMonitored = false var connectionMonitored = false
var baseUrl = appSettingsService.getBaseUrl() var baseUrl = appSettingsService.getBaseUrl()
lateinit var dateUtils: DateUtils
var displayedItems = ItemType.UNREAD var displayedItems = ItemType.UNREAD
@ -73,7 +75,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
if (fetchedItems.success && fetchedItems.data != null) { if (fetchedItems.success && fetchedItems.data != null) {
items = ArrayList(fetchedItems.data!!) items = ArrayList(fetchedItems.data!!)
if (fromDB) { if (fromDB) {
items.sortByDescending { DateUtils.parseDate(it.datetime) } items.sortByDescending { dateUtils.parseDate(it.datetime) }
} }
} }
return items return items
@ -392,6 +394,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
appSettingsService.updateApiVersion(fetchedVersion.data.getApiMajorVersion()) appSettingsService.updateApiVersion(fetchedVersion.data.getApiMajorVersion())
} }
} }
dateUtils = DateUtils(appSettingsService)
} }
fun isNetworkAvailable() = isConnectionAvailable.value && !offlineOverride fun isNetworkAvailable() = isConnectionAvailable.value && !offlineOverride

View File

@ -0,0 +1,27 @@
package bou.amine.apps.readerforselfossv2.service
import bou.amine.apps.readerforselfossv2.utils.DateUtils
class SearchService(val dateUtils: DateUtils) {
var displayedItems: String = "unread"
set(value) {
field = when (value) {
"all" -> "all"
"unread" -> "unread"
"read" -> "read"
"starred" -> "starred"
else -> "all"
}
}
var position = 0
var searchFilter: String? = null
var sourceIDFilter: Long? = null
var sourceFilter: String? = null
var tagFilter: String? = null
var itemsCaching = false
var badgeUnread = -1
var badgeAll = -1
var badgeStarred = -1
}

View File

@ -1,9 +1,16 @@
package bou.amine.apps.readerforselfossv2.utils package bou.amine.apps.readerforselfossv2.utils
expect class DateUtils() { import bou.amine.apps.readerforselfossv2.model.SelfossModel
companion object { import bou.amine.apps.readerforselfossv2.service.AppSettingsService
fun parseDate(dateString: String): Long
fun parseRelativeDate(dateString: String): String
} fun SelfossModel.Item.parseDate(dateUtils: DateUtils): Long =
dateUtils.parseDate(this.datetime)
expect class DateUtils constructor(appSettingsService: AppSettingsService) {
val appSettingsService: AppSettingsService // This is needed because of https://stackoverflow.com/a/65249085
fun parseDate(dateString: String): Long
fun parseRelativeDate(dateString: String): String
} }

View File

@ -1,13 +1,14 @@
package bou.amine.apps.readerforselfossv2.utils package bou.amine.apps.readerforselfossv2.utils
actual class DateUtils { import bou.amine.apps.readerforselfossv2.service.AppSettingsService
actual companion object {
actual fun parseDate(dateString: String): Long {
TODO("Not yet implemented")
}
actual fun parseRelativeDate(dateString: String): String { actual class DateUtils actual constructor(actual val appSettingsService: AppSettingsService) {
TODO("Not yet implemented") actual fun parseDate(dateString: String): Long {
} TODO("Not yet implemented")
} }
actual fun parseRelativeDate(dateString: String): String {
TODO("Not yet implemented")
}
} }

View File

@ -2,15 +2,13 @@ package bou.amine.apps.readerforselfossv2.utils
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
actual class DateUtils { actual class DateUtils actual constructor(actual val appSettingsService: AppSettingsService) {
actual companion object { actual fun parseDate(dateString: String): Long {
actual fun parseDate(dateString: String): Long { TODO("Not yet implemented")
TODO("Not yet implemented") }
}
actual fun parseRelativeDate(dateString: String): String { actual fun parseRelativeDate(dateString: String): String {
TODO("Not yet implemented") TODO("Not yet implemented")
}
} }
} }