Compare commits

..

18 Commits

Author SHA1 Message Date
11c39ae87c Merge pull request 'Bump dependencies' (#173) from upgarde into master
All checks were successful
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
6645902ec8 chore: "faster" action.
All checks were successful
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
0a07a5dfad fastlane: icon change.
Some checks failed
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
d88d38fd3b chore: ignoring a pixel issue.
All checks were successful
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
28fe38aa17 test: fixed an ui test issue.
All checks were successful
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
d524c30732 fix: center the loading thing.
All checks were successful
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
8c00aa65da test: items displaying.
All checks were successful
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
ae81261cb1 bump: sqldelight.
All checks were successful
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
03c567ee33 bump: material, desugar jdk, jsoup, kodein, settings, napier, mock.
All checks were successful
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
d23dd82fc2 bump: androix and coroutines.
All checks were successful
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
2e7a168424 bump: ktor. Closes #67.
All checks were successful
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
5bc2f614af Changelog for v124123651
All checks were successful
Check master code / build (push) Successful in 12m31s
2024-12-30 22:54:15 +00:00
934c112db5 Merge pull request 'Bugfixes' (#171) from bugfixes into master
All checks were successful
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
ad7549a89f config: crowdin
All checks were successful
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
fb9ceecabd chore: can links be empty ?
All checks were successful
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
61b9fd30e0 fix: Context issues in article fragment. 2024-12-30 22:37:36 +01:00
806e56e20b fix: Context issues in fragment sheet.
All checks were successful
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
cd8b7aaf9d fix: build. 2024-12-30 22:12:21 +01:00
30 changed files with 367 additions and 262 deletions

View File

@ -3,7 +3,7 @@ on:
workflow_call:
jobs:
BuildAndTest:
BuildAndTestAndCoverage:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
@ -16,9 +16,30 @@ jobs:
with:
distribution: 'temurin'
java-version: '17'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
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: Build and test
run: ./gradlew build --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

View File

@ -85,6 +85,7 @@ jobs:
with:
distribution: 'temurin'
java-version: '17'
cache: gradle
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Configure gradle...

View File

@ -12,8 +12,9 @@ jobs:
uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin' # See 'Supported distributions' for available options
distribution: 'temurin'
java-version: '17'
cache: gradle
- 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/
- name: Install detekt
@ -23,4 +24,5 @@ jobs:
- name: Detecting...
run: ./detekt-cli-1.23.1/bin/detekt-cli --all-rules --excludes '**/shared/build/**/*.kt' || true
build:
needs: Lint
uses: ./.gitea/workflows/common_build.yml

View File

@ -1,46 +0,0 @@
name: Check master code
on:
push:
branches:
- master
pull_request:
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

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
- Chore: no tests on build.

View File

@ -1,7 +1,7 @@
import java.io.ByteArrayOutputStream
val ignoreGitVersion: String by project
val acraVersion = "5.9.7"
val acraVersion = "5.12.0"
plugins {
id("com.android.application")
@ -9,6 +9,7 @@ plugins {
kotlin("kapt")
id("com.mikepenz.aboutlibraries.plugin")
id("org.jetbrains.kotlinx.kover")
id("app.cash.sqldelight") version "2.0.2"
}
fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
@ -65,14 +66,14 @@ android {
kotlinOptions {
jvmTarget = "17"
}
compileSdk = 34
compileSdk = 35
buildFeatures {
viewBinding = true
}
defaultConfig {
applicationId = "bou.amine.apps.readerforselfossv2.android"
minSdk = 25
targetSdk = 34
targetSdk = 34 // 35 when edge-to-edge is handled
versionCode = versionCodeFromGit()
versionName = versionNameFromGit()
@ -119,28 +120,26 @@ android {
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
implementation(project(":shared"))
implementation("com.google.android.material:material:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.1")
implementation("androidx.preference:preference-ktx:1.2.1")
implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs")))
// Android Support
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.9.0")
implementation("androidx.recyclerview:recyclerview:1.3.1")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.recyclerview:recyclerview:1.4.0-rc01")
implementation("androidx.legacy:legacy-support-v4:1.0.0")
implementation("androidx.vectordrawable:vectordrawable:1.2.0-beta01")
implementation("androidx.vectordrawable:vectordrawable:1.2.0")
implementation("androidx.cardview:cardview:1.0.0")
implementation("androidx.annotation:annotation:1.7.0")
implementation("androidx.work:work-runtime-ktx:2.8.1")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("org.jsoup:jsoup:1.15.4")
implementation("androidx.annotation:annotation:1.9.1")
implementation("androidx.work:work-runtime-ktx:2.10.0")
implementation("androidx.constraintlayout:constraintlayout:2.2.0")
implementation("org.jsoup:jsoup:1.18.3")
//multidex
implementation("androidx.multidex:multidex:2.0.1")
@ -153,31 +152,31 @@ dependencies {
implementation("com.ashokvarma.android:bottom-navigation-bar:2.2.0")
// glide
kapt("com.github.bumptech.glide:compiler:4.15.0")
implementation("com.github.bumptech.glide:okhttp3-integration:4.15.0")
kapt("com.github.bumptech.glide:compiler:4.16.0")
implementation("com.github.bumptech.glide:okhttp3-integration:4.16.0")
// Themes
implementation("com.github.rubensousa:floatingtoolbar:1.5.1")
// Pager
implementation("me.relex:circleindicator:2.1.6")
implementation("androidx.viewpager2:viewpager2:1.1.0-beta02")
implementation("androidx.viewpager2:viewpager2:1.1.0")
//Dependency Injection
implementation("org.kodein.di:kodein-di:7.14.0")
implementation("org.kodein.di:kodein-di-framework-android-x:7.14.0")
implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.14.0")
implementation("org.kodein.di:kodein-di:7.23.1")
implementation("org.kodein.di:kodein-di-framework-android-x:7.23.1")
implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.23.1")
//Settings
implementation("com.russhwolf:multiplatform-settings-no-arg:0.9")
implementation("com.russhwolf:multiplatform-settings-no-arg:1.3.0")
//Logging
implementation("io.github.aakira:napier:2.6.1")
implementation("io.github.aakira:napier:2.7.1")
//PhotoView
implementation("com.github.chrisbanes:PhotoView:2.3.0")
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.core:core-ktx:1.15.0")
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
@ -185,13 +184,13 @@ dependencies {
implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0")
// SQLDELIGHT
implementation("com.squareup.sqldelight:android-driver:1.5.4")
implementation("app.cash.sqldelight:android-driver:2.0.2")
//test
testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk:1.12.0")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
testImplementation("io.mockk:mockk:1.13.14")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
androidTestImplementation("androidx.test:runner:1.6.2")
androidTestImplementation("androidx.test:rules:1.6.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")

View File

@ -2,6 +2,7 @@ package bou.amine.apps.readerforselfossv2.android
import android.content.Context
import androidx.annotation.ArrayRes
import androidx.test.espresso.Espresso.onData
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.replaceText
@ -14,6 +15,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.Matchers.hasToString
fun performLogin(someUrl: String? = null) {
onView(withId(R.id.urlView)).perform(click()).perform(
@ -22,7 +24,6 @@ fun performLogin(someUrl: String? = null) {
)
)
onView(withId(R.id.signInButton)).perform(click())
Thread.sleep(10000)
}
fun loginAndInitHome() {
@ -87,4 +88,22 @@ fun testPreferencesFromArray(
openSettingItem()
onView(withText(res)).check(matches(allOf(isDisplayed(), isChecked())))
}
}
fun testAddSourceWithUrl(url: String, sourceName: String) {
onView(withId(R.id.fab))
.perform(click())
onView(withId(R.id.nameInput))
.perform(click()).perform(typeTextIntoFocusedView(sourceName))
onView(withId(R.id.sourceUri))
.perform(click())
.perform(typeTextIntoFocusedView(url))
onView(withId(R.id.tags))
.perform(click()).perform(typeTextIntoFocusedView("tag1,tag2,tag3"))
onView(withId(R.id.spoutsSpinner))
.perform(click())
onData(hasToString("RSS Feed")).perform(click())
onView(withId(R.id.saveBtn))
.perform(click())
onView(withText(sourceName)).check(matches(isDisplayed()))
}

View File

@ -1,9 +1,6 @@
package bou.amine.apps.readerforselfossv2.android
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
@ -47,9 +44,7 @@ class HomeActivityTest {
isClickable()
)
)
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>()
)
openMenu()
onView(withText(R.string.readAll)).check(matches(isDisplayed()))
onView(withText(R.string.menu_home_sources)).check(matches(isDisplayed()))
onView(withText(R.string.title_activity_settings)).check(matches(isDisplayed()))
@ -78,43 +73,31 @@ class HomeActivityTest {
).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>()
)
openMenu()
onView(withText(R.string.readAll)).perform(click())
onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>()
)
openMenu()
onView(withText(R.string.menu_home_sources)).perform(click())
onView(withId(R.id.fab)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>()
)
openMenu()
onView(withText(R.string.title_activity_settings)).perform(click())
onView(withText(R.string.pref_header_general)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>()
)
openMenu()
onView(withText(R.string.menu_home_refresh)).perform(click())
onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>()
)
openMenu()
/*onView(withText(R.string.issue_tracker_link)).perform(click())
onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>()
)*/
openMenu()*/
onView(withText(R.string.action_disconnect)).perform(click())
onView(withText(R.string.confirm_disconnect_title)).check(matches(isDisplayed()))

View File

@ -6,6 +6,7 @@ import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.replaceText
import androidx.test.espresso.action.ViewActions.swipeUp
import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isChecked
@ -102,6 +103,7 @@ class SettingsActivityGeneralTest {
)
)
)
onView(withId(R.id.settings)).perform(swipeUp())
onView(withSettingsCheckboxWidget(R.string.display_all_counts_title)).check(
matches(
allOf(

View File

@ -1,9 +1,7 @@
package bou.amine.apps.readerforselfossv2.android
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
@ -33,9 +31,7 @@ class SettingsActivityTest {
context = activity.window.context
}
loginAndInitHome()
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>()
)
openMenu()
onView(withText(R.string.title_activity_settings)).perform(click())
}

View File

@ -1,19 +1,20 @@
package bou.amine.apps.readerforselfossv2.android
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.AmbiguousViewMatcherException
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.scrollCompletelyTo
import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView
import androidx.test.espresso.action.ViewActions.swipeDown
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -23,7 +24,6 @@ import java.util.UUID
@RunWith(AndroidJUnit4::class)
@LargeTest
class SourcesActivityTest {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
@ -34,32 +34,50 @@ class SourcesActivityTest {
sourceName = UUID.randomUUID().toString().substring(0, 15)
loginAndInitHome()
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>()
)
onView(withText(R.string.menu_home_sources))
.perform(click())
goToSources()
}
@Test
fun addSource() {
onView(withId(R.id.fab))
.perform(click())
onView(withId(R.id.nameInput))
.perform(click()).perform(typeTextIntoFocusedView(sourceName))
onView(withId(R.id.sourceUri))
.perform(click())
.perform(typeTextIntoFocusedView("https://lorem-rss.herokuapp.com/feed?unit=year&interval=1&length=10"))
onView(withId(R.id.tags))
.perform(click()).perform(typeTextIntoFocusedView("tag1,tag2,tag3"))
onView(withId(R.id.spoutsSpinner))
.perform(click())
onView(withText("RSS Feed"))
.perform(scrollCompletelyTo())
.perform(click())
onView(withId(R.id.saveBtn))
.perform(click())
onView(withText(sourceName)).check(matches(isDisplayed()))
testAddSourceWithUrl(
"https://lorem-rss.herokuapp.com/feed?unit=year&interval=1&length=10",
sourceName
)
}
@Test
fun addSourceCheckContent() {
testAddSourceWithUrl("https://news.google.com/rss?hl=en-US&gl=US&ceid=US:en", sourceName)
onView(isRoot()).perform(ViewActions.pressBack())
openMenu()
onView(withText(R.string.menu_home_refresh)).perform(click())
onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed()))
onView(
withId(android.R.id.button1)
).perform(click())
Thread.sleep(10000)
onView(withId(R.id.swipeRefreshLayout)).perform(swipeDown())
Thread.sleep(2000)
try {
onView(withId(R.id.sourceTitleAndDate)).check(matches(isDisplayed()))
} catch (e: AmbiguousViewMatcherException) {
assert(true)
}
goToSources()
}
@After
fun deleteTheCreatedSource() {
onView(withText(sourceName)).check(matches(isDisplayed()))
onView(withId(R.id.deleteBtn)).perform(click())
onView(withText(sourceName)).check(doesNotExist())
}
private fun goToSources() {
openMenu()
onView(withText(R.string.menu_home_sources))
.perform(click())
}
}

View File

@ -1,5 +1,6 @@
package bou.amine.apps.readerforselfossv2.android
import android.content.Context
import android.view.View
import android.widget.EditText
import android.widget.ImageView
@ -7,6 +8,8 @@ import android.widget.RelativeLayout
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.graphics.drawable.toBitmap
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.Root
import androidx.test.espresso.matcher.RootMatchers.isPlatformPopup
import androidx.test.espresso.matcher.ViewMatchers.hasSibling
@ -98,4 +101,10 @@ fun withSettingsCheckboxFrame(@StringRes id: Int): Matcher<View>? {
)
)
)
}
fun openMenu() {
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>()
)
}

View File

@ -52,6 +52,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.acra.ktx.sendSilentlyWithAcra
import org.kodein.di.DI
import org.kodein.di.DIAware
import org.kodein.di.android.x.closestDI
@ -103,7 +104,12 @@ class ArticleFragment : Fragment(), DIAware {
try {
binding = FragmentArticleBinding.inflate(inflater, container, false)
url = item.getLinkDecoded()
try {
url = item.getLinkDecoded()
} catch (e: Exception) {
e.sendSilentlyWithAcra()
}
contentText = item.content
contentTitle = item.title.getHtmlDecoded()
contentImage = item.getThumbnail(repository.baseUrl)
@ -157,7 +163,7 @@ class ArticleFragment : Fragment(), DIAware {
)
} catch (e: InflateException) {
e.sendSilentlyWithAcraWithName("webview not available")
if (context != null) {
try {
AlertDialog.Builder(requireContext())
.setMessage(requireContext().getString(R.string.webview_dialog_issue_message))
.setTitle(requireContext().getString(R.string.webview_dialog_issue_title))
@ -169,6 +175,8 @@ class ArticleFragment : Fragment(), DIAware {
}
.create()
.show()
} catch (e: IllegalStateException) {
e.sendSilentlyWithAcraWithName("Context required is null")
}
}
@ -218,14 +226,14 @@ class ArticleFragment : Fragment(), DIAware {
R.id.share_action -> requireActivity().shareLink(url, contentTitle)
R.id.open_action -> requireActivity().openItemUrlInBrowserAsNewTask(this@ArticleFragment.item)
R.id.unread_action ->
if (context != null) {
try {
if (this@ArticleFragment.item.unread) {
CoroutineScope(Dispatchers.IO).launch {
repository.markAsRead(this@ArticleFragment.item)
}
this@ArticleFragment.item.unread = false
Toast.makeText(
context,
requireContext(),
R.string.marked_as_read,
Toast.LENGTH_LONG,
).show()
@ -240,6 +248,8 @@ class ArticleFragment : Fragment(), DIAware {
Toast.LENGTH_LONG,
).show()
}
} catch (e: IllegalStateException) {
e.sendSilentlyWithAcraWithName("Context required is null")
}
else -> Unit
@ -389,7 +399,7 @@ class ArticleFragment : Fragment(), DIAware {
}
private fun htmlToWebview() {
if (context != null) {
try {
val attrs: IntArray = intArrayOf(android.R.attr.fontFamily)
val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs)
@ -534,6 +544,8 @@ class ArticleFragment : Fragment(), DIAware {
"utf-8",
null,
)
} catch (e: IllegalStateException) {
e.sendSilentlyWithAcraWithName("Context required is null")
}
}
@ -549,10 +561,10 @@ class ArticleFragment : Fragment(), DIAware {
private fun openInBrowserAfterFailing() {
binding.progressBar.visibility = View.GONE
if (context != null) {
try {
requireContext().openItemUrlInBrowserAsNewTask(this@ArticleFragment.item)
} else {
Exception("openInBrowserAfterFailing context is null").sendSilentlyWithAcraWithName("openInBrowserAfterFailing > $context")
} catch (e: IllegalStateException) {
e.sendSilentlyWithAcraWithName("Context required is null")
}
}

View File

@ -52,19 +52,17 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
false,
)
val context: Context? = context
if (context == null) {
dismiss()
Exception("FilterSheetFragment context is null").sendSilentlyWithAcraWithName("FilterSheetFragment > onCreateView")
} else {
try {
CoroutineScope(Dispatchers.Main).launch {
handleTagChips(context)
handleSourceChips(context)
handleTagChips(requireContext())
handleSourceChips(requireContext())
binding.progressBar2.visibility = GONE
binding.filterView.visibility = VISIBLE
}
} catch (e: IllegalStateException) {
dismiss()
e.sendSilentlyWithAcraWithName("FilterSheetFragment > onCreateView")
}
binding.floatingActionButton2.setOnClickListener {

View File

@ -16,7 +16,7 @@ class AcraReportingAdministrator : ReportingAdministrator {
config: CoreConfiguration,
reportBuilder: ReportBuilder
): Boolean {
return reportBuilder.exception !is DeadSystemException
return reportBuilder.exception !is DeadSystemException && (reportBuilder.exception != null && reportBuilder.exception!!::class.simpleName != "CannotDeliverBroadcastException")
}
override fun shouldSendReport(

View File

@ -23,6 +23,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/activity_horizontal_margin">
<!-- Login progress -->
@ -37,7 +38,7 @@
<LinearLayout
android:id="@+id/loginForm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
@ -100,4 +101,4 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1,10 +1,3 @@
buildscript {
dependencies {
// SqlDelight
classpath("com.squareup.sqldelight:gradle-plugin:1.5.5")
}
}
plugins {
//trick: for the same plugin versions in all sub-modules
id("com.android.application").version("8.7.3").apply(false)

View File

@ -3,11 +3,4 @@ files:
translation: /androidApp/src/main/res/values-%android_code%/%original_file_name%
translate_attributes: '0'
content_segmentation: '0'
translation_replace": {
"/master/app": "/androidApp",
"/master/androidApp": "/androidApp"
}
"ignore": [
"/gl/**"
]
preserve_hierarchy: true

View File

@ -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

View File

@ -1,18 +1,18 @@
val ktorVersion = "2.3.2"
val ktorVersion = "3.0.3"
object SqlDelight {
const val runtime = "com.squareup.sqldelight:runtime:1.5.4"
const val android = "com.squareup.sqldelight:android-driver:1.5.4"
const val native = "com.squareup.sqldelight:native-driver:1.5.4"
const val runtime = "app.cash.sqldelight:runtime:2.0.2"
const val android = "app.cash.sqldelight:android-driver:2.0.2"
const val native = "app.cash.sqldelight:native-driver:2.0.2"
}
plugins {
kotlin("multiplatform")
id("com.android.library")
id("com.squareup.sqldelight")
kotlin("plugin.serialization") version "1.9.0"
id("org.jetbrains.kotlinx.kover")
id("app.cash.sqldelight") version "2.0.2"
}
kotlin {
@ -37,7 +37,7 @@ kotlin {
implementation("io.ktor:ktor-client-logging:$ktorVersion")
implementation("io.ktor:ktor-client-auth:$ktorVersion")
implementation("io.ktor:ktor-client-cio:$ktorVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
implementation("org.jsoup:jsoup:1.15.4")
@ -66,7 +66,6 @@ kotlin {
val androidMain by getting {
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.11.0")
implementation("io.ktor:ktor-client-okhttp:2.2.4")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
// Sql
@ -86,7 +85,6 @@ kotlin {
dependencies {
implementation(SqlDelight.native)
implementation("io.ktor:ktor-client-ios:2.1.1")
}
}
val iosX64Test by getting
@ -110,11 +108,10 @@ android {
namespace = "bou.amine.apps.readerforselfossv2"
}
sqldelight {
database("ReaderForSelfossDB") {
packageName = "bou.amine.apps.readerforselfossv2.dao"
sourceFolders = listOf("sqldelight")
databases {
create("ReaderForSelfossDB") {
packageName.set("bou.amine.apps.readerforselfossv2.dao")
}
}
}

View File

@ -1,10 +1,15 @@
package bou.amine.apps.readerforselfossv2.dao
import android.content.Context
import com.squareup.sqldelight.android.AndroidSqliteDriver
import com.squareup.sqldelight.db.SqlDriver
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
actual class DriverFactory(private val context: Context) {
actual fun createDriver(): SqlDriver {
return AndroidSqliteDriver(ReaderForSelfossDB.Schema, context, "ReaderForSelfossV2-android.db")
return AndroidSqliteDriver(
ReaderForSelfossDB.Schema,
context,
"ReaderForSelfossV2-android.db"
)
}
}
}

View File

@ -1,7 +1,7 @@
package bou.amine.apps.readerforselfossv2.dao
import com.squareup.sqldelight.db.SqlDriver
import app.cash.sqldelight.db.SqlDriver
expect class DriverFactory {
fun createDriver(): SqlDriver
}
}

View File

@ -2,6 +2,7 @@ package bou.amine.apps.readerforselfossv2.model
import bou.amine.apps.readerforselfossv2.utils.DateUtils
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.PrimitiveKind
@ -10,7 +11,12 @@ import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
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 {
@Serializable
@ -134,6 +140,10 @@ class SelfossModel {
stringUrl = "http:$stringUrl"
}
if (stringUrl.isEmptyOrNullOrNullString()) {
throw Exception("Link ${link} was translated to ${stringUrl}, but was empty. Handle this.")
}
return stringUrl
}
@ -176,7 +186,10 @@ class SelfossModel {
encoder: Encoder,
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
get() = PrimitiveSerialDescriptor("BooleanOrIntForSomeSelfossVersions", PrimitiveKind.BOOLEAN)
get() = PrimitiveSerialDescriptor(
"BooleanOrIntForSomeSelfossVersions",
PrimitiveKind.BOOLEAN
)
override fun serialize(
encoder: Encoder,
@ -200,4 +216,4 @@ class SelfossModel {
TODO("Not yet implemented")
}
}
}
}

View File

@ -1,22 +1,26 @@
package bou.amine.apps.readerforselfossv2.rest
import bou.amine.apps.readerforselfossv2.model.*
import bou.amine.apps.readerforselfossv2.model.MercuryModel
import bou.amine.apps.readerforselfossv2.model.StatusAndData
import io.github.aakira.napier.Napier
import io.ktor.client.*
import io.ktor.client.plugins.cache.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.logging.*
import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.client.HttpClient
import io.ktor.client.plugins.cache.HttpCache
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
class MercuryApi() {
class MercuryApi {
var client = createHttpClient()
private fun createHttpClient(): HttpClient {
return HttpClient {
install(HttpCache)
install(ContentNegotiation) {
install(HttpCache)
json(
Json {
prettyPrint = true
@ -44,4 +48,4 @@ class MercuryApi() {
parameter("link", url)
},
)
}
}

View File

@ -45,8 +45,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
setupInsecureHTTPEngine(this)
}
}
install(HttpCache)
install(ContentNegotiation) {
install(HttpCache)
json(
Json {
prettyPrint = true
@ -105,8 +105,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
private fun hasLoginInfo() =
appSettingsService.getUserName().isNotEmpty() &&
appSettingsService.getPassword()
.isNotEmpty()
appSettingsService.getPassword()
.isNotEmpty()
suspend fun login(): SuccessResponse =
if (appSettingsService.getUserName().isNotEmpty() &&
@ -127,7 +127,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
client.tryToGet(url("/login")) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -148,7 +150,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
client.tryToPost(url("/login")) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -164,7 +168,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
},
)
private fun shouldHaveNewLogout() = appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0
private fun shouldHaveNewLogout() =
appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0
suspend fun logout(): SuccessResponse =
if (shouldHaveNewLogout()) {
@ -176,7 +181,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
private suspend fun maybeLogoutIfAvailable() =
responseOrSuccessIf404(
client.tryToGet(url("/logout")) {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -195,7 +202,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
private suspend fun doLogout() =
maybeResponse(
client.tryToDelete(url("/api/session/current")) {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -233,7 +242,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("updatedsince", updatedSince)
parameter("items", items ?: appSettingsService.getItemsNumber())
parameter("offset", offset)
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -258,7 +269,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
}
parameter("type", "all")
parameter("items", 1)
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -281,7 +294,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
}
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -304,7 +319,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
}
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -327,7 +344,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
}
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -350,7 +369,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
}
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -373,7 +394,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
}
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -396,7 +419,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
}
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -415,7 +440,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
suspend fun apiInformation(): StatusAndData<SelfossModel.ApiInformation> =
bodyOrFailure(
client.tryToGet(url("/api/about")) {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -438,7 +465,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
}
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -461,7 +490,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
}
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -484,7 +515,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
}
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -507,7 +540,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
}
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -528,15 +563,17 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
client.tryToSubmitForm(
url = url("/mark"),
formParameters =
Parameters.build {
if (!shouldHavePostLogin()) {
append("username", appSettingsService.getUserName())
append("password", appSettingsService.getPassword())
}
ids.map { append("ids[]", it) }
},
Parameters.build {
if (!shouldHavePostLogin()) {
append("username", appSettingsService.getUserName())
append("password", appSettingsService.getPassword())
}
ids.map { append("ids[]", it) }
},
block = {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -577,18 +614,20 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
client.tryToSubmitForm(
url = url("/source"),
formParameters =
Parameters.build {
if (!shouldHavePostLogin()) {
append("username", appSettingsService.getUserName())
append("password", appSettingsService.getPassword())
}
append("title", title)
append("url", url)
append("spout", spout)
append(tagsParamName, tags)
},
Parameters.build {
if (!shouldHavePostLogin()) {
append("username", appSettingsService.getUserName())
append("password", appSettingsService.getPassword())
}
append("title", title)
append("url", url)
append("spout", spout)
append(tagsParamName, tags)
},
block = {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -630,18 +669,20 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
client.tryToSubmitForm(
url = url("/source/$id"),
formParameters =
Parameters.build {
if (!shouldHavePostLogin()) {
append("username", appSettingsService.getUserName())
append("password", appSettingsService.getPassword())
}
append("title", title)
append("url", url)
append("spout", spout)
append(tagsParamName, tags)
},
Parameters.build {
if (!shouldHavePostLogin()) {
append("username", appSettingsService.getUserName())
append("password", appSettingsService.getPassword())
}
append("title", title)
append("url", url)
append("spout", spout)
append(tagsParamName, tags)
},
block = {
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -664,7 +705,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
parameter("username", appSettingsService.getUserName())
parameter("password", appSettingsService.getPassword())
}
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
if (appSettingsService.getBasicUserName()
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
) {
headers {
append(
HttpHeaders.Authorization,
@ -679,4 +722,4 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
}
},
)
}
}

View File

@ -1,3 +1,5 @@
import kotlin.Boolean;
CREATE TABLE `ACTION` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`articleid` TEXT NOT NULL,

View File

@ -1,3 +1,5 @@
import kotlin.Boolean;
CREATE TABLE ITEM (
`id` TEXT NOT NULL,
`datetime` TEXT NOT NULL,

View File

@ -1,10 +1,10 @@
package bou.amine.apps.readerforselfossv2.dao
import com.squareup.sqldelight.db.SqlDriver
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.native.NativeSqliteDriver
actual class DriverFactory {
actual fun createDriver(): SqlDriver {
return NativeSqliteDriver(ReaderForSelfossDB.Schema, "ReaderForSelfossV2-IOS.db")
}
}
}

View File

@ -1,10 +1,10 @@
package bou.amine.apps.readerforselfossv2.dao
import com.squareup.sqldelight.db.SqlDriver
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
import app.cash.sqldelight.db.SqlDriver
import app.cash.sqldelight.driver.native.NativeSqliteDriver
actual class DriverFactory {
actual fun createDriver(): SqlDriver {
return NativeSqliteDriver(ReaderForSelfossDB.Schema, "ReaderForSelfossV2-IOS.db")
}
}
}