Compare commits

..

25 Commits

Author SHA1 Message Date
AmineB 11c39ae87c Merge pull request 'Bump dependencies' (#173) from upgarde into master
Check master code / build (push) Successful in 19m42s
Create tag / build (push) Successful in 27m4s
Create tag / createTagAndChangelog (push) Successful in 52s
Create tag / release (push) Successful in 7m41s
Reviewed-on: #173
2025-01-03 08:41:15 +00:00
AmineB 6645902ec8 chore: "faster" action.
Check PR code / Lint (pull_request) Successful in 5m59s
Check PR code / build (pull_request) Successful in 14m16s
2025-01-03 09:19:12 +01:00
AmineB 0a07a5dfad fastlane: icon change.
Check PR code / Lint (pull_request) Has been cancelled
Check PR code / build (pull_request) Has been cancelled
2025-01-03 09:18:06 +01:00
AmineB d88d38fd3b chore: ignoring a pixel issue.
Check PR code / Lint (pull_request) Successful in 9m15s
Check PR code / build (pull_request) Successful in 23m30s
2025-01-02 20:53:00 +01:00
AmineB 28fe38aa17 test: fixed an ui test issue.
Check PR code / Lint (pull_request) Successful in 11m23s
Check PR code / build (pull_request) Successful in 19m41s
2025-01-02 20:32:53 +01:00
AmineB d524c30732 fix: center the loading thing.
Check PR code / Lint (pull_request) Successful in 5m35s
Check PR code / build (pull_request) Successful in 18m15s
2024-12-31 15:31:47 +01:00
AmineB 8c00aa65da test: items displaying.
Check PR code / Lint (pull_request) Successful in 9m8s
Check PR code / build (pull_request) Successful in 36m8s
2024-12-31 15:23:26 +01:00
AmineB ae81261cb1 bump: sqldelight.
Check PR code / Lint (pull_request) Successful in 1m47s
Check PR code / build (pull_request) Successful in 15m9s
2024-12-31 12:37:36 +01:00
AmineB 03c567ee33 bump: material, desugar jdk, jsoup, kodein, settings, napier, mock.
Check PR code / Lint (pull_request) Successful in 1m37s
Check PR code / build (pull_request) Successful in 13m29s
2024-12-31 11:45:11 +01:00
AmineB d23dd82fc2 bump: androix and coroutines.
Check PR code / Lint (pull_request) Successful in 1m50s
Check PR code / build (pull_request) Successful in 13m29s
2024-12-31 11:06:36 +01:00
AmineB 2e7a168424 bump: ktor. Closes #67.
Check PR code / Lint (pull_request) Successful in 47s
Check PR code / build (pull_request) Successful in 15m22s
2024-12-31 10:03:21 +01:00
giteadrone 5bc2f614af Changelog for v124123651
Check master code / build (push) Successful in 12m31s
2024-12-30 22:54:15 +00:00
AmineB 934c112db5 Merge pull request 'Bugfixes' (#171) from bugfixes into master
Check master code / build (push) Successful in 19m51s
Create tag / build (push) Successful in 21m4s
Create tag / createTagAndChangelog (push) Successful in 50s
Create tag / release (push) Successful in 3m39s
Reviewed-on: #171
2024-12-30 22:32:11 +00:00
AmineB ad7549a89f config: crowdin
Check PR code / Lint (pull_request) Successful in 54s
Check PR code / build (pull_request) Successful in 10m35s
2024-12-30 23:20:03 +01:00
AmineB fb9ceecabd chore: can links be empty ?
Check PR code / Lint (pull_request) Successful in 1m3s
Check PR code / build (pull_request) Successful in 11m14s
2024-12-30 22:45:18 +01:00
AmineB 61b9fd30e0 fix: Context issues in article fragment. 2024-12-30 22:37:36 +01:00
AmineB 806e56e20b fix: Context issues in fragment sheet.
Check PR code / Lint (pull_request) Successful in 1m7s
Check PR code / build (pull_request) Successful in 12m15s
2024-12-30 22:12:38 +01:00
AmineB cd8b7aaf9d fix: build. 2024-12-30 22:12:21 +01:00
AmineB c25ad7621e chore: compile issue fix.
Check PR code / Lint (pull_request) Successful in 1m10s
Check PR code / build (pull_request) Successful in 9m57s
2024-12-30 15:07:35 +01:00
AmineB 63da3b9fe7 chore: filter some bugs. 2024-12-30 15:07:18 +01:00
AmineB 1d99eeb633 bugfix: catch users using something other than selfoss. 2024-12-30 13:51:45 +01:00
AmineB 162a350a8f bugfix: No browser, no link. 2024-12-30 13:51:14 +01:00
AmineB 27c1bba146 translations
Check PR code / Lint (pull_request) Successful in 54s
Check PR code / build (pull_request) Successful in 9m26s
2024-12-30 13:49:42 +01:00
AmineB b7f3a9877a chore: remove log. 2024-12-30 12:58:34 +01:00
AmineB 47f78754dc translation
Check PR code / Lint (pull_request) Successful in 46s
Check PR code / build (pull_request) Failing after 3m20s
2024-12-30 12:48:42 +01:00
57 changed files with 671 additions and 374 deletions
+25 -4
View File
@@ -3,7 +3,7 @@ on:
workflow_call: workflow_call:
jobs: jobs:
BuildAndTest: BuildAndTestAndCoverage:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out repository code - name: Check out repository code
@@ -16,9 +16,30 @@ jobs:
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: '17' java-version: '17'
- name: Setup Android SDK cache: gradle
uses: android-actions/setup-android@v3 - uses: gradle/actions/setup-gradle@v3
- uses: android-actions/setup-android@v3
- name: Configure gradle... - name: Configure gradle...
run: mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true" >> ~/.gradle/gradle.properties run: mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true" >> ~/.gradle/gradle.properties
- name: Build and test - name: Build and test
run: ./gradlew build -x test --stacktrace run: ./gradlew build -x testReleaseUnitTest -x testDebugUnitTest -x testGithubConfigReleaseUnitTest -x testGithubConfigDebugUnitTest # These tests will be done
- uses: KengoTODA/actions-setup-docker-compose@v1
with:
version: "2.23.3"
- name: run selfoss
run: |
docker compose -f .gitea/workflows/assets/docker-compose.yml up -d
- name: coverage
run: |
./gradlew :koverHtmlReport
- uses: actions/upload-artifact@v3
with:
name: coverage
path: build/reports/kover/html
retention-days: 1
overwrite: true
include-hidden-files: true
- name: Clean
if: always()
run: |
docker compose -f .gitea/workflows/assets/docker-compose.yml stop
+1
View File
@@ -85,6 +85,7 @@ jobs:
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: '17' java-version: '17'
cache: gradle
- name: Setup Android SDK - name: Setup Android SDK
uses: android-actions/setup-android@v3 uses: android-actions/setup-android@v3
- name: Configure gradle... - name: Configure gradle...
+3 -1
View File
@@ -12,8 +12,9 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- uses: actions/setup-java@v4 - uses: actions/setup-java@v4
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin'
java-version: '17' java-version: '17'
cache: gradle
- name: Install klint - name: Install klint
run: curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.0.0/ktlint && chmod a+x ktlint && mv ktlint /usr/local/bin/ run: curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.0.0/ktlint && chmod a+x ktlint && mv ktlint /usr/local/bin/
- name: Install detekt - name: Install detekt
@@ -23,4 +24,5 @@ jobs:
- name: Detecting... - name: Detecting...
run: ./detekt-cli-1.23.1/bin/detekt-cli --all-rules --excludes '**/shared/build/**/*.kt' || true run: ./detekt-cli-1.23.1/bin/detekt-cli --all-rules --excludes '**/shared/build/**/*.kt' || true
build: build:
needs: Lint
uses: ./.gitea/workflows/common_build.yml uses: ./.gitea/workflows/common_build.yml
-44
View File
@@ -1,44 +0,0 @@
name: Check master code
on:
push:
branches:
- master
jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch tags
run: git fetch --tags -p
- uses: KengoTODA/actions-setup-docker-compose@v1
with:
version: "2.23.3"
- name: run selfoss
run: |
docker compose -f .gitea/workflows/assets/docker-compose.yml up -d
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: gradle
- uses: gradle/actions/setup-gradle@v3
- uses: android-actions/setup-android@v3
- name: Configure gradle...
run: mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true" >> ~/.gradle/gradle.properties
- name: coverage
run: |
./gradlew :koverHtmlReport
- uses: actions/upload-artifact@v3
with:
name: coverage
path: build/reports/kover/html
retention-days: 1
overwrite: true
include-hidden-files: true
- name: Clean
if: always()
run: |
docker compose -f .gitea/workflows/assets/docker-compose.yml stop
+19
View File
@@ -1,3 +1,22 @@
**v124123651
- Merge pull request 'Bugfixes' (#171) from bugfixes into master
- config: crowdin
- chore: can links be empty ?
- fix: Context issues in article fragment.
- fix: Context issues in fragment sheet.
- fix: build.
- chore: compile issue fix.
- chore: filter some bugs.
- bugfix: catch users using something other than selfoss.
- bugfix: No browser, no link.
- translations
- chore: remove log.
- translation
- Changelog for v124123641
--------------------------------------------------------------------
**v124123641 **v124123641
- Chore: no tests on build. - Chore: no tests on build.
+28 -29
View File
@@ -1,7 +1,7 @@
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
val ignoreGitVersion: String by project val ignoreGitVersion: String by project
val acraVersion = "5.9.7" val acraVersion = "5.12.0"
plugins { plugins {
id("com.android.application") id("com.android.application")
@@ -9,6 +9,7 @@ plugins {
kotlin("kapt") kotlin("kapt")
id("com.mikepenz.aboutlibraries.plugin") id("com.mikepenz.aboutlibraries.plugin")
id("org.jetbrains.kotlinx.kover") id("org.jetbrains.kotlinx.kover")
id("app.cash.sqldelight") version "2.0.2"
} }
fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String { fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
@@ -65,14 +66,14 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = "17" jvmTarget = "17"
} }
compileSdk = 34 compileSdk = 35
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
} }
defaultConfig { defaultConfig {
applicationId = "bou.amine.apps.readerforselfossv2.android" applicationId = "bou.amine.apps.readerforselfossv2.android"
minSdk = 25 minSdk = 25
targetSdk = 34 targetSdk = 34 // 35 when edge-to-edge is handled
versionCode = versionCodeFromGit() versionCode = versionCodeFromGit()
versionName = versionNameFromGit() versionName = versionNameFromGit()
@@ -119,28 +120,26 @@ android {
} }
dependencies { 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(project(":shared"))
implementation("com.google.android.material:material:1.9.0") implementation("androidx.appcompat:appcompat:1.7.0")
implementation("androidx.appcompat:appcompat:1.6.1") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1")
implementation("androidx.preference:preference-ktx:1.2.1") implementation("androidx.preference:preference-ktx:1.2.1")
implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs"))) implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs")))
// Android Support // Android Support
implementation("androidx.appcompat:appcompat:1.6.1") implementation("com.google.android.material:material:1.12.0")
implementation("com.google.android.material:material:1.9.0") implementation("androidx.recyclerview:recyclerview:1.4.0-rc01")
implementation("androidx.recyclerview:recyclerview:1.3.1")
implementation("androidx.legacy:legacy-support-v4:1.0.0") 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.cardview:cardview:1.0.0")
implementation("androidx.annotation:annotation:1.7.0") implementation("androidx.annotation:annotation:1.9.1")
implementation("androidx.work:work-runtime-ktx:2.8.1") implementation("androidx.work:work-runtime-ktx:2.10.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.2.0")
implementation("org.jsoup:jsoup:1.15.4") implementation("org.jsoup:jsoup:1.18.3")
//multidex //multidex
implementation("androidx.multidex:multidex:2.0.1") implementation("androidx.multidex:multidex:2.0.1")
@@ -153,31 +152,31 @@ dependencies {
implementation("com.ashokvarma.android:bottom-navigation-bar:2.2.0") implementation("com.ashokvarma.android:bottom-navigation-bar:2.2.0")
// glide // glide
kapt("com.github.bumptech.glide:compiler:4.15.0") kapt("com.github.bumptech.glide:compiler:4.16.0")
implementation("com.github.bumptech.glide:okhttp3-integration:4.15.0") implementation("com.github.bumptech.glide:okhttp3-integration:4.16.0")
// Themes // Themes
implementation("com.github.rubensousa:floatingtoolbar:1.5.1") implementation("com.github.rubensousa:floatingtoolbar:1.5.1")
// Pager // Pager
implementation("me.relex:circleindicator:2.1.6") implementation("me.relex:circleindicator:2.1.6")
implementation("androidx.viewpager2:viewpager2:1.1.0-beta02") implementation("androidx.viewpager2:viewpager2:1.1.0")
//Dependency Injection //Dependency Injection
implementation("org.kodein.di:kodein-di:7.14.0") implementation("org.kodein.di:kodein-di:7.23.1")
implementation("org.kodein.di:kodein-di-framework-android-x:7.14.0") implementation("org.kodein.di:kodein-di-framework-android-x:7.23.1")
implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.14.0") implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.23.1")
//Settings //Settings
implementation("com.russhwolf:multiplatform-settings-no-arg:0.9") implementation("com.russhwolf:multiplatform-settings-no-arg:1.3.0")
//Logging //Logging
implementation("io.github.aakira:napier:2.6.1") implementation("io.github.aakira:napier:2.7.1")
//PhotoView //PhotoView
implementation("com.github.chrisbanes:PhotoView:2.3.0") 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") implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
@@ -185,13 +184,13 @@ 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("app.cash.sqldelight:android-driver:2.0.2")
//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.13.14")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
androidTestImplementation("androidx.test:runner:1.6.2") androidTestImplementation("androidx.test:runner:1.6.2")
androidTestImplementation("androidx.test:rules:1.6.1") androidTestImplementation("androidx.test:rules:1.6.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
@@ -203,7 +202,7 @@ dependencies {
implementation("ch.acra:acra-http:$acraVersion") implementation("ch.acra:acra-http:$acraVersion")
implementation("ch.acra:acra-toast:$acraVersion") implementation("ch.acra:acra-toast:$acraVersion")
implementation("com.google.auto.service:auto-service:1.1.1")
} }
tasks.withType<Test> { tasks.withType<Test> {
@@ -2,6 +2,7 @@ package bou.amine.apps.readerforselfossv2.android
import android.content.Context import android.content.Context
import androidx.annotation.ArrayRes import androidx.annotation.ArrayRes
import androidx.test.espresso.Espresso.onData
import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.replaceText 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.withId
import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.Matchers.hasToString
fun performLogin(someUrl: String? = null) { fun performLogin(someUrl: String? = null) {
onView(withId(R.id.urlView)).perform(click()).perform( onView(withId(R.id.urlView)).perform(click()).perform(
@@ -22,7 +24,6 @@ fun performLogin(someUrl: String? = null) {
) )
) )
onView(withId(R.id.signInButton)).perform(click()) onView(withId(R.id.signInButton)).perform(click())
Thread.sleep(10000)
} }
fun loginAndInitHome() { fun loginAndInitHome() {
@@ -88,3 +89,21 @@ fun testPreferencesFromArray(
onView(withText(res)).check(matches(allOf(isDisplayed(), isChecked()))) 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()))
}
@@ -1,9 +1,6 @@
package bou.amine.apps.readerforselfossv2.android 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.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
@@ -47,9 +44,7 @@ class HomeActivityTest {
isClickable() isClickable()
) )
) )
openActionBarOverflowOrOptionsMenu( openMenu()
ApplicationProvider.getApplicationContext<Context>()
)
onView(withText(R.string.readAll)).check(matches(isDisplayed())) onView(withText(R.string.readAll)).check(matches(isDisplayed()))
onView(withText(R.string.menu_home_sources)).check(matches(isDisplayed())) onView(withText(R.string.menu_home_sources)).check(matches(isDisplayed()))
onView(withText(R.string.title_activity_settings)).check(matches(isDisplayed())) onView(withText(R.string.title_activity_settings)).check(matches(isDisplayed()))
@@ -78,43 +73,31 @@ class HomeActivityTest {
).check(matches(isDisplayed())) ).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack()) onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu( openMenu()
ApplicationProvider.getApplicationContext<Context>()
)
onView(withText(R.string.readAll)).perform(click()) onView(withText(R.string.readAll)).perform(click())
onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed())) onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack()) onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu( openMenu()
ApplicationProvider.getApplicationContext<Context>()
)
onView(withText(R.string.menu_home_sources)).perform(click()) onView(withText(R.string.menu_home_sources)).perform(click())
onView(withId(R.id.fab)).check(matches(isDisplayed())) onView(withId(R.id.fab)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack()) onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu( openMenu()
ApplicationProvider.getApplicationContext<Context>()
)
onView(withText(R.string.title_activity_settings)).perform(click()) onView(withText(R.string.title_activity_settings)).perform(click())
onView(withText(R.string.pref_header_general)).check(matches(isDisplayed())) onView(withText(R.string.pref_header_general)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack()) onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu( openMenu()
ApplicationProvider.getApplicationContext<Context>()
)
onView(withText(R.string.menu_home_refresh)).perform(click()) onView(withText(R.string.menu_home_refresh)).perform(click())
onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed())) onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack()) onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu( openMenu()
ApplicationProvider.getApplicationContext<Context>()
)
/*onView(withText(R.string.issue_tracker_link)).perform(click()) /*onView(withText(R.string.issue_tracker_link)).perform(click())
onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed())) onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack()) onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu( openMenu()*/
ApplicationProvider.getApplicationContext<Context>()
)*/
onView(withText(R.string.action_disconnect)).perform(click()) onView(withText(R.string.action_disconnect)).perform(click())
onView(withText(R.string.confirm_disconnect_title)).check(matches(isDisplayed())) onView(withText(R.string.confirm_disconnect_title)).check(matches(isDisplayed()))
@@ -62,7 +62,7 @@ class LoginActivityTest {
@Test @Test
fun urlError() { fun urlError() {
performLogin("172.17.0.1:8888") performLogin("10.0.2.2:8888")
onView(withId(R.id.urlView)).perform(click()) onView(withId(R.id.urlView)).perform(click())
onView(withId(R.id.urlView)).check(matches(withError(R.string.wrong_infos))) onView(withId(R.id.urlView)).check(matches(withError(R.string.wrong_infos)))
} }
@@ -6,6 +6,7 @@ import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.replaceText 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.action.ViewActions.typeTextIntoFocusedView
import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isChecked 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( onView(withSettingsCheckboxWidget(R.string.display_all_counts_title)).check(
matches( matches(
allOf( allOf(
@@ -1,9 +1,7 @@
package bou.amine.apps.readerforselfossv2.android package bou.amine.apps.readerforselfossv2.android
import android.content.Context import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
@@ -33,9 +31,7 @@ class SettingsActivityTest {
context = activity.window.context context = activity.window.context
} }
loginAndInitHome() loginAndInitHome()
openActionBarOverflowOrOptionsMenu( openMenu()
ApplicationProvider.getApplicationContext<Context>()
)
onView(withText(R.string.title_activity_settings)).perform(click()) onView(withText(R.string.title_activity_settings)).perform(click())
} }
@@ -1,19 +1,20 @@
package bou.amine.apps.readerforselfossv2.android package bou.amine.apps.readerforselfossv2.android
import android.content.Context import androidx.test.espresso.AmbiguousViewMatcherException
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView 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.click
import androidx.test.espresso.action.ViewActions.scrollCompletelyTo import androidx.test.espresso.action.ViewActions.swipeDown
import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed 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.withId
import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest import androidx.test.filters.LargeTest
import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@@ -23,7 +24,6 @@ import java.util.UUID
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@LargeTest @LargeTest
class SourcesActivityTest { class SourcesActivityTest {
@get:Rule @get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java) val activityRule = ActivityScenarioRule(LoginActivity::class.java)
@@ -34,32 +34,50 @@ class SourcesActivityTest {
sourceName = UUID.randomUUID().toString().substring(0, 15) sourceName = UUID.randomUUID().toString().substring(0, 15)
loginAndInitHome() loginAndInitHome()
openActionBarOverflowOrOptionsMenu( goToSources()
ApplicationProvider.getApplicationContext<Context>()
)
onView(withText(R.string.menu_home_sources))
.perform(click())
} }
@Test @Test
fun addSource() { fun addSource() {
onView(withId(R.id.fab)) testAddSourceWithUrl(
.perform(click()) "https://lorem-rss.herokuapp.com/feed?unit=year&interval=1&length=10",
onView(withId(R.id.nameInput)) sourceName
.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()))
} }
@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())
}
} }
@@ -1,5 +1,6 @@
package bou.amine.apps.readerforselfossv2.android package bou.amine.apps.readerforselfossv2.android
import android.content.Context
import android.view.View import android.view.View
import android.widget.EditText import android.widget.EditText
import android.widget.ImageView import android.widget.ImageView
@@ -7,6 +8,8 @@ import android.widget.RelativeLayout
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.graphics.drawable.toBitmap 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.Root
import androidx.test.espresso.matcher.RootMatchers.isPlatformPopup import androidx.test.espresso.matcher.RootMatchers.isPlatformPopup
import androidx.test.espresso.matcher.ViewMatchers.hasSibling import androidx.test.espresso.matcher.ViewMatchers.hasSibling
@@ -99,3 +102,9 @@ fun withSettingsCheckboxFrame(@StringRes id: Int): Matcher<View>? {
) )
) )
} }
fun openMenu() {
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>()
)
}
@@ -1,7 +1,6 @@
package bou.amine.apps.readerforselfossv2.android package bou.amine.apps.readerforselfossv2.android
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@@ -32,6 +31,7 @@ import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge
import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowser
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
@@ -589,9 +589,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.issue_tracker -> { R.id.issue_tracker -> {
val browserIntent = baseContext.openUrlInBrowser(AppSettingsService.trackerUrl)
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
startActivity(browserIntent)
return true return true
} }
@@ -8,11 +8,11 @@ import android.widget.ImageView.ScaleType
import androidx.recyclerview.widget.RecyclerView 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.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
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.circularDrawable import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask import bou.amine.apps.readerforselfossv2.android.utils.openItemUrlInBrowserAsNewTask
import bou.amine.apps.readerforselfossv2.android.utils.shareLink 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
@@ -71,7 +71,7 @@ class ItemCardAdapter(
} }
binding.browserBtn.setOnClickListener { binding.browserBtn.setOnClickListener {
c.openInBrowserAsNewTask(items[position]) c.openItemUrlInBrowserAsNewTask(items[position])
} }
} }
@@ -6,8 +6,8 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView 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.ListItemBinding import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
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
@@ -1,16 +1,20 @@
package bou.amine.apps.readerforselfossv2.android.fragments package bou.amine.apps.readerforselfossv2.android.fragments
import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.TypedArray import android.content.res.TypedArray
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Typeface import android.graphics.Typeface
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.TypedValue import android.util.TypedValue
import android.view.* import android.view.GestureDetector
import android.view.InflateException
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.webkit.WebResourceResponse import android.webkit.WebResourceResponse
import android.webkit.WebSettings import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
@@ -25,10 +29,11 @@ import bou.amine.apps.readerforselfossv2.android.databinding.FragmentArticleBind
import bou.amine.apps.readerforselfossv2.android.model.ParecelableItem import bou.amine.apps.readerforselfossv2.android.model.ParecelableItem
import bou.amine.apps.readerforselfossv2.android.model.toModel import bou.amine.apps.readerforselfossv2.android.model.toModel
import bou.amine.apps.readerforselfossv2.android.model.toParcelable import bou.amine.apps.readerforselfossv2.android.model.toParcelable
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream
import bou.amine.apps.readerforselfossv2.android.utils.isUrlValid import bou.amine.apps.readerforselfossv2.android.utils.isUrlValid
import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask import bou.amine.apps.readerforselfossv2.android.utils.openItemUrlInBrowserAsNewTask
import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowser
import bou.amine.apps.readerforselfossv2.android.utils.shareLink import bou.amine.apps.readerforselfossv2.android.utils.shareLink
import bou.amine.apps.readerforselfossv2.model.MercuryModel import bou.amine.apps.readerforselfossv2.model.MercuryModel
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
@@ -47,13 +52,14 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.acra.ktx.sendSilentlyWithAcra
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.android.x.closestDI import org.kodein.di.android.x.closestDI
import org.kodein.di.instance import org.kodein.di.instance
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.URL import java.net.URL
import java.util.* import java.util.Locale
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
private const val IMAGE_JPG = "image/jpg" private const val IMAGE_JPG = "image/jpg"
@@ -98,7 +104,12 @@ class ArticleFragment : Fragment(), DIAware {
try { try {
binding = FragmentArticleBinding.inflate(inflater, container, false) binding = FragmentArticleBinding.inflate(inflater, container, false)
try {
url = item.getLinkDecoded() url = item.getLinkDecoded()
} catch (e: Exception) {
e.sendSilentlyWithAcra()
}
contentText = item.content contentText = item.content
contentTitle = item.title.getHtmlDecoded() contentTitle = item.title.getHtmlDecoded()
contentImage = item.getThumbnail(repository.baseUrl) contentImage = item.getThumbnail(repository.baseUrl)
@@ -152,7 +163,7 @@ class ArticleFragment : Fragment(), DIAware {
) )
} catch (e: InflateException) { } catch (e: InflateException) {
e.sendSilentlyWithAcraWithName("webview not available") e.sendSilentlyWithAcraWithName("webview not available")
if (context != null) { try {
AlertDialog.Builder(requireContext()) AlertDialog.Builder(requireContext())
.setMessage(requireContext().getString(R.string.webview_dialog_issue_message)) .setMessage(requireContext().getString(R.string.webview_dialog_issue_message))
.setTitle(requireContext().getString(R.string.webview_dialog_issue_title)) .setTitle(requireContext().getString(R.string.webview_dialog_issue_title))
@@ -164,6 +175,8 @@ class ArticleFragment : Fragment(), DIAware {
} }
.create() .create()
.show() .show()
} catch (e: IllegalStateException) {
e.sendSilentlyWithAcraWithName("Context required is null")
} }
} }
@@ -211,16 +224,16 @@ class ArticleFragment : Fragment(), DIAware {
override fun onItemClick(item: MenuItem) { override fun onItemClick(item: MenuItem) {
when (item.itemId) { when (item.itemId) {
R.id.share_action -> requireActivity().shareLink(url, contentTitle) R.id.share_action -> requireActivity().shareLink(url, contentTitle)
R.id.open_action -> requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item) R.id.open_action -> requireActivity().openItemUrlInBrowserAsNewTask(this@ArticleFragment.item)
R.id.unread_action -> R.id.unread_action ->
if (context != null) { try {
if (this@ArticleFragment.item.unread) { if (this@ArticleFragment.item.unread) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.markAsRead(this@ArticleFragment.item) repository.markAsRead(this@ArticleFragment.item)
} }
this@ArticleFragment.item.unread = false this@ArticleFragment.item.unread = false
Toast.makeText( Toast.makeText(
context, requireContext(),
R.string.marked_as_read, R.string.marked_as_read,
Toast.LENGTH_LONG, Toast.LENGTH_LONG,
).show() ).show()
@@ -235,7 +248,10 @@ class ArticleFragment : Fragment(), DIAware {
Toast.LENGTH_LONG, Toast.LENGTH_LONG,
).show() ).show()
} }
} catch (e: IllegalStateException) {
e.sendSilentlyWithAcraWithName("Context required is null")
} }
else -> Unit else -> Unit
} }
} }
@@ -288,7 +304,7 @@ class ArticleFragment : Fragment(), DIAware {
contentText = data.content.orEmpty() contentText = data.content.orEmpty()
htmlToWebview() htmlToWebview()
handleLeadImage(data?.lead_image_url) handleLeadImage(data.lead_image_url)
binding.nestedScrollView.scrollTo(0, 0) binding.nestedScrollView.scrollTo(0, 0)
binding.progressBar.visibility = View.GONE binding.progressBar.visibility = View.GONE
@@ -320,11 +336,7 @@ class ArticleFragment : Fragment(), DIAware {
url: String, url: String,
): Boolean { ): Boolean {
return if (context != null && url.isUrlValid() && binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { return if (context != null && url.isUrlValid() && binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
try { requireContext().openUrlInBrowser(url)
requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
} catch (e: ActivityNotFoundException) {
e.sendSilentlyWithAcraWithName("activityNotFound > $url")
}
true true
} else { } else {
false false
@@ -343,7 +355,8 @@ class ArticleFragment : Fragment(), DIAware {
) { ) {
try { try {
val image = val image =
Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() Glide.with(view).asBitmap().apply(glideOptions).load(url).submit()
.get()
return WebResourceResponse( return WebResourceResponse(
IMAGE_JPG, IMAGE_JPG,
"UTF-8", "UTF-8",
@@ -355,7 +368,8 @@ class ArticleFragment : Fragment(), DIAware {
} else if (url.lowercase(Locale.US).contains(".png")) { } else if (url.lowercase(Locale.US).contains(".png")) {
try { try {
val image = val image =
Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() Glide.with(view).asBitmap().apply(glideOptions).load(url).submit()
.get()
return WebResourceResponse( return WebResourceResponse(
IMAGE_JPG, IMAGE_JPG,
"UTF-8", "UTF-8",
@@ -367,7 +381,8 @@ class ArticleFragment : Fragment(), DIAware {
} else if (url.lowercase(Locale.US).contains(".webp")) { } else if (url.lowercase(Locale.US).contains(".webp")) {
try { try {
val image = val image =
Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() Glide.with(view).asBitmap().apply(glideOptions).load(url).submit()
.get()
return WebResourceResponse( return WebResourceResponse(
IMAGE_JPG, IMAGE_JPG,
"UTF-8", "UTF-8",
@@ -384,7 +399,7 @@ class ArticleFragment : Fragment(), DIAware {
} }
private fun htmlToWebview() { private fun htmlToWebview() {
if (context != null) { try {
val attrs: IntArray = intArrayOf(android.R.attr.fontFamily) val attrs: IntArray = intArrayOf(android.R.attr.fontFamily)
val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs) val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs)
@@ -529,6 +544,8 @@ class ArticleFragment : Fragment(), DIAware {
"utf-8", "utf-8",
null, null,
) )
} catch (e: IllegalStateException) {
e.sendSilentlyWithAcraWithName("Context required is null")
} }
} }
@@ -544,10 +561,10 @@ class ArticleFragment : Fragment(), DIAware {
private fun openInBrowserAfterFailing() { private fun openInBrowserAfterFailing() {
binding.progressBar.visibility = View.GONE binding.progressBar.visibility = View.GONE
if (context != null) { try {
requireContext().openInBrowserAsNewTask(this@ArticleFragment.item) requireContext().openItemUrlInBrowserAsNewTask(this@ArticleFragment.item)
} else { } catch (e: IllegalStateException) {
Exception("openInBrowserAfterFailing context is null").sendSilentlyWithAcraWithName("openInBrowserAfterFailing > $context") e.sendSilentlyWithAcraWithName("Context required is null")
} }
} }
@@ -15,7 +15,7 @@ import android.view.ViewGroup
import bou.amine.apps.readerforselfossv2.android.HomeActivity import bou.amine.apps.readerforselfossv2.android.HomeActivity
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.utils.getColorHexCode import bou.amine.apps.readerforselfossv2.utils.getColorHexCode
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
@@ -52,19 +52,17 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
false, false,
) )
val context: Context? = context try {
if (context == null) {
dismiss()
Exception("FilterSheetFragment context is null").sendSilentlyWithAcraWithName("FilterSheetFragment > onCreateView")
} else {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
handleTagChips(context) handleTagChips(requireContext())
handleSourceChips(context) handleSourceChips(requireContext())
binding.progressBar2.visibility = GONE binding.progressBar2.visibility = GONE
binding.filterView.visibility = VISIBLE binding.filterView.visibility = VISIBLE
} }
} catch (e: IllegalStateException) {
dismiss()
e.sendSilentlyWithAcraWithName("FilterSheetFragment > onCreateView")
} }
binding.floatingActionButton2.setOnClickListener { binding.floatingActionButton2.setOnClickListener {
@@ -2,7 +2,7 @@ package bou.amine.apps.readerforselfossv2.android.model
import android.content.Context import android.content.Context
import android.webkit.URLUtil import android.webkit.URLUtil
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.utils.getImages import bou.amine.apps.readerforselfossv2.utils.getImages
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
@@ -1,7 +1,5 @@
package bou.amine.apps.readerforselfossv2.android.settings package bou.amine.apps.readerforselfossv2.android.settings
import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.InputFilter import android.text.InputFilter
@@ -16,7 +14,8 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySettingsBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySettingsBinding
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowser
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.mikepenz.aboutlibraries.LibsBuilder import com.mikepenz.aboutlibraries.LibsBuilder
import org.kodein.di.DIAware import org.kodein.di.DIAware
@@ -105,7 +104,9 @@ class SettingsActivity :
preferenceManager.findPreference<Preference>("currentMode")?.onPreferenceChangeListener = preferenceManager.findPreference<Preference>("currentMode")?.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _, newValue -> Preference.OnPreferenceChangeListener { _, newValue ->
AppCompatDelegate.setDefaultNightMode(newValue.toString().toInt()) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯ AppCompatDelegate.setDefaultNightMode(
newValue.toString().toInt()
) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
true true
} }
@@ -129,7 +130,8 @@ class SettingsActivity :
) { ) {
setPreferencesFromResource(R.xml.pref_general, rootKey) setPreferencesFromResource(R.xml.pref_general, rootKey)
val editTextPreference = preferenceManager.findPreference<EditTextPreference>("prefer_api_items_number") val editTextPreference =
preferenceManager.findPreference<EditTextPreference>("prefer_api_items_number")
editTextPreference?.setOnBindEditTextListener { editText -> editTextPreference?.setOnBindEditTextListener { editText ->
editText.inputType = InputType.TYPE_CLASS_NUMBER editText.inputType = InputType.TYPE_CLASS_NUMBER
editText.filters = editText.filters =
@@ -139,8 +141,11 @@ class SettingsActivity :
val input: Int = (dest.toString() + source.toString()).toInt() val input: Int = (dest.toString() + source.toString()).toInt()
if (input in 1..200) return@InputFilter null if (input in 1..200) return@InputFilter null
} catch (nfe: NumberFormatException) { } catch (nfe: NumberFormatException) {
nfe.sendSilentlyWithAcraWithName("GeneralPreferenceFragment") Toast.makeText(
Toast.makeText(activity, R.string.items_number_should_be_number, Toast.LENGTH_LONG).show() activity,
R.string.items_number_should_be_number,
Toast.LENGTH_LONG
).show()
} }
"" ""
}, },
@@ -222,16 +227,17 @@ class SettingsActivity :
preferenceManager.findPreference<Preference>("currentMode")?.onPreferenceChangeListener = preferenceManager.findPreference<Preference>("currentMode")?.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _, newValue -> Preference.OnPreferenceChangeListener { _, newValue ->
AppCompatDelegate.setDefaultNightMode(newValue.toString().toInt()) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯ AppCompatDelegate.setDefaultNightMode(
newValue.toString().toInt()
) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
true true
} }
} }
} }
class LinksPreferenceFragment : PreferenceFragmentCompat() { class LinksPreferenceFragment : PreferenceFragmentCompat() {
private fun openUrl(uri: Uri?) { private fun openUrl(url: String) {
val browserIntent = Intent(Intent.ACTION_VIEW, uri) context?.openUrlInBrowser(url)
startActivity(browserIntent)
} }
override fun onCreatePreferences( override fun onCreatePreferences(
@@ -242,19 +248,19 @@ class SettingsActivity :
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener = preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
openUrl(Uri.parse(AppSettingsService.trackerUrl)) openUrl(AppSettingsService.trackerUrl)
true true
} }
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener = preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
openUrl(Uri.parse(AppSettingsService.sourceUrl)) openUrl(AppSettingsService.sourceUrl)
false false
} }
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener = preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener =
Preference.OnPreferenceClickListener { Preference.OnPreferenceClickListener {
openUrl(Uri.parse(AppSettingsService.translationUrl)) openUrl(AppSettingsService.translationUrl)
false false
} }
} }
@@ -1,6 +1,7 @@
package bou.amine.apps.readerforselfossv2.android.utils package bou.amine.apps.readerforselfossv2.android.utils
import android.app.Activity import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
@@ -17,6 +18,7 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
fun Context.openItemUrl( fun Context.openItemUrl(
currentItem: Int, currentItem: Int,
linkDecoded: String, linkDecoded: String,
@@ -35,15 +37,13 @@ fun Context.openItemUrl(
intent.putExtra("currentItem", currentItem) intent.putExtra("currentItem", currentItem)
app.startActivity(intent) app.startActivity(intent)
} else { } else {
val intent = Intent(Intent.ACTION_VIEW) this.openUrlInBrowserAsNewTask(linkDecoded)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.data = Uri.parse(linkDecoded.toStringUriWithHttp())
startActivity(intent)
} }
} }
} }
fun String.isUrlValid(): Boolean = this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches() fun String.isUrlValid(): Boolean =
this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
fun String.isBaseUrlInvalid(): Boolean { fun String.isBaseUrlInvalid(): Boolean {
val baseUrl = this.toHttpUrlOrNull() val baseUrl = this.toHttpUrlOrNull()
@@ -56,11 +56,31 @@ fun String.isBaseUrlInvalid(): Boolean {
return !(Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash) return !(Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash)
} }
fun Context.openInBrowserAsNewTask(i: SelfossModel.Item) { fun Context.openItemUrlInBrowserAsNewTask(i: SelfossModel.Item) {
this.openUrlInBrowserAsNewTask(i.getLinkDecoded().toStringUriWithHttp())
}
fun Context.openUrlInBrowserAsNewTask(url: String) {
val intent = Intent(Intent.ACTION_VIEW) val intent = Intent(Intent.ACTION_VIEW)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.data = Uri.parse(i.getLinkDecoded().toStringUriWithHttp()) intent.data = Uri.parse(url)
startActivity(intent) this.mayBeStartActivity(intent)
}
fun Context.openUrlInBrowser(url: String) {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url)
this.mayBeStartActivity(intent)
}
fun Context.mayBeStartActivity(intent: Intent) {
try {
this.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(this, getString(R.string.no_browser), Toast.LENGTH_SHORT).show()
}
} }
class LinkOnTouchListener : View.OnTouchListener { class LinkOnTouchListener : View.OnTouchListener {
@@ -1,4 +1,4 @@
package bou.amine.apps.readerforselfossv2.android package bou.amine.apps.readerforselfossv2.android.utils.acra
import org.acra.ACRA import org.acra.ACRA
import org.acra.ktx.sendSilentlyWithAcra import org.acra.ktx.sendSilentlyWithAcra
@@ -0,0 +1,29 @@
package bou.amine.apps.readerforselfossv2.android.utils.acra
import android.content.Context
import android.os.DeadSystemException
import com.google.auto.service.AutoService
import org.acra.builder.ReportBuilder
import org.acra.config.CoreConfiguration
import org.acra.config.ReportingAdministrator
import org.acra.data.CrashReportData
@AutoService(ReportingAdministrator::class)
class AcraReportingAdministrator : ReportingAdministrator {
override fun shouldStartCollecting(
context: Context,
config: CoreConfiguration,
reportBuilder: ReportBuilder
): Boolean {
return reportBuilder.exception !is DeadSystemException && (reportBuilder.exception != null && reportBuilder.exception!!::class.simpleName != "CannotDeliverBroadcastException")
}
override fun shouldSendReport(
context: Context,
config: CoreConfiguration,
crashReportData: CrashReportData
): Boolean {
return crashReportData.get("BRAND") != "redroid"
}
}
@@ -23,6 +23,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:padding="@dimen/activity_horizontal_margin"> android:padding="@dimen/activity_horizontal_margin">
<!-- Login progress --> <!-- Login progress -->
@@ -37,7 +38,7 @@
<LinearLayout <LinearLayout
android:id="@+id/loginForm" android:id="@+id/loginForm"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<EditText <EditText
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"La contrasenya és massa curta"</string> <string name="error_invalid_password">"La contrasenya és massa curta"</string>
<string name="error_field_required">"Camp necessari"</string> <string name="error_field_required">"Camp necessari"</string>
<string name="prompt_url">"URL"</string> <string name="prompt_url">"URL"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Autenticació (si és necessària)"</string> <string name="withLoginSwitch">"Autenticació (si és necessària)"</string>
<string name="login_url_problem">"Pot ser que falti una \"/\" al final de l'url."</string> <string name="login_url_problem">"Pot ser que falti una \"/\" al final de l'url."</string>
<string name="prompt_login">"Nom d'usuari"</string> <string name="prompt_login">"Nom d'usuari"</string>
@@ -83,7 +84,7 @@
<string name="pref_switch_items_caching">Guarda els elements per utilitzar-los sense connexió</string> <string name="pref_switch_items_caching">Guarda els elements per utilitzar-los sense connexió</string>
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="network_connectivity_lost">Sense connexió!</string> <string name="network_connectivity_lost">"Sense connexió!"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string> <string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sincronitza els articles</string> <string name="pref_switch_periodic_refresh">Sincronitza els articles</string>
<string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string> <string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string>
@@ -123,5 +124,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"No hi ha res"</string>
<string name="tab_new">"Nou"</string>
<string name="tab_read">"Tot"</string>
<string name="tab_favs">"Preferits"</string>
<string name="action_about">"Quant a"</string>
<string name="marked_as_read">"Element llegit"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"Passwort ist nicht lang genug"</string> <string name="error_invalid_password">"Passwort ist nicht lang genug"</string>
<string name="error_field_required">"Pflichtfeld"</string> <string name="error_field_required">"Pflichtfeld"</string>
<string name="prompt_url">"URL"</string> <string name="prompt_url">"URL"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Anmeldung erforderlich?"</string> <string name="withLoginSwitch">"Anmeldung erforderlich?"</string>
<string name="login_url_problem">"Ups. Du musst eventuell ein \"/\" am Ende der URL anhängen."</string> <string name="login_url_problem">"Ups. Du musst eventuell ein \"/\" am Ende der URL anhängen."</string>
<string name="prompt_login">"Benutzername"</string> <string name="prompt_login">"Benutzername"</string>
@@ -123,5 +124,12 @@
<string name="update_source">Quelle aktualisieren</string> <string name="update_source">Quelle aktualisieren</string>
<string name="confirm_disconnect_title">Verbindung trennen?</string> <string name="confirm_disconnect_title">Verbindung trennen?</string>
<string name="confirm_disconnect_description">Die Verbindung zur Selfoss-Instanz wird getrennt.</string> <string name="confirm_disconnect_description">Die Verbindung zur Selfoss-Instanz wird getrennt.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Keine Einträge vorhanden"</string>
<string name="tab_new">"Neu"</string>
<string name="tab_read">"Alle"</string>
<string name="tab_favs">"Favoriten"</string>
<string name="action_about">"Über"</string>
<string name="marked_as_read">"Artikel gelesen"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"La contraseña no es suficientemente larga"</string> <string name="error_invalid_password">"La contraseña no es suficientemente larga"</string>
<string name="error_field_required">"Campo requerido"</string> <string name="error_field_required">"Campo requerido"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Inicio de sesión requerido ?"</string> <string name="withLoginSwitch">"Inicio de sesión requerido ?"</string>
<string name="login_url_problem">"Oops. Puede que necesite añadir un \"/\" al final de la url."</string> <string name="login_url_problem">"Oops. Puede que necesite añadir un \"/\" al final de la url."</string>
<string name="prompt_login">"Nombre de usuario"</string> <string name="prompt_login">"Nombre de usuario"</string>
@@ -83,7 +84,7 @@
<string name="pref_switch_items_caching">Guardar elementos para uso sin conexión</string> <string name="pref_switch_items_caching">Guardar elementos para uso sin conexión</string>
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="network_connectivity_lost">Sin conexión!</string> <string name="network_connectivity_lost">"Sin conexión!"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string> <string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sincronizar artículos</string> <string name="pref_switch_periodic_refresh">Sincronizar artículos</string>
<string name="pref_switch_periodic_refresh_off">Los artículos no se sincronizarán en segundo plano</string> <string name="pref_switch_periodic_refresh_off">Los artículos no se sincronizarán en segundo plano</string>
@@ -123,5 +124,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Nada aquí"</string>
<string name="tab_new">"Nuevo"</string>
<string name="tab_read">"Todo"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Acerca de"</string>
<string name="marked_as_read">"Artículo leído"</string>
<string name="marked_as_unread">"Artículo no leído"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"Mot de passe trop court"</string> <string name="error_invalid_password">"Mot de passe trop court"</string>
<string name="error_field_required">"Champ requis"</string> <string name="error_field_required">"Champ requis"</string>
<string name="prompt_url">"Url Selfoss"</string> <string name="prompt_url">"Url Selfoss"</string>
<string name="disable_ssl">"Désactiver la vérification SSL"</string>
<string name="withLoginSwitch">"Avec login ?"</string> <string name="withLoginSwitch">"Avec login ?"</string>
<string name="login_url_problem">"Petit souci. Il manque peut-être un / à la fin ?"</string> <string name="login_url_problem">"Petit souci. Il manque peut-être un / à la fin ?"</string>
<string name="prompt_login">"Utilisateur"</string> <string name="prompt_login">"Utilisateur"</string>
@@ -24,7 +25,7 @@
<string name="all_posts_read">"Tous les posts sont lus"</string> <string name="all_posts_read">"Tous les posts sont lus"</string>
<string name="undo_string">"Annuler"</string> <string name="undo_string">"Annuler"</string>
<string name="addStringNoUrl">"Identifiez-vous pour ajouter une source."</string> <string name="addStringNoUrl">"Identifiez-vous pour ajouter une source."</string>
<string name="cant_get_sources">"Impossible de récupérer la liste des sources"</string> <string name="cant_get_sources">"Impossible de récupérer la liste des sources."</string>
<string name="cant_create_source">"Impossible de créer la source."</string> <string name="cant_create_source">"Impossible de créer la source."</string>
<string name="cant_get_spouts_no_network">"Impossible d'obtenir la liste des spouts en raison d'un problème de réseau."</string> <string name="cant_get_spouts_no_network">"Impossible d'obtenir la liste des spouts en raison d'un problème de réseau."</string>
<string name="cant_get_spouts">"Impossible d'obtenir la liste des spouts. Cela pourrait venir de l'api."</string> <string name="cant_get_spouts">"Impossible d'obtenir la liste des spouts. Cela pourrait venir de l'api."</string>
@@ -35,13 +36,13 @@
<string name="warning_wrong_url">"ATTENTION"</string> <string name="warning_wrong_url">"ATTENTION"</string>
<string name="pref_switch_card_view_title">"Vue en carte"</string> <string name="pref_switch_card_view_title">"Vue en carte"</string>
<string name="share">"Partager"</string> <string name="share">"Partager"</string>
<string name="switch_unread_count">"Afficher le nombre d'articles non lus sur la barre en bas de l'écran"</string> <string name="switch_unread_count">"Afficher le nombre d'articles non lus sur la barre en bas de l'écran."</string>
<string name="switch_unread_count_title">"Afficher le nombre de non lus"</string> <string name="switch_unread_count_title">"Afficher le nombre de non lus"</string>
<string name="display_all_counts_title">"Afficher le nombre de favoris et d'articles lus"</string> <string name="display_all_counts_title">"Afficher le nombre de favoris et d'articles lus"</string>
<string name="text_wrong_url">"Vous semblez essayer de vous connecter avec une URL invalide. Assurez-vous que c'est la bonne, et si le problème persiste, contactez-moi via le lien du play store. Notez aussi que l'application ne peut fonctionner sans l'application web Selfoss. Vous ne pouvez pas utiliser l'application pour accéder directement aux flux RSS."</string> <string name="text_wrong_url">"Vous semblez essayer de vous connecter avec une URL invalide. Assurez-vous que c'est la bonne, et si le problème persiste, contactez-moi via le lien du play store. Notez aussi que l'application ne peut fonctionner sans l'application web Selfoss. Vous ne pouvez pas utiliser l'application pour accéder directement aux flux RSS."</string>
<string name="pref_article_viewer_title">"Ouvrir les liens dans l'application"</string> <string name="pref_article_viewer_title">"Ouvrir les liens dans l'application"</string>
<string name="pref_article_viewer_on">"Les articles s'ouvriront dans l'application"</string> <string name="pref_article_viewer_on">"Les articles s'ouvriront dans l'application"</string>
<string name="pref_article_viewer_off">"Les articles s'ouvriront dans votre naviguateur par défaut"</string> <string name="pref_article_viewer_off">"Les articles s'ouvriront dans votre navigateur par défaut"</string>
<string name="pref_general_category_links">"Gestion des liens"</string> <string name="pref_general_category_links">"Gestion des liens"</string>
<string name="pref_general_category_displaying">"Affichage"</string> <string name="pref_general_category_displaying">"Affichage"</string>
<string name="pref_switch_card_view_on">"Les articles seront affichés en forme de carte"</string> <string name="pref_switch_card_view_on">"Les articles seront affichés en forme de carte"</string>
@@ -58,7 +59,7 @@
<string name="filter_item_sources">Sources</string> <string name="filter_item_sources">Sources</string>
<string name="menu_home_search">Rechercher</string> <string name="menu_home_search">Rechercher</string>
<string name="can_delete_source">Impossible de supprimer la source…</string> <string name="can_delete_source">Impossible de supprimer la source…</string>
<string name="base_url_error">Il y a eu un souci lors de la communication avec votre instance Selfoss. Si le problèmes persiste, contactez-moi pour trouver une solution.</string> <string name="base_url_error">Il y a eu un souci lors de la communication avec votre instance Selfoss. Si le problème persiste, contactez-moi pour trouver une solution.</string>
<string name="pref_header_theme">Thèmes</string> <string name="pref_header_theme">Thèmes</string>
<string name="pref_selfoss_category">Api Selfoss</string> <string name="pref_selfoss_category">Api Selfoss</string>
<string name="pref_api_items_number_title">Nombre d\'articles chargés</string> <string name="pref_api_items_number_title">Nombre d\'articles chargés</string>
@@ -74,7 +75,7 @@
<string name="pref_header_viewer">Lecteur d\'articles</string> <string name="pref_header_viewer">Lecteur d\'articles</string>
<string name="refresh_dialog_message">En validant, votre instance Selfoss sera mise à jour.</string> <string name="refresh_dialog_message">En validant, votre instance Selfoss sera mise à jour.</string>
<string name="markall_dialog_message">Marquer tous les éléments comme lus ?</string> <string name="markall_dialog_message">Marquer tous les éléments comme lus ?</string>
<string name="pref_switch_actions_pager_scroll">Marquer comme lu à la navigation.</string> <string name="pref_switch_actions_pager_scroll">Marquer comme lu à la navigation</string>
<string name="pref_switch_actions_pager_scroll_off">Ne pas marquer les articles comme lus à la navigation.</string> <string name="pref_switch_actions_pager_scroll_off">Ne pas marquer les articles comme lus à la navigation.</string>
<string name="unmark">Marquer l\'article comme non lu</string> <string name="unmark">Marquer l\'article comme non lu</string>
<string name="pref_header_offline">Hors ligne et cache</string> <string name="pref_header_offline">Hors ligne et cache</string>
@@ -86,9 +87,9 @@
<string name="network_connectivity_lost">"Connexion au réseau perdue"</string> <string name="network_connectivity_lost">"Connexion au réseau perdue"</string>
<string name="network_connectivity_retrieved">"Connexion réseau de nouveau disponible"</string> <string name="network_connectivity_retrieved">"Connexion réseau de nouveau disponible"</string>
<string name="pref_switch_periodic_refresh">Synchroniser les articles</string> <string name="pref_switch_periodic_refresh">Synchroniser les articles</string>
<string name="pref_switch_periodic_refresh_off">Les articles ne seront pas synchronisés en arrière plan</string> <string name="pref_switch_periodic_refresh_off">Les articles ne seront pas synchronisés en arrière-plan</string>
<string name="pref_switch_periodic_refresh_on">Articles seront périodiquement synchronisées</string> <string name="pref_switch_periodic_refresh_on">Articles seront périodiquement synchronisés</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Interval de synchronisation ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Intervalle de synchronisation ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Synchroniser uniquement lorsque le téléphone est en charge</string> <string name="pref_switch_refresh_when_charging">Synchroniser uniquement lorsque le téléphone est en charge</string>
<string name="loading_notification_title">Chargement …</string> <string name="loading_notification_title">Chargement …</string>
<string name="loading_notification_text">Selfoss synchronise vos articles</string> <string name="loading_notification_text">Selfoss synchronise vos articles</string>
@@ -100,7 +101,7 @@
<string name="shortcut_offline">Hors ligne</string> <string name="shortcut_offline">Hors ligne</string>
<string name="pref_api_timeout">Timeout de l\'api</string> <string name="pref_api_timeout">Timeout de l\'api</string>
<string name="pref_header_experimental">Expérimental</string> <string name="pref_header_experimental">Expérimental</string>
<string name="webview_dialog_issue_message">Pas de Webview disponible. Désactivation du lecteur d\'article pour éviter les futures erreurs. Les articles seront lu via votre navigateur à l\'avenir.</string> <string name="webview_dialog_issue_message">Pas de Webview disponible. Désactivation du lecteur d\'article pour éviter les futures erreurs. Les articles seront lus via votre navigateur à l\'avenir.</string>
<string name="webview_dialog_issue_title">Problème de Webview</string> <string name="webview_dialog_issue_title">Problème de Webview</string>
<string name="reader_text_align_left">Aligner à gauche</string> <string name="reader_text_align_left">Aligner à gauche</string>
<string name="reader_text_align_justify">Justifier le texte</string> <string name="reader_text_align_justify">Justifier le texte</string>
@@ -113,8 +114,8 @@
<string name="mode_dark">Thème sombre</string> <string name="mode_dark">Thème sombre</string>
<string name="mode_system">Utiliser les paramètres système</string> <string name="mode_system">Utiliser les paramètres système</string>
<string name="mode_light">Thème clair</string> <string name="mode_light">Thème clair</string>
<string name="gdpr_dialog_title">L\'application ne partage aucune information personnelle</string> <string name="gdpr_dialog_title">L\'application ne partage aucune information personnelle.</string>
<string name="gdpr_dialog_message"><![CDATA[Le rapport de plantage est activés par défaut. Il peut être désactivé depuis les paramètres de l\'application. Notez que les rapports de plantage sont essentiels pour le développement de l'application.]]></string> <string name="gdpr_dialog_message"><![CDATA[Le rapport de plantage est activés par défaut. Il peut être désactivé depuis les paramètres de l\'application. Notez que les rapports de plantage sont essentiels pour le développement de l\'application.]]></string>
<string name="crash_toast_text">Un bug s\'est produit. Le développeur en sera informé.</string> <string name="crash_toast_text">Un bug s\'est produit. Le développeur en sera informé.</string>
<string name="pref_switch_disable_acra">"Désactiver les rapports de plantage."</string> <string name="pref_switch_disable_acra">"Désactiver les rapports de plantage."</string>
<string name="menu_home_filter">Filtres</string> <string name="menu_home_filter">Filtres</string>
@@ -123,5 +124,12 @@
<string name="update_source">Mise à jour des sources</string> <string name="update_source">Mise à jour des sources</string>
<string name="confirm_disconnect_title">Se déconnecter ?</string> <string name="confirm_disconnect_title">Se déconnecter ?</string>
<string name="confirm_disconnect_description">Vous allez être déconnecté de votre instance Selfoss.</string> <string name="confirm_disconnect_description">Vous allez être déconnecté de votre instance Selfoss.</string>
<string name="disable_ssl">Désactiver la vérification SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Il n'y a rien ici !"</string>
<string name="tab_new">"Non lus"</string>
<string name="tab_read">"Tous"</string>
<string name="tab_favs">"Favoris"</string>
<string name="action_about">"À propos"</string>
<string name="marked_as_read">"Marqué comme lu"</string>
<string name="marked_as_unread">"Marqué comme non lu"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"O contrasinal non é suficientemente longo"</string> <string name="error_invalid_password">"O contrasinal non é suficientemente longo"</string>
<string name="error_field_required">"Campo requirido"</string> <string name="error_field_required">"Campo requirido"</string>
<string name="prompt_url">"URL"</string> <string name="prompt_url">"URL"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"É preciso iniciar sesión?"</string> <string name="withLoginSwitch">"É preciso iniciar sesión?"</string>
<string name="login_url_problem">"Ups! Pode que precises engadir un \"/\" o final da URL."</string> <string name="login_url_problem">"Ups! Pode que precises engadir un \"/\" o final da URL."</string>
<string name="prompt_login">"Nome de usuario"</string> <string name="prompt_login">"Nome de usuario"</string>
@@ -123,5 +124,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Non hai nada aquí"</string>
<string name="tab_new">"Novo"</string>
<string name="tab_read">"Todos"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Acerca de"</string>
<string name="marked_as_read">"Elemento lido"</string>
<string name="marked_as_unread">"Elemento non lido"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"Kata sandinya tidak cukup panjang"</string> <string name="error_invalid_password">"Kata sandinya tidak cukup panjang"</string>
<string name="error_field_required">"Kolom wajib diisi"</string> <string name="error_field_required">"Kolom wajib diisi"</string>
<string name="prompt_url">"URL"</string> <string name="prompt_url">"URL"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Harus masuk?"</string> <string name="withLoginSwitch">"Harus masuk?"</string>
<string name="login_url_problem">"Ups. Anda mungkin harus menambahkan \"/\" di akhir url."</string> <string name="login_url_problem">"Ups. Anda mungkin harus menambahkan \"/\" di akhir url."</string>
<string name="prompt_login">"Nama pengguna"</string> <string name="prompt_login">"Nama pengguna"</string>
@@ -123,5 +124,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Tidak ada di sini"</string>
<string name="tab_new">"Baru"</string>
<string name="tab_read">"Semua"</string>
<string name="tab_favs">"Favorit"</string>
<string name="action_about">"Tentang"</string>
<string name="marked_as_read">"Membaca item"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"La password non è sufficientemente lunga"</string> <string name="error_invalid_password">"La password non è sufficientemente lunga"</string>
<string name="error_field_required">"Campo obbligatorio"</string> <string name="error_field_required">"Campo obbligatorio"</string>
<string name="prompt_url">"URL"</string> <string name="prompt_url">"URL"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"È richiesto l'accesso?"</string> <string name="withLoginSwitch">"È richiesto l'accesso?"</string>
<string name="login_url_problem">"Oops. Potrebbe essere necessario aggiungere un \"/\" alla fine dell'url."</string> <string name="login_url_problem">"Oops. Potrebbe essere necessario aggiungere un \"/\" alla fine dell'url."</string>
<string name="prompt_login">"Nome utente"</string> <string name="prompt_login">"Nome utente"</string>
@@ -123,5 +124,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Non c'è niente qui"</string>
<string name="tab_new">"Nuovi"</string>
<string name="tab_read">"Tutti"</string>
<string name="tab_favs">"Preferiti"</string>
<string name="action_about">"Informazioni"</string>
<string name="marked_as_read">"Articolo letto"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"패스워드가 짧습니다."</string> <string name="error_invalid_password">"패스워드가 짧습니다."</string>
<string name="error_field_required">"필수 항목"</string> <string name="error_field_required">"필수 항목"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"로그인이 필요합니까?"</string> <string name="withLoginSwitch">"로그인이 필요합니까?"</string>
<string name="login_url_problem">"죄송합니다. Url의 끝에 \"/\"를 추가할 필요가 있습니다."</string> <string name="login_url_problem">"죄송합니다. Url의 끝에 \"/\"를 추가할 필요가 있습니다."</string>
<string name="prompt_login">"사용자 이름"</string> <string name="prompt_login">"사용자 이름"</string>
@@ -123,5 +124,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"비어있음"</string>
<string name="tab_new">"새로운"</string>
<string name="tab_read">"전체"</string>
<string name="tab_favs">"즐겨찾기"</string>
<string name="action_about">"정보"</string>
<string name="marked_as_read">"항목 읽기"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"Wachtwoord niet lang genoeg"</string> <string name="error_invalid_password">"Wachtwoord niet lang genoeg"</string>
<string name="error_field_required">"Dit veld is verplicht"</string> <string name="error_field_required">"Dit veld is verplicht"</string>
<string name="prompt_url">"Selfoss server"</string> <string name="prompt_url">"Selfoss server"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Authenticatie vereist?"</string> <string name="withLoginSwitch">"Authenticatie vereist?"</string>
<string name="login_url_problem">"Oeps, ben je soms de \"/\" vergeten aan het eind?"</string> <string name="login_url_problem">"Oeps, ben je soms de \"/\" vergeten aan het eind?"</string>
<string name="prompt_login">"Gebruikersnaam"</string> <string name="prompt_login">"Gebruikersnaam"</string>
@@ -22,7 +23,6 @@
<string name="wrong_infos">"Controleer de gegevens nogmaals."</string> <string name="wrong_infos">"Controleer de gegevens nogmaals."</string>
<string name="all_posts_not_read">"Fout bij markeren als gelezen"</string> <string name="all_posts_not_read">"Fout bij markeren als gelezen"</string>
<string name="all_posts_read">"Alle artikelen gemarkeerd als gelezen"</string> <string name="all_posts_read">"Alle artikelen gemarkeerd als gelezen"</string>
<string name="undo_string">"Ongedaan maken"</string>
<string name="addStringNoUrl">"Login om bronnen toe te voegen"</string> <string name="addStringNoUrl">"Login om bronnen toe te voegen"</string>
<string name="cant_get_sources">"Kan de lijst met bronnen niet ophalen"</string> <string name="cant_get_sources">"Kan de lijst met bronnen niet ophalen"</string>
<string name="cant_create_source">"Kan bron niet creëeren"</string> <string name="cant_create_source">"Kan bron niet creëeren"</string>
@@ -123,5 +123,13 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Niets gevonden"</string>
<string name="tab_new">"Nieuw"</string>
<string name="tab_read">"Alle"</string>
<string name="tab_favs">"Favorieten"</string>
<string name="action_about">"Over"</string>
<string name="marked_as_read">"Artikel gelezen"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="undo_string">"Ongedaan maken"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"Senha muito pequena"</string> <string name="error_invalid_password">"Senha muito pequena"</string>
<string name="error_field_required">"Campo obrigatório"</string> <string name="error_field_required">"Campo obrigatório"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"É necessário o login ?"</string> <string name="withLoginSwitch">"É necessário o login ?"</string>
<string name="login_url_problem">"Oops. Talvez você precise adicionar uma \"/\" no final da url."</string> <string name="login_url_problem">"Oops. Talvez você precise adicionar uma \"/\" no final da url."</string>
<string name="prompt_login">"Usuário"</string> <string name="prompt_login">"Usuário"</string>
@@ -123,5 +124,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Nada aqui"</string>
<string name="tab_new">"Novo"</string>
<string name="tab_read">"Todos"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Sobre"</string>
<string name="marked_as_read">"Item lido"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"Senha não é longa o suficiente"</string> <string name="error_invalid_password">"Senha não é longa o suficiente"</string>
<string name="error_field_required">"Campo obrigatório"</string> <string name="error_field_required">"Campo obrigatório"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"É necessário fazer login?"</string> <string name="withLoginSwitch">"É necessário fazer login?"</string>
<string name="login_url_problem">"Uups. Você pode precisar adicionar uma \"/\" no final da url."</string> <string name="login_url_problem">"Uups. Você pode precisar adicionar uma \"/\" no final da url."</string>
<string name="prompt_login">"Nome do usuário"</string> <string name="prompt_login">"Nome do usuário"</string>
@@ -123,5 +124,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Nada aqui"</string>
<string name="tab_new">"Novo"</string>
<string name="tab_read">"Tudo"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Sobre"</string>
<string name="marked_as_read">"Item lido"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"Password not long enough"</string> <string name="error_invalid_password">"Password not long enough"</string>
<string name="error_field_required">"Field required"</string> <string name="error_field_required">"Field required"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Login required ?"</string> <string name="withLoginSwitch">"Login required ?"</string>
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string> <string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
<string name="prompt_login">"පරිශීලක නාමය"</string> <string name="prompt_login">"පරිශීලක නාමය"</string>
@@ -123,5 +124,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Nothing here"</string>
<string name="tab_new">"New"</string>
<string name="tab_read">"සියල්ල"</string>
<string name="tab_favs">"Favorites"</string>
<string name="action_about">"මේ ගැන"</string>
<string name="marked_as_read">"Item read"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"Parola yeterince uzun değil"</string> <string name="error_invalid_password">"Parola yeterince uzun değil"</string>
<string name="error_field_required">"Alan gereklidir"</string> <string name="error_field_required">"Alan gereklidir"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Kullanıcı Girişi Gerekli?"</string> <string name="withLoginSwitch">"Kullanıcı Girişi Gerekli?"</string>
<string name="login_url_problem">"Oops. Url'nin sonuna \"/\" eklemek gerekebilir."</string> <string name="login_url_problem">"Oops. Url'nin sonuna \"/\" eklemek gerekebilir."</string>
<string name="prompt_login">"Kullanıcı adı"</string> <string name="prompt_login">"Kullanıcı adı"</string>
@@ -123,5 +124,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Burada hiçbir şey yok"</string>
<string name="tab_new">"Yeni"</string>
<string name="tab_read">"Tüm"</string>
<string name="tab_favs">"Favoriler"</string>
<string name="action_about">"Hakkında"</string>
<string name="marked_as_read">"Öğeleri oku"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"密码不够长"</string> <string name="error_invalid_password">"密码不够长"</string>
<string name="error_field_required">"必填字段"</string> <string name="error_field_required">"必填字段"</string>
<string name="prompt_url">"网址"</string> <string name="prompt_url">"网址"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"需要登录?"</string> <string name="withLoginSwitch">"需要登录?"</string>
<string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string> <string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string>
<string name="prompt_login">"用户名"</string> <string name="prompt_login">"用户名"</string>
@@ -123,5 +124,12 @@
<string name="update_source">更新源</string> <string name="update_source">更新源</string>
<string name="confirm_disconnect_title">断开连接?</string> <string name="confirm_disconnect_title">断开连接?</string>
<string name="confirm_disconnect_description">您将断开与 selfoss 实例的连接。</string> <string name="confirm_disconnect_description">您将断开与 selfoss 实例的连接。</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"暂无内容!"</string>
<string name="tab_new">"新建"</string>
<string name="tab_read">"所有"</string>
<string name="tab_favs">"收藏夹"</string>
<string name="action_about">"关于我们"</string>
<string name="marked_as_read">"已读"</string>
<string name="marked_as_unread">"未读条目"</string>
</resources> </resources>
@@ -7,6 +7,7 @@
<string name="error_invalid_password">"密码不够长"</string> <string name="error_invalid_password">"密码不够长"</string>
<string name="error_field_required">"欄位必填"</string> <string name="error_field_required">"欄位必填"</string>
<string name="prompt_url">"网址"</string> <string name="prompt_url">"网址"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"需要登入?"</string> <string name="withLoginSwitch">"需要登入?"</string>
<string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string> <string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string>
<string name="prompt_login">"使用者名稱"</string> <string name="prompt_login">"使用者名稱"</string>
@@ -123,5 +124,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string> <string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"暂无内容!"</string>
<string name="tab_new">"新建"</string>
<string name="tab_read">"所有"</string>
<string name="tab_favs">"收藏夹"</string>
<string name="action_about">"关于我们"</string>
<string name="marked_as_read">"已读"</string>
<string name="marked_as_unread">"未讀項目"</string>
</resources> </resources>
@@ -126,4 +126,12 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Nothing here"</string>
<string name="tab_new">"New"</string>
<string name="tab_read">"All"</string>
<string name="tab_favs">"Favorites"</string>
<string name="action_about">"About"</string>
<string name="marked_as_read">"Item read"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>
-7
View File
@@ -1,10 +1,3 @@
buildscript {
dependencies {
// SqlDelight
classpath("com.squareup.sqldelight:gradle-plugin:1.5.5")
}
}
plugins { plugins {
//trick: for the same plugin versions in all sub-modules //trick: for the same plugin versions in all sub-modules
id("com.android.application").version("8.7.3").apply(false) id("com.android.application").version("8.7.3").apply(false)
+1
View File
@@ -3,3 +3,4 @@ files:
translation: /androidApp/src/main/res/values-%android_code%/%original_file_name% translation: /androidApp/src/main/res/values-%android_code%/%original_file_name%
translate_attributes: '0' translate_attributes: '0'
content_segmentation: '0' content_segmentation: '0'
preserve_hierarchy: true
@@ -0,0 +1,16 @@
**v124123651**
- Merge pull request 'Bugfixes' (#171) from bugfixes into master
- config: crowdin
- chore: can links be empty ?
- fix: Context issues in article fragment.
- fix: Context issues in fragment sheet.
- fix: build.
- chore: compile issue fix.
- chore: filter some bugs.
- bugfix: catch users using something other than selfoss.
- bugfix: No browser, no link.
- translations
- chore: remove log.
- translation
- Changelog for v124123641
Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

After

Width:  |  Height:  |  Size: 24 KiB

+10 -13
View File
@@ -1,18 +1,18 @@
val ktorVersion = "2.3.2" val ktorVersion = "3.0.3"
object SqlDelight { object SqlDelight {
const val runtime = "com.squareup.sqldelight:runtime:1.5.4" const val runtime = "app.cash.sqldelight:runtime:2.0.2"
const val android = "com.squareup.sqldelight:android-driver:1.5.4" const val android = "app.cash.sqldelight:android-driver:2.0.2"
const val native = "com.squareup.sqldelight:native-driver:1.5.4" const val native = "app.cash.sqldelight:native-driver:2.0.2"
} }
plugins { plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("com.android.library") id("com.android.library")
id("com.squareup.sqldelight")
kotlin("plugin.serialization") version "1.9.0" kotlin("plugin.serialization") version "1.9.0"
id("org.jetbrains.kotlinx.kover") id("org.jetbrains.kotlinx.kover")
id("app.cash.sqldelight") version "2.0.2"
} }
kotlin { kotlin {
@@ -37,7 +37,7 @@ kotlin {
implementation("io.ktor:ktor-client-logging:$ktorVersion") implementation("io.ktor:ktor-client-logging:$ktorVersion")
implementation("io.ktor:ktor-client-auth:$ktorVersion") implementation("io.ktor:ktor-client-auth:$ktorVersion")
implementation("io.ktor:ktor-client-cio:$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") implementation("org.jsoup:jsoup:1.15.4")
@@ -66,7 +66,6 @@ kotlin {
val androidMain by getting { val androidMain by getting {
dependencies { dependencies {
implementation("com.squareup.okhttp3:okhttp:4.11.0") 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") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
// Sql // Sql
@@ -86,7 +85,6 @@ kotlin {
dependencies { dependencies {
implementation(SqlDelight.native) implementation(SqlDelight.native)
implementation("io.ktor:ktor-client-ios:2.1.1")
} }
} }
val iosX64Test by getting val iosX64Test by getting
@@ -110,11 +108,10 @@ android {
namespace = "bou.amine.apps.readerforselfossv2" namespace = "bou.amine.apps.readerforselfossv2"
} }
sqldelight { sqldelight {
database("ReaderForSelfossDB") { databases {
packageName = "bou.amine.apps.readerforselfossv2.dao" create("ReaderForSelfossDB") {
sourceFolders = listOf("sqldelight") packageName.set("bou.amine.apps.readerforselfossv2.dao")
}
} }
} }
@@ -1,10 +1,15 @@
package bou.amine.apps.readerforselfossv2.dao package bou.amine.apps.readerforselfossv2.dao
import android.content.Context import android.content.Context
import com.squareup.sqldelight.android.AndroidSqliteDriver import app.cash.sqldelight.db.SqlDriver
import com.squareup.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.android.AndroidSqliteDriver
actual class DriverFactory(private val context: Context) { actual class DriverFactory(private val context: Context) {
actual fun createDriver(): SqlDriver { actual fun createDriver(): SqlDriver {
return AndroidSqliteDriver(ReaderForSelfossDB.Schema, context, "ReaderForSelfossV2-android.db") return AndroidSqliteDriver(
ReaderForSelfossDB.Schema,
context,
"ReaderForSelfossV2-android.db"
)
} }
} }
@@ -1,6 +1,6 @@
package bou.amine.apps.readerforselfossv2.dao package bou.amine.apps.readerforselfossv2.dao
import com.squareup.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlDriver
expect class DriverFactory { expect class DriverFactory {
fun createDriver(): SqlDriver fun createDriver(): SqlDriver
@@ -2,6 +2,7 @@ package bou.amine.apps.readerforselfossv2.model
import bou.amine.apps.readerforselfossv2.utils.DateUtils import bou.amine.apps.readerforselfossv2.utils.DateUtils
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveKind
@@ -10,7 +11,12 @@ import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.encodeCollection import kotlinx.serialization.encoding.encodeCollection
import kotlinx.serialization.json.* import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.booleanOrNull
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonPrimitive
class SelfossModel { class SelfossModel {
@Serializable @Serializable
@@ -134,6 +140,10 @@ class SelfossModel {
stringUrl = "http:$stringUrl" stringUrl = "http:$stringUrl"
} }
if (stringUrl.isEmptyOrNullOrNullString()) {
throw Exception("Link ${link} was translated to ${stringUrl}, but was empty. Handle this.")
}
return stringUrl return stringUrl
} }
@@ -176,7 +186,10 @@ class SelfossModel {
encoder: Encoder, encoder: Encoder,
value: List<String>, value: List<String>,
) { ) {
encoder.encodeCollection(PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING), value.size) { this.toString() } encoder.encodeCollection(
PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING),
value.size
) { this.toString() }
} }
} }
@@ -191,7 +204,10 @@ class SelfossModel {
} }
override val descriptor: SerialDescriptor override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor("BooleanOrIntForSomeSelfossVersions", PrimitiveKind.BOOLEAN) get() = PrimitiveSerialDescriptor(
"BooleanOrIntForSomeSelfossVersions",
PrimitiveKind.BOOLEAN
)
override fun serialize( override fun serialize(
encoder: Encoder, encoder: Encoder,
@@ -1,22 +1,26 @@
package bou.amine.apps.readerforselfossv2.rest 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.github.aakira.napier.Napier
import io.ktor.client.* import io.ktor.client.HttpClient
import io.ktor.client.plugins.cache.* import io.ktor.client.plugins.cache.HttpCache
import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.logging.* import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.request.* import io.ktor.client.plugins.logging.Logger
import io.ktor.serialization.kotlinx.json.* 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 import kotlinx.serialization.json.Json
class MercuryApi() { class MercuryApi {
var client = createHttpClient() var client = createHttpClient()
private fun createHttpClient(): HttpClient { private fun createHttpClient(): HttpClient {
return HttpClient { return HttpClient {
install(ContentNegotiation) {
install(HttpCache) install(HttpCache)
install(ContentNegotiation) {
json( json(
Json { Json {
prettyPrint = true prettyPrint = true
@@ -30,11 +30,15 @@ suspend fun maybeResponse(r: HttpResponse?): SuccessResponse {
} }
suspend inline fun <reified T> bodyOrFailure(r: HttpResponse?): StatusAndData<T> { suspend inline fun <reified T> bodyOrFailure(r: HttpResponse?): StatusAndData<T> {
try {
return if (r != null && r.status.isSuccess()) { return if (r != null && r.status.isSuccess()) {
StatusAndData.succes(r.body()) StatusAndData.succes(r.body())
} else { } else {
StatusAndData.error() StatusAndData.error()
} }
} catch (e: Throwable) {
return StatusAndData.error()
}
} }
inline fun tryToRequest( inline fun tryToRequest(
@@ -45,8 +45,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
setupInsecureHTTPEngine(this) setupInsecureHTTPEngine(this)
} }
} }
install(ContentNegotiation) {
install(HttpCache) install(HttpCache)
install(ContentNegotiation) {
json( json(
Json { Json {
prettyPrint = true prettyPrint = true
@@ -127,7 +127,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
client.tryToGet(url("/login")) { client.tryToGet(url("/login")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -148,7 +150,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
client.tryToPost(url("/login")) { client.tryToPost(url("/login")) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, 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 = suspend fun logout(): SuccessResponse =
if (shouldHaveNewLogout()) { if (shouldHaveNewLogout()) {
@@ -176,7 +181,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
private suspend fun maybeLogoutIfAvailable() = private suspend fun maybeLogoutIfAvailable() =
responseOrSuccessIf404( responseOrSuccessIf404(
client.tryToGet(url("/logout")) { client.tryToGet(url("/logout")) {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -195,7 +202,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
private suspend fun doLogout() = private suspend fun doLogout() =
maybeResponse( maybeResponse(
client.tryToDelete(url("/api/session/current")) { client.tryToDelete(url("/api/session/current")) {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -233,7 +242,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("updatedsince", updatedSince) parameter("updatedsince", updatedSince)
parameter("items", items ?: appSettingsService.getItemsNumber()) parameter("items", items ?: appSettingsService.getItemsNumber())
parameter("offset", offset) parameter("offset", offset)
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -258,7 +269,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
} }
parameter("type", "all") parameter("type", "all")
parameter("items", 1) parameter("items", 1)
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -281,7 +294,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
} }
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -304,7 +319,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
} }
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -327,7 +344,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
} }
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -350,7 +369,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
} }
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -373,7 +394,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
} }
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -396,7 +419,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
} }
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -415,7 +440,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
suspend fun apiInformation(): StatusAndData<SelfossModel.ApiInformation> = suspend fun apiInformation(): StatusAndData<SelfossModel.ApiInformation> =
bodyOrFailure( bodyOrFailure(
client.tryToGet(url("/api/about")) { client.tryToGet(url("/api/about")) {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -438,7 +465,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
} }
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -461,7 +490,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
} }
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -484,7 +515,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
} }
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -507,7 +540,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
} }
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -536,7 +571,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
ids.map { append("ids[]", it) } ids.map { append("ids[]", it) }
}, },
block = { block = {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -588,7 +625,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
append(tagsParamName, tags) append(tagsParamName, tags)
}, },
block = { block = {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -641,7 +680,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
append(tagsParamName, tags) append(tagsParamName, tags)
}, },
block = { block = {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -664,7 +705,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName()) parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword()) parameter("password", appSettingsService.getPassword())
} }
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) { if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers { headers {
append( append(
HttpHeaders.Authorization, HttpHeaders.Authorization,
@@ -1,3 +1,5 @@
import kotlin.Boolean;
CREATE TABLE `ACTION` ( CREATE TABLE `ACTION` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`articleid` TEXT NOT NULL, `articleid` TEXT NOT NULL,
@@ -1,3 +1,5 @@
import kotlin.Boolean;
CREATE TABLE ITEM ( CREATE TABLE ITEM (
`id` TEXT NOT NULL, `id` TEXT NOT NULL,
`datetime` TEXT NOT NULL, `datetime` TEXT NOT NULL,
@@ -1,7 +1,7 @@
package bou.amine.apps.readerforselfossv2.dao package bou.amine.apps.readerforselfossv2.dao
import com.squareup.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlDriver
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver import app.cash.sqldelight.driver.native.NativeSqliteDriver
actual class DriverFactory { actual class DriverFactory {
actual fun createDriver(): SqlDriver { actual fun createDriver(): SqlDriver {
@@ -2,10 +2,6 @@ package bou.amine.apps.readerforselfossv2.utils
actual class DateUtils { actual class DateUtils {
actual companion object { actual companion object {
actual fun parseDate(dateString: String): Long {
TODO("Not yet implemented")
}
actual fun parseRelativeDate(dateString: String): String { actual fun parseRelativeDate(dateString: String): String {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
@@ -1,7 +1,7 @@
package bou.amine.apps.readerforselfossv2.dao package bou.amine.apps.readerforselfossv2.dao
import com.squareup.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlDriver
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver import app.cash.sqldelight.driver.native.NativeSqliteDriver
actual class DriverFactory { actual class DriverFactory {
actual fun createDriver(): SqlDriver { actual fun createDriver(): SqlDriver {
@@ -2,10 +2,6 @@ package bou.amine.apps.readerforselfossv2.utils
actual class DateUtils { actual class DateUtils {
actual companion object { actual companion object {
actual fun parseDate(dateString: String): Long {
TODO("Not yet implemented")
}
actual fun parseRelativeDate(dateString: String): String { actual fun parseRelativeDate(dateString: String): String {
TODO("Not yet implemented") TODO("Not yet implemented")
} }