From 1dfa3c9f07e4e6b6a4f75c597e3c8c1f0a91783a Mon Sep 17 00:00:00 2001 From: aminecmi Date: Wed, 7 Sep 2022 20:24:19 +0200 Subject: [PATCH 01/11] Version scripts. --- androidApp/build.sh => build.sh | 2 +- androidApp/publish-version.sh => publish-version.sh | 0 androidApp/version.sh => version.sh | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename androidApp/build.sh => build.sh (97%) rename androidApp/publish-version.sh => publish-version.sh (100%) rename androidApp/version.sh => version.sh (100%) diff --git a/androidApp/build.sh b/build.sh similarity index 97% rename from androidApp/build.sh rename to build.sh index abdea4d..a8bcc27 100755 --- a/androidApp/build.sh +++ b/build.sh @@ -2,7 +2,7 @@ git fetch --tags -p -BASE_VERSION="1.7" +BASE_VERSION="1" LAST_TAG=$(git tag -l | sort -V | tail -1) INITIAL_VERSION="${BASE_VERSION//./}$(date '+%y%m%j')" diff --git a/androidApp/publish-version.sh b/publish-version.sh similarity index 100% rename from androidApp/publish-version.sh rename to publish-version.sh diff --git a/androidApp/version.sh b/version.sh similarity index 100% rename from androidApp/version.sh rename to version.sh From d0d6a4378cf74a7fee1325ad77b475de634ece2f Mon Sep 17 00:00:00 2001 From: aminecmi Date: Wed, 7 Sep 2022 21:24:38 +0200 Subject: [PATCH 02/11] Trying to fix fdroid build. --- androidApp/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 846f050..df60d29 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -93,7 +93,7 @@ android { } flavorDimensions.add("build") productFlavors { - create("github") { + create("githubConfig") { versionNameSuffix = "-github" dimension = "build" } From 0473a5f7bc28efbc4c910b164a99132efad02746 Mon Sep 17 00:00:00 2001 From: aminecmi Date: Wed, 7 Sep 2022 21:43:46 +0200 Subject: [PATCH 03/11] Changelog. --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a87b03b..aef1896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# V2/Multiplatform rewrite + +**v1** + +- The app has the same functionalities as before. + + +-------------------------------------------------------------------- + +# Old version changes + **1.7.x** - Hiding tags with 0 articles From e2411c00d878d33e67b4279e131a9e21ed0adf77 Mon Sep 17 00:00:00 2001 From: aminecmi Date: Thu, 8 Sep 2022 19:00:19 +0200 Subject: [PATCH 04/11] Shorter description. --- fastlane/metadata/android/en-US/short_description.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt index 543c361..655b51a 100644 --- a/fastlane/metadata/android/en-US/short_description.txt +++ b/fastlane/metadata/android/en-US/short_description.txt @@ -1,3 +1 @@ A new RSS reader for selfoss. - -It connects to your selfoss instance (works only with selfoss, and can't work without it), and you'll be able to read and manage all your RSS feeds. From 01763556b19f2e8ed9e157b106b754f73acc1bda Mon Sep 17 00:00:00 2001 From: aminecmi Date: Tue, 13 Sep 2022 09:00:54 +0200 Subject: [PATCH 05/11] Fixed release build issues. --- androidApp/proguard-rules.pro | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/androidApp/proguard-rules.pro b/androidApp/proguard-rules.pro index 0b3ee7b..f7c7eff 100644 --- a/androidApp/proguard-rules.pro +++ b/androidApp/proguard-rules.pro @@ -55,11 +55,38 @@ public *; } --dontwarn com.anupcowkur.reservoir.** - -dontwarn javax.annotation.** -keep class android.support.v7.widget.SearchView { *; } # maybe remove later ? -keep class * extends androidx.fragment.app.Fragment + + +# Keep `Companion` object fields of serializable classes. +# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects. +-if @kotlinx.serialization.Serializable class ** +-keepclassmembers class <1> { + static <1>$Companion Companion; +} + +# Keep `serializer()` on companion objects (both default and named) of serializable classes. +-if @kotlinx.serialization.Serializable class ** { + static **$* *; +} +-keepclassmembers class <2>$<3> { + kotlinx.serialization.KSerializer serializer(...); +} + +# Keep `INSTANCE.serializer()` of serializable objects. +-if @kotlinx.serialization.Serializable class ** { + public static ** INSTANCE; +} +-keepclassmembers class <1> { + public static <1> INSTANCE; + kotlinx.serialization.KSerializer serializer(...); +} + +# @Serializable and @Polymorphic are used at runtime for polymorphic serialization. +-keepattributes RuntimeVisibleAnnotations,AnnotationDefault + From f4db02521df440cf8b0a22f3bdbd72e35f6939b8 Mon Sep 17 00:00:00 2001 From: Amine Louveau Date: Wed, 14 Sep 2022 10:32:00 +0000 Subject: [PATCH 06/11] drone-sign (#57) ## Types of changes - [ ] I have read the **CONTRIBUTING** document. - [ ] My code follows the code style of this project. - [ ] I have updated the documentation accordingly. - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. - [ ] This is **NOT** translation related. This closes issue #XXX This is implements feature #YYY This finishes chore #ZZZ Co-authored-by: aminecmi Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/57 --- .drone.yml | 104 +++++++++++++++++++++++++++++++++++++++++++-- build.sh | 4 +- publish-version.sh | 12 ++++-- version.sh | 17 +++++--- 4 files changed, 120 insertions(+), 17 deletions(-) diff --git a/.drone.yml b/.drone.yml index b401477..2703761 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,21 +1,117 @@ kind: pipeline type: docker -name: android +name: test steps: - - name: code-analysis + - name: AnylyseBuildTest image: mingc/android-build-box:latest failure: ignore commands: - - ls -la + - echo "---------------------------------------------------------" + - echo "Analysing..." - ./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 "Building..." + - ./gradlew :androidApp:build -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false + - echo "---------------------------------------------------------" + - echo "Testing..." + - echo "---------------------------------------------------------" environment: SONAR_HOST_URL: from_secret: sonarScannerHostUrl SONAR_LOGIN: from_secret: sonarScannerLogin +trigger: + event: + - push + - pull_request +--- +kind: pipeline +type: docker +name: Publish + +steps: + - name: createTag + image: ubuntu:latest + commands: + - apt-get update && apt-get install -y git + - ./build.sh --publish --from-ci + - git remote add pushing https://$GITEA_USR:$GITEA_PASS@gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform.git + - git push pushing --tags + environment: + GITEA_USR: + from_secret: giteaUsr + GITEA_PASS: + from_secret: giteaPass + + - name: scpFiles + image: appleboy/drone-scp + settings: + host: amine-louveau.fr + username: ubuntu + key: + from_secret: privateKey + port: 22 + target: /home/ubuntu/ + source: version.txt + + - name: deploy + image: appleboy/drone-ssh + settings: + host: amine-louveau.fr + user: ubuntu + key: + from_secret: privateKey + command_timeout: 2m + script: + - cd /home/ubuntu + - sudo rm -rf /var/www/amine/version.txt + - sudo chown www-data:www-data ./version.txt + - sudo mv version.txt /var/www/amine/ + +trigger: + event: + - promote + target: + - production + +--- +kind: pipeline +type: docker +name: Release + +steps: - name: build image: mingc/android-build-box:latest commands: - - ./gradlew :androidApp:build -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false \ No newline at end of file + - echo "Generate APK" + - ./gradlew :androidApp:assembleGithubConfigRelease -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false + - echo "---------------------------------------------------------" + - echo "Get Key" + - wget https://amine-louveau.fr/key + - echo "---------------------------------------------------------" + - echo "Zipalign" + - $ANDROID_HOME/build-tools/31.0.0/zipalign -f -v 4 androidApp/build/outputs/apk/githubConfig/release/androidApp-githubConfig-release-unsigned.apk androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk + - echo "---------------------------------------------------------" + - echo "Sign" + - $ANDROID_HOME/build-tools/31.0.0/apksigner sign -v --out signed.apk --ks ./key --ks-key-alias $YOUR_KEY_ALIAS --ks-pass pass:$YOUR_KEYSTORE_PASSWORD --v1-signing-enabled true --v2-signing-enabled true androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk + - echo "---------------------------------------------------------" + - echo "Verify" + - $ANDROID_HOME/build-tools/31.0.0/apksigner verify signed.apk + environment: + YOUR_KEYSTORE_PASSWORD: + from_secret: keyPass + YOUR_KEY_ALIAS: + from_secret: keyAlias + + - name: gitea_release + image: plugins/gitea-release + settings: + api_key: + from_secret: giteaAPI + base_url: https://gitea.amine-louveau.fr + files: signed.apk +trigger: + event: + - tag \ No newline at end of file diff --git a/build.sh b/build.sh index a8bcc27..c68baad 100755 --- a/build.sh +++ b/build.sh @@ -21,11 +21,11 @@ VERSION="${INITIAL_VERSION}${TODAYS_VERSION}" PARAMS_EXCEPT_PUBLISH=$(echo $1 | sed 's/\-\-publish//') -./version.sh ${VERSION} ${PARAMS_EXCEPT_PUBLISH} +./version.sh ${VERSION} ${PARAMS_EXCEPT_PUBLISH} $@ if [[ "$@" == *'--publish'* ]] then - ./publish-version.sh ${VERSION} + ./publish-version.sh ${VERSION} $@ else echo "Did not publish. If you wanted to do so, call the script with \"--publish\" or \"--publish-local\"." fi diff --git a/publish-version.sh b/publish-version.sh index 4d4c5b9..38ed8a6 100755 --- a/publish-version.sh +++ b/publish-version.sh @@ -5,7 +5,11 @@ rm -f version.txt printf "versionName=$1-github\nversionCode=$1" >> version.txt -# You'll need to change server as your server and define a VERSION_PATH. -scp version.txt server:$VERSION_PATH - -rm version.txt +if [[ "$@" == *'--from-ci'* ]] +then + echo "File created. HANDLE IN CI" +else + # You'll need to change server as your server and define a VERSION_PATH. + scp version.txt server:$VERSION_PATH + rm version.txt +fi diff --git a/version.sh b/version.sh index 3bb4033..afb679a 100755 --- a/version.sh +++ b/version.sh @@ -1,11 +1,14 @@ #!/bin/bash -# You can pass --force as first parameter to force push and tag creation. -echo "Creating tag $@" +echo "Creating tag $1" -TAG="v$@" -git tag ${TAG} +TAG="v$1" +git tag -a ${TAG} -m ${TAG} -echo "Pushing tag" - -git push origin ${TAG} +if [[ "$@" == *'--from-ci'* ]] +then + echo "Tag created. HANDLE IN CI" +else + echo "Pushing tag" + git push origin ${TAG} +fi From 4c12c9d570be64ae9a68db70fd68b6b4c14537f7 Mon Sep 17 00:00:00 2001 From: aminecmi Date: Wed, 14 Sep 2022 14:54:13 +0200 Subject: [PATCH 07/11] added badge. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f9166ad..338b57f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ReaderForSelfoss-multiplatform +# ReaderForSelfoss-multiplatform [![Build Status](https://build.amine-louveau.fr/api/badges/Louvorg/ReaderForSelfoss-multiplatform/status.svg)](https://build.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/readerforselfoss/localized.svg)](https://crowdin.com/project/readerforselfoss) From 4184bbb90020b841defad398f9ccd232215ee18e Mon Sep 17 00:00:00 2001 From: aminecmi Date: Sat, 24 Sep 2022 13:54:48 +0200 Subject: [PATCH 08/11] Update gradle. --- androidApp/build.gradle.kts | 1 + androidApp/src/main/AndroidManifest.xml | 3 +-- build.gradle.kts | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- shared/build.gradle.kts | 1 + shared/src/androidMain/AndroidManifest.xml | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index df60d29..ed8c38c 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -101,6 +101,7 @@ android { kotlinOptions { jvmTarget = "1.8" } + namespace = "bou.amine.apps.readerforselfossv2.android" } diff --git a/androidApp/src/main/AndroidManifest.xml b/androidApp/src/main/AndroidManifest.xml index d46e8e1..21addf9 100644 --- a/androidApp/src/main/AndroidManifest.xml +++ b/androidApp/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> diff --git a/build.gradle.kts b/build.gradle.kts index 8be2b47..27db859 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10") - classpath("com.android.tools.build:gradle:7.2.2") + classpath("com.android.tools.build:gradle:7.3.0") // sonarquve classpath("org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513") diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e4ddf24..b8c9f22 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Wed Feb 09 17:05:19 CET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 725d301..0a348ad 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -112,6 +112,7 @@ android { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } + namespace = "bou.amine.apps.readerforselfossv2" } sqldelight { diff --git a/shared/src/androidMain/AndroidManifest.xml b/shared/src/androidMain/AndroidManifest.xml index 1829faa..568741e 100644 --- a/shared/src/androidMain/AndroidManifest.xml +++ b/shared/src/androidMain/AndroidManifest.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file From 6d11dfb80cf4a60614b91f94ca3f8b32500c5611 Mon Sep 17 00:00:00 2001 From: aminecmi Date: Sat, 24 Sep 2022 14:43:24 +0200 Subject: [PATCH 09/11] Fixing #59. --- .../readerforselfossv2/model/SelfossModel.kt | 12 ++ .../repository/RepositoryImpl.kt | 82 +++++++----- .../readerforselfossv2/rest/SelfossApi.kt | 123 ++++++++++-------- 3 files changed, 130 insertions(+), 87 deletions(-) diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt index 15decfb..e392786 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt @@ -105,4 +105,16 @@ class SelfossModel { return this } } + + class StatusAndData(val success: Boolean, val data: T? = null) { + companion object { + fun succes(d: T): StatusAndData { + return StatusAndData(true, d) + } + + fun error(): StatusAndData { + return StatusAndData(false) + } + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt index c970507..db9a789 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt @@ -47,7 +47,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun getNewerItems(): ArrayList { // TODO: Use the updatedSince parameter - var fetchedItems: List? = null + var fetchedItems: SelfossModel.StatusAndData> = SelfossModel.StatusAndData.error() if (isNetworkAvailable()) { fetchedItems = api.getItems( displayedItems.type, @@ -59,23 +59,25 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap ) } else { if (appSettingsService.isItemCachingEnabled()) { - fetchedItems = getDBItems().filter { - displayedItems == ItemType.ALL || - (it.unread && displayedItems == ItemType.UNREAD) || - (it.starred && displayedItems == ItemType.STARRED) - }.map { it.toView() } + fetchedItems = SelfossModel.StatusAndData.succes( + getDBItems().filter { + displayedItems == ItemType.ALL || + (it.unread && displayedItems == ItemType.UNREAD) || + (it.starred && displayedItems == ItemType.STARRED) + }.map { it.toView() } + ) } } - if (fetchedItems != null) { - items = ArrayList(fetchedItems) + if (fetchedItems.success && fetchedItems.data != null) { + items = ArrayList(fetchedItems.data!!) sortItems() } return items } suspend fun getOlderItems(): ArrayList { - var fetchedItems: List? = null + var fetchedItems: SelfossModel.StatusAndData> = SelfossModel.StatusAndData.error() if (isNetworkAvailable()) { val offset = items.size fetchedItems = api.getItems( @@ -88,16 +90,16 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap ) } // When using the db cache, we load everything the first time, so there should be nothing more to load. - if (fetchedItems != null) { - items.addAll(fetchedItems) + if (fetchedItems.success && fetchedItems.data != null) { + items.addAll(fetchedItems.data!!) sortItems() } return items } - private suspend fun getMaxItemsForBackground(itemType: ItemType): List? { + private suspend fun getMaxItemsForBackground(itemType: ItemType): List { return if (isNetworkAvailable()) { - api.getItems( + val items = api.getItems( itemType.type, 0, tagFilter?.tag, @@ -106,6 +108,11 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap null, 200 ) + return if (items.success && items.data != null) { + items.data + } else { + emptyList() + } } else { emptyList() } @@ -119,10 +126,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap var success = false if (isNetworkAvailable()) { val response = api.stats() - if (response != null) { - badgeUnread = response.unread - badgeAll = response.total - badgeStarred = response.starred + if (response.success && response.data != null) { + badgeUnread = response.data.unread + badgeAll = response.data.total + badgeStarred = response.data.starred success = true } } else { @@ -138,10 +145,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun getTags(): List? { return if (isNetworkAvailable()) { val apiTags = api.tags() - if (apiTags != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { - resetDBTagsWithData(apiTags) + if (apiTags.success && apiTags.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { + resetDBTagsWithData(apiTags.data) } - apiTags + apiTags.data } else { getDBTags().map { it.toView() } } @@ -149,7 +156,12 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun getSpouts(): Map? { return if (isNetworkAvailable()) { - api.spouts() + val spouts = api.spouts() + return if (spouts.success && spouts.data != null) { + spouts.data + } else { + emptyMap() // TODO: do something here + } } else { throw NetworkUnavailableException() } @@ -158,10 +170,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun getSources(): ArrayList? { return if (isNetworkAvailable()) { val apiSources = api.sources() - if (apiSources != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { - resetDBSourcesWithData(apiSources) + if (apiSources.success && apiSources.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) { + resetDBSourcesWithData(apiSources.data) } - apiSources + apiSources.data } else { ArrayList(getDBSources().map { it.toView() }) } @@ -178,7 +190,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private suspend fun markAsReadById(id: Int): Boolean { return if (isNetworkAvailable()) { - api.markAsRead(id.toString())?.isSuccess == true + api.markAsRead(id.toString())?.isSuccess } else { insertDBAction(id.toString(), read = true) true @@ -197,7 +209,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private suspend fun unmarkAsReadById(id: Int): Boolean { return if (isNetworkAvailable()) { - api.unmarkAsRead(id.toString())?.isSuccess == true + api.unmarkAsRead(id.toString())?.isSuccess } else { insertDBAction(id.toString(), unread = true) true @@ -215,7 +227,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private suspend fun starrById(id: Int): Boolean { return if (isNetworkAvailable()) { - api.starr(id.toString())?.isSuccess == true + api.starr(id.toString())?.isSuccess } else { insertDBAction(id.toString(), starred = true) true @@ -233,7 +245,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap private suspend fun unstarrById(id: Int): Boolean { return if (isNetworkAvailable()) { - api.unstarr(id.toString())?.isSuccess == true + api.unstarr(id.toString())?.isSuccess } else { insertDBAction(id.toString(), starred = true) true @@ -243,7 +255,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun markAllAsRead(items: ArrayList): Boolean { var success = false - if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess == true) { + if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess) { success = true for (item in items) { markAsReadLocally(item) @@ -332,7 +344,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap suspend fun updateRemote(): Boolean { return if (isNetworkAvailable()) { - api.update()?.equals("finished") ?: false + api.update()?.equals("finished") } else { false } @@ -365,8 +377,8 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap if (isNetworkAvailable()) { val fetchedVersion = api.version() - if (fetchedVersion != null && fetchedVersion.getApiMajorVersion() != apiMajorVersion) { - appSettingsService.updateApiVersion(fetchedVersion.getApiMajorVersion()) + if (fetchedVersion.success && fetchedVersion.data != null && fetchedVersion.data.getApiMajorVersion() != apiMajorVersion) { + appSettingsService.updateApiVersion(fetchedVersion.data.getApiMajorVersion()) } } } @@ -383,7 +395,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap fun getDBSources(): List = db.sourcesQueries.sources().executeAsList() - fun resetDBTagsWithData(tagEntities: List) { + private fun resetDBTagsWithData(tagEntities: List) { db.tagsQueries.deleteAllTags() db.tagsQueries.transaction { @@ -393,7 +405,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap } } - fun resetDBSourcesWithData(sources: List) { + private fun resetDBSourcesWithData(sources: List) { db.sourcesQueries.deleteAllSources() db.sourcesQueries.transaction { @@ -425,7 +437,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap val newItems = getMaxItemsForBackground(ItemType.UNREAD) val allItems = getMaxItemsForBackground(ItemType.ALL) val starredItems = getMaxItemsForBackground(ItemType.STARRED) - insertDBItems(newItems.orEmpty() + allItems.orEmpty() + starredItems.orEmpty()) + insertDBItems(newItems + allItems + starredItems) return newItems } catch (e: Throwable) { // We do nothing 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 c094532..c9f5c9a 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 @@ -10,6 +10,7 @@ import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.logging.* import io.ktor.client.request.* import io.ktor.client.request.forms.* +import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json @@ -34,7 +35,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { appSettingsService.logApiCalls(message) } } - level = LogLevel.ALL + level = LogLevel.INFO } install(HttpTimeout) { requestTimeoutMillis = appSettingsService.getApiTimeout() @@ -65,11 +66,11 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { client = createHttpClient() } - suspend fun login(): SelfossModel.SuccessResponse? = - client.get(url("/login")) { + suspend fun login(): SelfossModel.SuccessResponse = + maybeResponse(client.get(url("/login")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) suspend fun getItems( type: String, @@ -79,8 +80,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { search: String?, updatedSince: String?, items: Int? = null - ): List? = - client.get(url("/items")) { + ): SelfossModel.StatusAndData> = + bodyOrFailure(client.get(url("/items")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) parameter("type", type) @@ -90,74 +91,74 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { parameter("updatedsince", updatedSince) parameter("items", items ?: appSettingsService.getItemsNumber()) parameter("offset", offset) - }.body() + }) - suspend fun stats(): SelfossModel.Stats? = - client.get(url("/stats")) { + suspend fun stats(): SelfossModel.StatusAndData = + bodyOrFailure(client.get(url("/stats")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun tags(): List? = - client.get(url("/tags")) { + suspend fun tags(): SelfossModel.StatusAndData> = + bodyOrFailure(client.get(url("/tags")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun update(): String? = - client.get(url("/update")) { + suspend fun update(): SelfossModel.StatusAndData = + bodyOrFailure(client.get(url("/update")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun spouts(): Map? = - client.get(url("/sources/spouts")) { + suspend fun spouts(): SelfossModel.StatusAndData> = + bodyOrFailure(client.get(url("/sources/spouts")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun sources(): ArrayList? = - client.get(url("/sources/list")) { + suspend fun sources(): SelfossModel.StatusAndData> = + bodyOrFailure(client.get(url("/sources/list")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun version(): SelfossModel.ApiVersion? = - client.get(url("/api/about")).body() + suspend fun version(): SelfossModel.StatusAndData = + bodyOrFailure(client.get(url("/api/about"))) - suspend fun markAsRead(id: String): SelfossModel.SuccessResponse? = - client.post(url("/mark/$id")) { + suspend fun markAsRead(id: String): SelfossModel.SuccessResponse = + maybeResponse(client.post(url("/mark/$id")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse? = - client.post(url("/unmark/$id")) { + suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse = + maybeResponse(client.post(url("/unmark/$id")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun starr(id: String): SelfossModel.SuccessResponse? = - client.post(url("/starr/$id")) { + suspend fun starr(id: String): SelfossModel.SuccessResponse = + maybeResponse(client.post(url("/starr/$id")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun unstarr(id: String): SelfossModel.SuccessResponse? = - client.post(url("/unstarr/$id")) { + suspend fun unstarr(id: String): SelfossModel.SuccessResponse = + maybeResponse(client.post(url("/unstarr/$id")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) - suspend fun markAllAsRead(ids: List): SelfossModel.SuccessResponse? = - client.submitForm( + suspend fun markAllAsRead(ids: List): SelfossModel.SuccessResponse = + maybeResponse(client.submitForm( url = url("/mark"), formParameters = Parameters.build { append("username", appSettingsService.getUserName()) append("password", appSettingsService.getPassword()) ids.map { append("ids[]", it) } } - ).body() + )) suspend fun createSourceForVersion( title: String, @@ -166,12 +167,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { tags: String, filter: String, version: Int - ): SelfossModel.SuccessResponse? = - if (version > 1) { - createSource2(title, url, spout, tags, filter) - } else { - createSource(title, url, spout, tags, filter) - } + ): SelfossModel.SuccessResponse = + maybeResponse( + if (version > 1) { + createSource2(title, url, spout, tags, filter) + } else { + createSource(title, url, spout, tags, filter) + } + ) suspend fun createSource( title: String, @@ -179,7 +182,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { spout: String, tags: String, filter: String - ): SelfossModel.SuccessResponse? = + ): HttpResponse = client.submitForm( url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"), formParameters = Parameters.build { @@ -189,7 +192,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { append("tags", tags) append("filter", filter) } - ).body() + ) suspend fun createSource2( title: String, @@ -197,7 +200,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { spout: String, tags: String, filter: String - ): SelfossModel.SuccessResponse? = + ): HttpResponse = client.submitForm( url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"), formParameters = Parameters.build { @@ -207,11 +210,27 @@ class SelfossApi(private val appSettingsService: AppSettingsService) { append("tags[]", tags) append("filter", filter) } - ).body() + ) - suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse? = - client.delete(url("/source/$id")) { + suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse = + maybeResponse(client.delete(url("/source/$id")) { parameter("username", appSettingsService.getUserName()) parameter("password", appSettingsService.getPassword()) - }.body() + }) + + suspend fun maybeResponse(r: HttpResponse): SelfossModel.SuccessResponse { + return if (r.status.isSuccess()) { + r.body() + } else { + SelfossModel.SuccessResponse(false) + } + } + + suspend inline fun bodyOrFailure(r: HttpResponse): SelfossModel.StatusAndData { + return if (r.status.isSuccess()) { + SelfossModel.StatusAndData.succes(r.body()) + } else { + SelfossModel.StatusAndData.error() + } + } } \ No newline at end of file From 270d959ee05a8b9e03ee4f145667a5ebc81d02fd Mon Sep 17 00:00:00 2001 From: aminecmi Date: Sat, 24 Sep 2022 14:55:06 +0200 Subject: [PATCH 10/11] Cleaning. --- .../apps/readerforselfossv2/android/AddSourceActivity.kt | 4 ++-- .../amine/apps/readerforselfossv2/android/LoginActivity.kt | 4 ++-- .../readerforselfossv2/android/fragments/ArticleFragment.kt | 4 ++-- .../amine/apps/readerforselfossv2/android/utils/LinksUtils.kt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/AddSourceActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/AddSourceActivity.kt index 0c7093c..3cec99e 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/AddSourceActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/AddSourceActivity.kt @@ -9,7 +9,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import bou.amine.apps.readerforselfossv2.android.databinding.ActivityAddSourceBinding import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.themes.Toppings -import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid +import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.service.AppSettingsService @@ -84,7 +84,7 @@ class AddSourceActivity : AppCompatActivity(), DIAware { super.onResume() val baseUrl = appSettingsService.getBaseUrl() - if (baseUrl.isEmpty() || !baseUrl.isBaseUrlValid(this@AddSourceActivity)) { + if (baseUrl.isEmpty() || baseUrl.isBaseUrlInvalid(this@AddSourceActivity)) { mustLoginToAddSource() } else { handleSpoutsSpinner(binding.spoutsSpinner, binding.progress, binding.formContainer) diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt index c7883b6..be92dd2 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt @@ -14,7 +14,7 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding import bou.amine.apps.readerforselfossv2.android.themes.AppColors -import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid +import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.service.AppSettingsService import com.mikepenz.aboutlibraries.LibsBuilder @@ -122,7 +122,7 @@ class LoginActivity : AppCompatActivity(), DIAware { var cancel = false var focusView: View? = null - if (!url.isBaseUrlValid(this@LoginActivity)) { + if (url.isBaseUrlInvalid(this@LoginActivity)) { binding.urlView.error = getString(R.string.login_url_problem) focusView = binding.urlView cancel = true diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt index f50786a..7e24dea 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt @@ -268,8 +268,8 @@ class ArticleFragment : Fragment(), DIAware { private fun getContentFromMercury(customTabsIntent: CustomTabsIntent) { if (repository.isNetworkAvailable()) { - binding.progressBar.visibility = View.VISIBLE - val parser = MercuryApi() + binding.progressBar.visibility = View.VISIBLE + val parser = MercuryApi() parser.parseUrl(url).enqueue( object : Callback { diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt index 1fc608a..600864b 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt @@ -163,7 +163,7 @@ private fun openInBrowser(linkDecoded: String, app: Activity) { fun String.isUrlValid(): Boolean = this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches() -fun String.isBaseUrlValid(ctx: Context): Boolean { +fun String.isBaseUrlInvalid(ctx: Context): Boolean { val baseUrl = this.toHttpUrlOrNull() var existsAndEndsWithSlash = false if (baseUrl != null) { @@ -171,7 +171,7 @@ fun String.isBaseUrlValid(ctx: Context): Boolean { existsAndEndsWithSlash = "" == pathSegments[pathSegments.size - 1] } - return Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash + return !(Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash) } fun Context.openInBrowserAsNewTask(i: SelfossModel.Item) { From 0264da8ccc4a03761c14278bfdc3dafe20cf54d3 Mon Sep 17 00:00:00 2001 From: aminecmi Date: Sat, 24 Sep 2022 15:00:33 +0200 Subject: [PATCH 11/11] Fixing #54. --- .../amine/apps/readerforselfossv2/android/LoginActivity.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt index be92dd2..bc32762 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt @@ -115,9 +115,9 @@ class LoginActivity : AppCompatActivity(), DIAware { binding.passwordView.error = null // Store values at the time of the login attempt. - val url = binding.urlView.text.toString() - val login = binding.loginView.text.toString() - val password = binding.passwordView.text.toString() + val url = binding.urlView.text.toString().trim() + val login = binding.loginView.text.toString().trim() + val password = binding.passwordView.text.toString().trim() var cancel = false var focusView: View? = null