diff --git a/.gitea/workflows/on_pr.yml b/.gitea/workflows/on_pr.yml index d49e140..f29a25c 100644 --- a/.gitea/workflows/on_pr.yml +++ b/.gitea/workflows/on_pr.yml @@ -24,4 +24,5 @@ jobs: - name: Detecting... run: ./detekt-cli-1.23.1/bin/detekt-cli --all-rules --excludes '**/shared/build/**/*.kt' || true build: + needs: Lint uses: ./.gitea/workflows/common_build.yml \ No newline at end of file diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 49bc9b3..ace0b01 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -1,7 +1,7 @@ import java.io.ByteArrayOutputStream val ignoreGitVersion: String by project -val acraVersion = "5.9.7" +val acraVersion = "5.12.0" plugins { id("com.android.application") @@ -9,6 +9,7 @@ plugins { kotlin("kapt") id("com.mikepenz.aboutlibraries.plugin") id("org.jetbrains.kotlinx.kover") + id("app.cash.sqldelight") version "2.0.2" } fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String { @@ -65,14 +66,14 @@ android { kotlinOptions { jvmTarget = "17" } - compileSdk = 34 + compileSdk = 35 buildFeatures { viewBinding = true } defaultConfig { applicationId = "bou.amine.apps.readerforselfossv2.android" minSdk = 25 - targetSdk = 34 + targetSdk = 34 // 35 when edge-to-edge is handled versionCode = versionCodeFromGit() versionName = versionNameFromGit() @@ -119,28 +120,26 @@ android { } dependencies { - coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3") + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") implementation(project(":shared")) - implementation("com.google.android.material:material:1.9.0") - implementation("androidx.appcompat:appcompat:1.6.1") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1") + implementation("androidx.appcompat:appcompat:1.7.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.1") implementation("androidx.preference:preference-ktx:1.2.1") implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs"))) // Android Support - implementation("androidx.appcompat:appcompat:1.6.1") - implementation("com.google.android.material:material:1.9.0") - implementation("androidx.recyclerview:recyclerview:1.3.1") + implementation("com.google.android.material:material:1.12.0") + implementation("androidx.recyclerview:recyclerview:1.4.0-rc01") implementation("androidx.legacy:legacy-support-v4:1.0.0") - implementation("androidx.vectordrawable:vectordrawable:1.2.0-beta01") + implementation("androidx.vectordrawable:vectordrawable:1.2.0") implementation("androidx.cardview:cardview:1.0.0") - implementation("androidx.annotation:annotation:1.7.0") - implementation("androidx.work:work-runtime-ktx:2.8.1") - implementation("androidx.constraintlayout:constraintlayout:2.1.4") - implementation("org.jsoup:jsoup:1.15.4") + implementation("androidx.annotation:annotation:1.9.1") + implementation("androidx.work:work-runtime-ktx:2.10.0") + implementation("androidx.constraintlayout:constraintlayout:2.2.0") + implementation("org.jsoup:jsoup:1.18.3") //multidex implementation("androidx.multidex:multidex:2.0.1") @@ -153,31 +152,31 @@ dependencies { implementation("com.ashokvarma.android:bottom-navigation-bar:2.2.0") // glide - kapt("com.github.bumptech.glide:compiler:4.15.0") - implementation("com.github.bumptech.glide:okhttp3-integration:4.15.0") + kapt("com.github.bumptech.glide:compiler:4.16.0") + implementation("com.github.bumptech.glide:okhttp3-integration:4.16.0") // Themes implementation("com.github.rubensousa:floatingtoolbar:1.5.1") // Pager implementation("me.relex:circleindicator:2.1.6") - implementation("androidx.viewpager2:viewpager2:1.1.0-beta02") + implementation("androidx.viewpager2:viewpager2:1.1.0") //Dependency Injection - implementation("org.kodein.di:kodein-di:7.14.0") - implementation("org.kodein.di:kodein-di-framework-android-x:7.14.0") - implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.14.0") + implementation("org.kodein.di:kodein-di:7.23.1") + implementation("org.kodein.di:kodein-di-framework-android-x:7.23.1") + implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.23.1") //Settings - implementation("com.russhwolf:multiplatform-settings-no-arg:0.9") + implementation("com.russhwolf:multiplatform-settings-no-arg:1.3.0") //Logging - implementation("io.github.aakira:napier:2.6.1") + implementation("io.github.aakira:napier:2.7.1") //PhotoView implementation("com.github.chrisbanes:PhotoView:2.3.0") - implementation("androidx.core:core-ktx:1.12.0") + implementation("androidx.core:core-ktx:1.15.0") implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") @@ -185,13 +184,13 @@ dependencies { implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0") // SQLDELIGHT - implementation("com.squareup.sqldelight:android-driver:1.5.4") + implementation("app.cash.sqldelight:android-driver:2.0.2") //test testImplementation("junit:junit:4.13.2") - testImplementation("io.mockk:mockk:1.12.0") - testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1") - implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") + testImplementation("io.mockk:mockk:1.13.14") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1") + implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1") androidTestImplementation("androidx.test:runner:1.6.2") androidTestImplementation("androidx.test:rules:1.6.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") diff --git a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/CommonTests.kt b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/CommonTests.kt index 09ac1fd..b0e4cad 100644 --- a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/CommonTests.kt +++ b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/CommonTests.kt @@ -2,6 +2,7 @@ package bou.amine.apps.readerforselfossv2.android import android.content.Context import androidx.annotation.ArrayRes +import androidx.test.espresso.Espresso.onData import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.replaceText @@ -14,6 +15,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isNotChecked import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import org.hamcrest.CoreMatchers.allOf +import org.hamcrest.Matchers.hasToString fun performLogin(someUrl: String? = null) { onView(withId(R.id.urlView)).perform(click()).perform( @@ -22,7 +24,6 @@ fun performLogin(someUrl: String? = null) { ) ) onView(withId(R.id.signInButton)).perform(click()) - Thread.sleep(10000) } fun loginAndInitHome() { @@ -87,4 +88,22 @@ fun testPreferencesFromArray( openSettingItem() onView(withText(res)).check(matches(allOf(isDisplayed(), isChecked()))) } +} + +fun testAddSourceWithUrl(url: String, sourceName: String) { + onView(withId(R.id.fab)) + .perform(click()) + onView(withId(R.id.nameInput)) + .perform(click()).perform(typeTextIntoFocusedView(sourceName)) + onView(withId(R.id.sourceUri)) + .perform(click()) + .perform(typeTextIntoFocusedView(url)) + onView(withId(R.id.tags)) + .perform(click()).perform(typeTextIntoFocusedView("tag1,tag2,tag3")) + onView(withId(R.id.spoutsSpinner)) + .perform(click()) + onData(hasToString("RSS Feed")).perform(click()) + onView(withId(R.id.saveBtn)) + .perform(click()) + onView(withText(sourceName)).check(matches(isDisplayed())) } \ No newline at end of file diff --git a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/HomeActivityTest.kt b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/HomeActivityTest.kt index e95b84c..eb76219 100644 --- a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/HomeActivityTest.kt +++ b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/HomeActivityTest.kt @@ -1,9 +1,6 @@ package bou.amine.apps.readerforselfossv2.android -import android.content.Context -import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches @@ -47,9 +44,7 @@ class HomeActivityTest { isClickable() ) ) - openActionBarOverflowOrOptionsMenu( - ApplicationProvider.getApplicationContext() - ) + openMenu() onView(withText(R.string.readAll)).check(matches(isDisplayed())) onView(withText(R.string.menu_home_sources)).check(matches(isDisplayed())) onView(withText(R.string.title_activity_settings)).check(matches(isDisplayed())) @@ -78,43 +73,31 @@ class HomeActivityTest { ).check(matches(isDisplayed())) onView(isRoot()).perform(ViewActions.pressBack()) - openActionBarOverflowOrOptionsMenu( - ApplicationProvider.getApplicationContext() - ) + openMenu() onView(withText(R.string.readAll)).perform(click()) onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed())) onView(isRoot()).perform(ViewActions.pressBack()) - openActionBarOverflowOrOptionsMenu( - ApplicationProvider.getApplicationContext() - ) + openMenu() onView(withText(R.string.menu_home_sources)).perform(click()) onView(withId(R.id.fab)).check(matches(isDisplayed())) onView(isRoot()).perform(ViewActions.pressBack()) - openActionBarOverflowOrOptionsMenu( - ApplicationProvider.getApplicationContext() - ) + openMenu() onView(withText(R.string.title_activity_settings)).perform(click()) onView(withText(R.string.pref_header_general)).check(matches(isDisplayed())) onView(isRoot()).perform(ViewActions.pressBack()) - openActionBarOverflowOrOptionsMenu( - ApplicationProvider.getApplicationContext() - ) + openMenu() onView(withText(R.string.menu_home_refresh)).perform(click()) onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed())) onView(isRoot()).perform(ViewActions.pressBack()) - openActionBarOverflowOrOptionsMenu( - ApplicationProvider.getApplicationContext() - ) + openMenu() /*onView(withText(R.string.issue_tracker_link)).perform(click()) onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed())) onView(isRoot()).perform(ViewActions.pressBack()) - openActionBarOverflowOrOptionsMenu( - ApplicationProvider.getApplicationContext() - )*/ + openMenu()*/ onView(withText(R.string.action_disconnect)).perform(click()) onView(withText(R.string.confirm_disconnect_title)).check(matches(isDisplayed())) diff --git a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SettingsActivityGeneralTest.kt b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SettingsActivityGeneralTest.kt index 1b8b7e1..4859b02 100644 --- a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SettingsActivityGeneralTest.kt +++ b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SettingsActivityGeneralTest.kt @@ -6,6 +6,7 @@ import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.action.ViewActions.swipeUp import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isChecked @@ -102,6 +103,7 @@ class SettingsActivityGeneralTest { ) ) ) + onView(withId(R.id.settings)).perform(swipeUp()) onView(withSettingsCheckboxWidget(R.string.display_all_counts_title)).check( matches( allOf( diff --git a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SettingsActivityTest.kt b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SettingsActivityTest.kt index 202301d..69101c6 100644 --- a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SettingsActivityTest.kt +++ b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SettingsActivityTest.kt @@ -1,9 +1,7 @@ package bou.amine.apps.readerforselfossv2.android import android.content.Context -import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed @@ -33,9 +31,7 @@ class SettingsActivityTest { context = activity.window.context } loginAndInitHome() - openActionBarOverflowOrOptionsMenu( - ApplicationProvider.getApplicationContext() - ) + openMenu() onView(withText(R.string.title_activity_settings)).perform(click()) } diff --git a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SourcesActivityTest.kt b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SourcesActivityTest.kt index 8a3088b..f7e0a57 100644 --- a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SourcesActivityTest.kt +++ b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/SourcesActivityTest.kt @@ -1,19 +1,20 @@ package bou.amine.apps.readerforselfossv2.android -import android.content.Context -import androidx.test.core.app.ApplicationProvider +import androidx.test.espresso.AmbiguousViewMatcherException import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu +import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.action.ViewActions.scrollCompletelyTo -import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView +import androidx.test.espresso.action.ViewActions.swipeDown +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test @@ -23,7 +24,6 @@ import java.util.UUID @RunWith(AndroidJUnit4::class) @LargeTest class SourcesActivityTest { - @get:Rule val activityRule = ActivityScenarioRule(LoginActivity::class.java) @@ -34,32 +34,50 @@ class SourcesActivityTest { sourceName = UUID.randomUUID().toString().substring(0, 15) loginAndInitHome() - openActionBarOverflowOrOptionsMenu( - ApplicationProvider.getApplicationContext() - ) - onView(withText(R.string.menu_home_sources)) - .perform(click()) + goToSources() } @Test fun addSource() { - onView(withId(R.id.fab)) - .perform(click()) - onView(withId(R.id.nameInput)) - .perform(click()).perform(typeTextIntoFocusedView(sourceName)) - onView(withId(R.id.sourceUri)) - .perform(click()) - .perform(typeTextIntoFocusedView("https://lorem-rss.herokuapp.com/feed?unit=year&interval=1&length=10")) - onView(withId(R.id.tags)) - .perform(click()).perform(typeTextIntoFocusedView("tag1,tag2,tag3")) - onView(withId(R.id.spoutsSpinner)) - .perform(click()) - onView(withText("RSS Feed")) - .perform(scrollCompletelyTo()) - .perform(click()) - onView(withId(R.id.saveBtn)) - .perform(click()) - onView(withText(sourceName)).check(matches(isDisplayed())) + testAddSourceWithUrl( + "https://lorem-rss.herokuapp.com/feed?unit=year&interval=1&length=10", + sourceName + ) } + + @Test + fun addSourceCheckContent() { + testAddSourceWithUrl("https://news.google.com/rss?hl=en-US&gl=US&ceid=US:en", sourceName) + onView(isRoot()).perform(ViewActions.pressBack()) + openMenu() + onView(withText(R.string.menu_home_refresh)).perform(click()) + onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed())) + onView( + withId(android.R.id.button1) + ).perform(click()) + Thread.sleep(10000) + onView(withId(R.id.swipeRefreshLayout)).perform(swipeDown()) + Thread.sleep(2000) + try { + onView(withId(R.id.sourceTitleAndDate)).check(matches(isDisplayed())) + } catch (e: AmbiguousViewMatcherException) { + assert(true) + } + goToSources() + } + + @After + fun deleteTheCreatedSource() { + onView(withText(sourceName)).check(matches(isDisplayed())) + onView(withId(R.id.deleteBtn)).perform(click()) + onView(withText(sourceName)).check(doesNotExist()) + } + + + private fun goToSources() { + openMenu() + onView(withText(R.string.menu_home_sources)) + .perform(click()) + } } \ No newline at end of file diff --git a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/helpers.kt b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/helpers.kt index 60c300f..ecca84b 100644 --- a/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/helpers.kt +++ b/androidApp/src/androidTest/kotlin/bou/amine/apps/readerforselfossv2/android/helpers.kt @@ -1,5 +1,6 @@ package bou.amine.apps.readerforselfossv2.android +import android.content.Context import android.view.View import android.widget.EditText import android.widget.ImageView @@ -7,6 +8,8 @@ import android.widget.RelativeLayout import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.core.graphics.drawable.toBitmap +import androidx.test.core.app.ApplicationProvider +import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import androidx.test.espresso.Root import androidx.test.espresso.matcher.RootMatchers.isPlatformPopup import androidx.test.espresso.matcher.ViewMatchers.hasSibling @@ -98,4 +101,10 @@ fun withSettingsCheckboxFrame(@StringRes id: Int): Matcher? { ) ) ) +} + +fun openMenu() { + openActionBarOverflowOrOptionsMenu( + ApplicationProvider.getApplicationContext() + ) } \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/acra/AcraReportingAdministrator.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/acra/AcraReportingAdministrator.kt index cfd5079..0ac5463 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/acra/AcraReportingAdministrator.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/acra/AcraReportingAdministrator.kt @@ -16,7 +16,7 @@ class AcraReportingAdministrator : ReportingAdministrator { config: CoreConfiguration, reportBuilder: ReportBuilder ): Boolean { - return reportBuilder.exception !is DeadSystemException + return reportBuilder.exception !is DeadSystemException && (reportBuilder.exception != null && reportBuilder.exception!!::class.simpleName != "CannotDeliverBroadcastException") } override fun shouldSendReport( diff --git a/androidApp/src/main/res/layout/activity_login.xml b/androidApp/src/main/res/layout/activity_login.xml index c09300b..98270c7 100644 --- a/androidApp/src/main/res/layout/activity_login.xml +++ b/androidApp/src/main/res/layout/activity_login.xml @@ -23,6 +23,7 @@ @@ -37,7 +38,7 @@ - + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 96db5b8..52e706b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,10 +1,3 @@ -buildscript { - dependencies { - // SqlDelight - classpath("com.squareup.sqldelight:gradle-plugin:1.5.5") - } -} - plugins { //trick: for the same plugin versions in all sub-modules id("com.android.application").version("8.7.3").apply(false) diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png index 2b7a2f5..282b03c 100644 Binary files a/fastlane/metadata/android/en-US/images/icon.png and b/fastlane/metadata/android/en-US/images/icon.png differ diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 0af0d6f..3e20988 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -1,18 +1,18 @@ -val ktorVersion = "2.3.2" +val ktorVersion = "3.0.3" object SqlDelight { - const val runtime = "com.squareup.sqldelight:runtime:1.5.4" - const val android = "com.squareup.sqldelight:android-driver:1.5.4" - const val native = "com.squareup.sqldelight:native-driver:1.5.4" + const val runtime = "app.cash.sqldelight:runtime:2.0.2" + const val android = "app.cash.sqldelight:android-driver:2.0.2" + const val native = "app.cash.sqldelight:native-driver:2.0.2" } plugins { kotlin("multiplatform") id("com.android.library") - id("com.squareup.sqldelight") kotlin("plugin.serialization") version "1.9.0" id("org.jetbrains.kotlinx.kover") + id("app.cash.sqldelight") version "2.0.2" } kotlin { @@ -37,7 +37,7 @@ kotlin { implementation("io.ktor:ktor-client-logging:$ktorVersion") implementation("io.ktor:ktor-client-auth:$ktorVersion") implementation("io.ktor:ktor-client-cio:$ktorVersion") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1") implementation("org.jsoup:jsoup:1.15.4") @@ -66,7 +66,6 @@ kotlin { val androidMain by getting { dependencies { implementation("com.squareup.okhttp3:okhttp:4.11.0") - implementation("io.ktor:ktor-client-okhttp:2.2.4") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") // Sql @@ -86,7 +85,6 @@ kotlin { dependencies { implementation(SqlDelight.native) - implementation("io.ktor:ktor-client-ios:2.1.1") } } val iosX64Test by getting @@ -110,11 +108,10 @@ android { namespace = "bou.amine.apps.readerforselfossv2" } - - sqldelight { - database("ReaderForSelfossDB") { - packageName = "bou.amine.apps.readerforselfossv2.dao" - sourceFolders = listOf("sqldelight") + databases { + create("ReaderForSelfossDB") { + packageName.set("bou.amine.apps.readerforselfossv2.dao") + } } } \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt index cf4d39f..628c09f 100644 --- a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt +++ b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt @@ -1,10 +1,15 @@ package bou.amine.apps.readerforselfossv2.dao + import android.content.Context -import com.squareup.sqldelight.android.AndroidSqliteDriver -import com.squareup.sqldelight.db.SqlDriver +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.android.AndroidSqliteDriver actual class DriverFactory(private val context: Context) { actual fun createDriver(): SqlDriver { - return AndroidSqliteDriver(ReaderForSelfossDB.Schema, context, "ReaderForSelfossV2-android.db") + return AndroidSqliteDriver( + ReaderForSelfossDB.Schema, + context, + "ReaderForSelfossV2-android.db" + ) } -} +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt index eb28bb8..ab41017 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt @@ -1,7 +1,7 @@ package bou.amine.apps.readerforselfossv2.dao -import com.squareup.sqldelight.db.SqlDriver +import app.cash.sqldelight.db.SqlDriver expect class DriverFactory { fun createDriver(): SqlDriver -} +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/MercuryApi.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/MercuryApi.kt index 243659b..5fc678c 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/MercuryApi.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/MercuryApi.kt @@ -1,22 +1,26 @@ package bou.amine.apps.readerforselfossv2.rest -import bou.amine.apps.readerforselfossv2.model.* +import bou.amine.apps.readerforselfossv2.model.MercuryModel +import bou.amine.apps.readerforselfossv2.model.StatusAndData import io.github.aakira.napier.Napier -import io.ktor.client.* -import io.ktor.client.plugins.cache.* -import io.ktor.client.plugins.contentnegotiation.* -import io.ktor.client.plugins.logging.* -import io.ktor.client.request.* -import io.ktor.serialization.kotlinx.json.* +import io.ktor.client.HttpClient +import io.ktor.client.plugins.cache.HttpCache +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.plugins.logging.Logger +import io.ktor.client.plugins.logging.Logging +import io.ktor.client.request.get +import io.ktor.client.request.parameter +import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json -class MercuryApi() { +class MercuryApi { var client = createHttpClient() private fun createHttpClient(): HttpClient { return HttpClient { + install(HttpCache) install(ContentNegotiation) { - install(HttpCache) json( Json { prettyPrint = true @@ -44,4 +48,4 @@ class MercuryApi() { parameter("link", url) }, ) -} +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt index f0198c0..fc7bb82 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt @@ -45,8 +45,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { setupInsecureHTTPEngine(this) } } + install(HttpCache) install(ContentNegotiation) { - install(HttpCache) json( Json { prettyPrint = true @@ -105,8 +105,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { private fun hasLoginInfo() = appSettingsService.getUserName().isNotEmpty() && - appSettingsService.getPassword() - .isNotEmpty() + appSettingsService.getPassword() + .isNotEmpty() suspend fun login(): SuccessResponse = if (appSettingsService.getUserName().isNotEmpty() && @@ -127,7 +127,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { client.tryToGet(url("/login")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -148,7 +150,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { client.tryToPost(url("/login")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -164,7 +168,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { }, ) - private fun shouldHaveNewLogout() = appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0 + private fun shouldHaveNewLogout() = + appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0 suspend fun logout(): SuccessResponse = if (shouldHaveNewLogout()) { @@ -176,7 +181,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { private suspend fun maybeLogoutIfAvailable() = responseOrSuccessIf404( client.tryToGet(url("/logout")) { - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -195,7 +202,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { private suspend fun doLogout() = maybeResponse( client.tryToDelete(url("/api/session/current")) { - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -233,7 +242,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("updatedsince", updatedSince) parameter("items", items ?: appSettingsService.getItemsNumber()) parameter("offset", offset) - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -258,7 +269,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { } parameter("type", "all") parameter("items", 1) - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -281,7 +294,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -304,7 +319,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -327,7 +344,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -350,7 +369,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -373,7 +394,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -396,7 +419,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -415,7 +440,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { suspend fun apiInformation(): StatusAndData = bodyOrFailure( client.tryToGet(url("/api/about")) { - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -438,7 +465,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -461,7 +490,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -484,7 +515,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -507,7 +540,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -528,15 +563,17 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { client.tryToSubmitForm( url = url("/mark"), formParameters = - Parameters.build { - if (!shouldHavePostLogin()) { - append("username", appSettingsService.getUserName()) - append("password", appSettingsService.getPassword()) - } - ids.map { append("ids[]", it) } - }, + Parameters.build { + if (!shouldHavePostLogin()) { + append("username", appSettingsService.getUserName()) + append("password", appSettingsService.getPassword()) + } + ids.map { append("ids[]", it) } + }, block = { - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -577,18 +614,20 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { client.tryToSubmitForm( url = url("/source"), formParameters = - Parameters.build { - if (!shouldHavePostLogin()) { - append("username", appSettingsService.getUserName()) - append("password", appSettingsService.getPassword()) - } - append("title", title) - append("url", url) - append("spout", spout) - append(tagsParamName, tags) - }, + Parameters.build { + if (!shouldHavePostLogin()) { + append("username", appSettingsService.getUserName()) + append("password", appSettingsService.getPassword()) + } + append("title", title) + append("url", url) + append("spout", spout) + append(tagsParamName, tags) + }, block = { - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -630,18 +669,20 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { client.tryToSubmitForm( url = url("/source/$id"), formParameters = - Parameters.build { - if (!shouldHavePostLogin()) { - append("username", appSettingsService.getUserName()) - append("password", appSettingsService.getPassword()) - } - append("title", title) - append("url", url) - append("spout", spout) - append(tagsParamName, tags) - }, + Parameters.build { + if (!shouldHavePostLogin()) { + append("username", appSettingsService.getUserName()) + append("password", appSettingsService.getPassword()) + } + append("title", title) + append("url", url) + append("spout", spout) + append(tagsParamName, tags) + }, block = { - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -664,7 +705,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) } - if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { + if (appSettingsService.getBasicUserName() + .isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty() + ) { headers { append( HttpHeaders.Authorization, @@ -679,4 +722,4 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { } }, ) -} +} \ No newline at end of file diff --git a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Actions.sq b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Actions.sq index 8831df4..6b0d6dd 100644 --- a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Actions.sq +++ b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Actions.sq @@ -1,3 +1,5 @@ +import kotlin.Boolean; + CREATE TABLE `ACTION` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `articleid` TEXT NOT NULL, diff --git a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Items.sq b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Items.sq index fd5f34e..0756158 100644 --- a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Items.sq +++ b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Items.sq @@ -1,3 +1,5 @@ +import kotlin.Boolean; + CREATE TABLE ITEM ( `id` TEXT NOT NULL, `datetime` TEXT NOT NULL, diff --git a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt index 06d7b59..17e359d 100644 --- a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt +++ b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt @@ -1,10 +1,10 @@ package bou.amine.apps.readerforselfossv2.dao -import com.squareup.sqldelight.db.SqlDriver -import com.squareup.sqldelight.drivers.native.NativeSqliteDriver +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.native.NativeSqliteDriver actual class DriverFactory { actual fun createDriver(): SqlDriver { return NativeSqliteDriver(ReaderForSelfossDB.Schema, "ReaderForSelfossV2-IOS.db") } -} +} \ No newline at end of file diff --git a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt index 06d7b59..17e359d 100644 --- a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt +++ b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt @@ -1,10 +1,10 @@ package bou.amine.apps.readerforselfossv2.dao -import com.squareup.sqldelight.db.SqlDriver -import com.squareup.sqldelight.drivers.native.NativeSqliteDriver +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.native.NativeSqliteDriver actual class DriverFactory { actual fun createDriver(): SqlDriver { return NativeSqliteDriver(ReaderForSelfossDB.Schema, "ReaderForSelfossV2-IOS.db") } -} +} \ No newline at end of file