Compare commits
	
		
			29 Commits
		
	
	
		
			v123061651
			...
			29d33687b4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					29d33687b4 | ||
| 
						 | 
					33cca86383 | ||
| 
						 | 
					86a30f647d | ||
| 78d87ad52a | |||
| 
						 | 
					c458871569 | ||
| 056825aa0c | |||
| 16b19fc5ce | |||
| 2b82be61d0 | |||
| 4ad4a23ed8 | |||
| d8c215eacc | |||
| 2b446ab22b | |||
| 20aab0ea62 | |||
| dcb9b1cb1f | |||
| 776d5d36f6 | |||
| 990a354229 | |||
| bd6b96d09d | |||
| d26f3979cd | |||
| b59b1abc6d | |||
| baca7cffed | |||
| a029d8a7dc | |||
| 4482234e1a | |||
| b5de30f561 | |||
| 70ad5f322c | |||
| d167092c83 | |||
| c4f4bafe85 | |||
| ed06b22a77 | |||
| 
						 | 
					172362b533 | ||
| 
						 | 
					ad72cb6f56 | ||
| 
						 | 
					9057ee0052 | 
							
								
								
									
										49
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								.drone.yml
									
									
									
									
									
								
							@@ -3,35 +3,38 @@ type: docker
 | 
			
		||||
name: test
 | 
			
		||||
 | 
			
		||||
steps:
 | 
			
		||||
  - name: Lint
 | 
			
		||||
    failure: ignore
 | 
			
		||||
    image: mingc/android-build-box:latest
 | 
			
		||||
    commands:
 | 
			
		||||
      - echo "---------------------------------------------------------"
 | 
			
		||||
      - echo "Install linters..."
 | 
			
		||||
      - curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.0.0/ktlint && chmod a+x ktlint && mv ktlint /usr/local/bin/
 | 
			
		||||
      - curl -sSLO https://github.com/detekt/detekt/releases/download/v1.23.1/detekt-cli-1.23.1.zip && unzip detekt-cli-1.23.1.zip
 | 
			
		||||
      - echo "---------------------------------------------------------"
 | 
			
		||||
      - echo "Linting..."
 | 
			
		||||
      - ktlint || true
 | 
			
		||||
      - echo "---------------------------------------------------------"
 | 
			
		||||
      - echo "Detecting..."
 | 
			
		||||
      - ./detekt-cli-1.23.1/bin/detekt-cli --all-rules || true
 | 
			
		||||
      - echo "---------------------------------------------------------"
 | 
			
		||||
    command_timeout: 1m
 | 
			
		||||
  - name: BuildAndTest
 | 
			
		||||
    image: mingc/android-build-box:latest
 | 
			
		||||
    commands:
 | 
			
		||||
      - echo "---------------------------------------------------------"
 | 
			
		||||
      - echo "Configure gradle..."
 | 
			
		||||
      - mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\npushCache=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
 | 
			
		||||
      - mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
 | 
			
		||||
      - echo "---------------------------------------------------------"
 | 
			
		||||
      - echo "Building..."
 | 
			
		||||
      - ./gradlew build -x test
 | 
			
		||||
      - echo "Configure java..."
 | 
			
		||||
      - . ~/.bash_profile
 | 
			
		||||
      - jenv global 17.0
 | 
			
		||||
      - java --version
 | 
			
		||||
      - date
 | 
			
		||||
      - echo "---------------------------------------------------------"
 | 
			
		||||
      - echo "Testing..."
 | 
			
		||||
      - echo "Building and testing..."
 | 
			
		||||
      - ./gradlew build
 | 
			
		||||
      - echo "---------------------------------------------------------"
 | 
			
		||||
      - ./gradlew koverMergedXmlReport
 | 
			
		||||
    environment:
 | 
			
		||||
      TZ: Europe/Paris
 | 
			
		||||
      SONAR_HOST_URL:
 | 
			
		||||
        from_secret: sonarScannerHostUrl
 | 
			
		||||
      SONAR_LOGIN:
 | 
			
		||||
        from_secret: sonarScannerLogin
 | 
			
		||||
  - name: Analyse
 | 
			
		||||
    image: kytay/sonar-node-plugin
 | 
			
		||||
    settings:
 | 
			
		||||
      sonar_host:
 | 
			
		||||
        from_secret: sonarScannerHostUrl
 | 
			
		||||
      sonar_token:
 | 
			
		||||
        from_secret: sonarScannerLogin
 | 
			
		||||
      use_node_version: 16.18.1
 | 
			
		||||
      sonar_debug: true
 | 
			
		||||
      sonar_project_settings: ./sonar-project.properties
 | 
			
		||||
trigger:
 | 
			
		||||
  event:
 | 
			
		||||
    - push
 | 
			
		||||
@@ -107,10 +110,10 @@ steps:
 | 
			
		||||
      - git fetch --tags
 | 
			
		||||
      - echo "---------------------------------------------------------"
 | 
			
		||||
      - echo "Configure gradle..."
 | 
			
		||||
      - mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=false\npushCache=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
 | 
			
		||||
      - mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
 | 
			
		||||
      - echo "---------------------------------------------------------"
 | 
			
		||||
      - echo "Generate APK"
 | 
			
		||||
      - ./gradlew :androidApp:assembleGithubConfigRelease  -P pushCache=false
 | 
			
		||||
      - ./gradlew :androidApp:assembleGithubConfigRelease
 | 
			
		||||
      - echo "---------------------------------------------------------"
 | 
			
		||||
      - echo "Get Key"
 | 
			
		||||
      - wget https://amine-louveau.fr/key
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,3 +1,20 @@
 | 
			
		||||
**v123061811**
 | 
			
		||||
 | 
			
		||||
- feat: Added confirmation dialog for disconnect item menu.
 | 
			
		||||
- Changelog for v123061651 [CI SKIP]
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
**v123061651**
 | 
			
		||||
 | 
			
		||||
- i18n: Translation update.
 | 
			
		||||
- i18n: Translation update.
 | 
			
		||||
- i18n: Translation update.
 | 
			
		||||
- fix: avoid trying to open invalid image urls.
 | 
			
		||||
- Changelog for v123051471 [CI SKIP]
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
**v123051471**
 | 
			
		||||
 | 
			
		||||
- fix: images could be null.
 | 
			
		||||
 
 | 
			
		||||
@@ -8,15 +8,15 @@ plugins {
 | 
			
		||||
    kotlin("android")
 | 
			
		||||
    kotlin("kapt")
 | 
			
		||||
    id("com.mikepenz.aboutlibraries.plugin")
 | 
			
		||||
    id("org.jetbrains.kotlinx.kover") version "0.6.1"
 | 
			
		||||
    id("org.jetbrains.kotlinx.kover")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
 | 
			
		||||
    var result: String = ByteArrayOutputStream().use { outputStream ->
 | 
			
		||||
    val result: String = ByteArrayOutputStream().use { outputStream ->
 | 
			
		||||
        project.exec {
 | 
			
		||||
            commandLine = cmd.split(" ")
 | 
			
		||||
            standardOutput = outputStream
 | 
			
		||||
            isIgnoreExitValue = ignore ?: false
 | 
			
		||||
            isIgnoreExitValue = ignore
 | 
			
		||||
        }
 | 
			
		||||
        outputStream.toString()
 | 
			
		||||
    }
 | 
			
		||||
@@ -24,9 +24,8 @@ fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun gitVersion(): String {
 | 
			
		||||
    var process = ""
 | 
			
		||||
    val maybeTagOfCurrentCommit = execWithOutput("git -C ../ describe --contains HEAD", true)
 | 
			
		||||
    process = if (maybeTagOfCurrentCommit.isEmpty()) {
 | 
			
		||||
    val process = if (maybeTagOfCurrentCommit.isEmpty()) {
 | 
			
		||||
        println("No tag on current commit. Will take the latest one.")
 | 
			
		||||
        execWithOutput("git -C ../ for-each-ref refs/tags --sort=-refname --format='%(refname:short)' --count=1")
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -58,23 +57,22 @@ android {
 | 
			
		||||
    compileOptions {
 | 
			
		||||
        isCoreLibraryDesugaringEnabled = true
 | 
			
		||||
        // Flag to enable support for the new language APIs
 | 
			
		||||
        sourceCompatibility = JavaVersion.VERSION_11
 | 
			
		||||
        targetCompatibility = JavaVersion.VERSION_11
 | 
			
		||||
        sourceCompatibility = JavaVersion.VERSION_17
 | 
			
		||||
        targetCompatibility = JavaVersion.VERSION_17
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // For Kotlin projects
 | 
			
		||||
    kotlinOptions {
 | 
			
		||||
        jvmTarget = "11"
 | 
			
		||||
        jvmTarget = "17"
 | 
			
		||||
    }
 | 
			
		||||
    compileSdk = 33
 | 
			
		||||
    buildToolsVersion = "33.0.0"
 | 
			
		||||
    compileSdk = 34
 | 
			
		||||
    buildFeatures {
 | 
			
		||||
        viewBinding = true
 | 
			
		||||
    }
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId = "bou.amine.apps.readerforselfossv2.android"
 | 
			
		||||
        minSdk = 21
 | 
			
		||||
        targetSdk = 33
 | 
			
		||||
        minSdk = 25
 | 
			
		||||
        targetSdk = 34
 | 
			
		||||
        versionCode = versionCodeFromGit()
 | 
			
		||||
        versionName = versionNameFromGit()
 | 
			
		||||
 | 
			
		||||
@@ -87,7 +85,7 @@ android {
 | 
			
		||||
        // tests
 | 
			
		||||
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
 | 
			
		||||
    }
 | 
			
		||||
    packagingOptions {
 | 
			
		||||
    packaging {
 | 
			
		||||
        resources {
 | 
			
		||||
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
 | 
			
		||||
        }
 | 
			
		||||
@@ -116,25 +114,25 @@ dependencies {
 | 
			
		||||
    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
 | 
			
		||||
 | 
			
		||||
    implementation(project(":shared"))
 | 
			
		||||
    implementation("com.google.android.material:material:1.5.0")
 | 
			
		||||
    implementation("androidx.appcompat:appcompat:1.4.1")
 | 
			
		||||
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
 | 
			
		||||
    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.preference:preference-ktx:1.1.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.4.1")
 | 
			
		||||
    implementation("com.google.android.material:material:1.5.0")
 | 
			
		||||
    implementation("androidx.recyclerview:recyclerview:1.3.0-alpha01")
 | 
			
		||||
    implementation("androidx.appcompat:appcompat:1.6.1")
 | 
			
		||||
    implementation("com.google.android.material:material:1.9.0")
 | 
			
		||||
    implementation("androidx.recyclerview:recyclerview:1.3.1")
 | 
			
		||||
    implementation("androidx.legacy:legacy-support-v4:1.0.0")
 | 
			
		||||
    implementation("androidx.vectordrawable:vectordrawable:1.2.0-alpha02")
 | 
			
		||||
    implementation("androidx.vectordrawable:vectordrawable:1.2.0-beta01")
 | 
			
		||||
    implementation("androidx.cardview:cardview:1.0.0")
 | 
			
		||||
    implementation("androidx.annotation:annotation:1.3.0")
 | 
			
		||||
    implementation("androidx.work:work-runtime-ktx:2.7.1")
 | 
			
		||||
    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.14.3")
 | 
			
		||||
    implementation("org.jsoup:jsoup:1.15.4")
 | 
			
		||||
 | 
			
		||||
    //multidex
 | 
			
		||||
    implementation("androidx.multidex:multidex:2.0.1")
 | 
			
		||||
@@ -155,7 +153,7 @@ dependencies {
 | 
			
		||||
 | 
			
		||||
    // Pager
 | 
			
		||||
    implementation("me.relex:circleindicator:2.1.6")
 | 
			
		||||
    implementation("androidx.viewpager2:viewpager2:1.1.0-beta01")
 | 
			
		||||
    implementation("androidx.viewpager2:viewpager2:1.1.0-beta02")
 | 
			
		||||
 | 
			
		||||
    //Dependency Injection
 | 
			
		||||
    implementation("org.kodein.di:kodein-di:7.14.0")
 | 
			
		||||
@@ -171,7 +169,7 @@ dependencies {
 | 
			
		||||
    //PhotoView
 | 
			
		||||
    implementation("com.github.chrisbanes:PhotoView:2.3.0")
 | 
			
		||||
 | 
			
		||||
    implementation("androidx.core:core-ktx:1.8.0")
 | 
			
		||||
    implementation("androidx.core:core-ktx:1.12.0")
 | 
			
		||||
 | 
			
		||||
    implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
 | 
			
		||||
 | 
			
		||||
@@ -184,7 +182,7 @@ dependencies {
 | 
			
		||||
    //test
 | 
			
		||||
    testImplementation("junit:junit:4.13.2")
 | 
			
		||||
    testImplementation("io.mockk:mockk:1.12.0")
 | 
			
		||||
    testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
 | 
			
		||||
    testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
 | 
			
		||||
    implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
 | 
			
		||||
 | 
			
		||||
    implementation("ch.acra:acra-http:$acraVersion")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								androidApp/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								androidApp/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							@@ -55,6 +55,7 @@
 | 
			
		||||
# maybe remove later ?
 | 
			
		||||
-keep class * extends androidx.fragment.app.Fragment
 | 
			
		||||
 | 
			
		||||
-dontwarn org.slf4j.impl.StaticLoggerBinder
 | 
			
		||||
 | 
			
		||||
# Keep `Companion` object fields of serializable classes.
 | 
			
		||||
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
 | 
			
		||||
 
 | 
			
		||||
@@ -599,12 +599,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            R.id.action_disconnect -> {
 | 
			
		||||
                runBlocking {
 | 
			
		||||
                    repository.logout()
 | 
			
		||||
                needsConfirmation(R.string.confirm_disconnect_title, R.string.confirm_disconnect_description) {
 | 
			
		||||
                    runBlocking {
 | 
			
		||||
                        repository.logout()
 | 
			
		||||
                    }
 | 
			
		||||
                    val intent = Intent(this, LoginActivity::class.java)
 | 
			
		||||
                    this.startActivity(intent)
 | 
			
		||||
                    finish()
 | 
			
		||||
                }
 | 
			
		||||
                val intent = Intent(this, LoginActivity::class.java)
 | 
			
		||||
                this.startActivity(intent)
 | 
			
		||||
                finish()
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            R.id.action_settings -> {
 | 
			
		||||
 
 | 
			
		||||
@@ -139,9 +139,12 @@ class LoginActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
 | 
			
		||||
        showProgress(true)
 | 
			
		||||
 | 
			
		||||
        appSettingsService.updateSelfSigned(binding.selfSigned.isChecked)
 | 
			
		||||
 | 
			
		||||
        repository.refreshLoginInformation(url, login, password)
 | 
			
		||||
 | 
			
		||||
        CoroutineScope(Dispatchers.Main).launch {
 | 
			
		||||
            repository.updateApiInformation()
 | 
			
		||||
            val result = repository.login()
 | 
			
		||||
            if (result) {
 | 
			
		||||
                val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance()
 | 
			
		||||
 
 | 
			
		||||
@@ -82,7 +82,7 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
 | 
			
		||||
    ) {
 | 
			
		||||
        val sourceGroup = binding.sourcesGroup
 | 
			
		||||
 | 
			
		||||
        repository.getSourcesDetailsOrStats().forEach { source ->
 | 
			
		||||
        repository.getSourcesDetailsOrStats().forEachIndexed { _, source ->
 | 
			
		||||
            val c = Chip(context)
 | 
			
		||||
            c.ellipsize = TextUtils.TruncateAt.END
 | 
			
		||||
 | 
			
		||||
@@ -144,7 +144,7 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
 | 
			
		||||
 | 
			
		||||
        val tags = repository.getTags()
 | 
			
		||||
 | 
			
		||||
        tags.forEach { tag ->
 | 
			
		||||
        tags.forEachIndexed { _, tag ->
 | 
			
		||||
            val c = Chip(context)
 | 
			
		||||
            c.ellipsize = TextUtils.TruncateAt.END
 | 
			
		||||
            c.text = tag.tag
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,7 @@ class SettingsActivity : AppCompatActivity(),
 | 
			
		||||
        val args = pref.extras
 | 
			
		||||
        val fragment = supportFragmentManager.fragmentFactory.instantiate(
 | 
			
		||||
                classLoader,
 | 
			
		||||
                pref.fragment
 | 
			
		||||
            pref.fragment.toString()
 | 
			
		||||
        ).apply {
 | 
			
		||||
            arguments = args
 | 
			
		||||
            setTargetFragment(caller, 0)
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,13 @@
 | 
			
		||||
                android:maxLines="1"
 | 
			
		||||
                android:minHeight="48dp" />
 | 
			
		||||
 | 
			
		||||
            <com.google.android.material.switchmaterial.SwitchMaterial
 | 
			
		||||
                android:id="@+id/selfSigned"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:text="@string/disable_ssl"
 | 
			
		||||
                android:textAlignment="viewStart" />
 | 
			
		||||
 | 
			
		||||
            <com.google.android.material.switchmaterial.SwitchMaterial
 | 
			
		||||
                android:id="@+id/withLogin"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">Esta aplicación só funciona cunha instancia de Selfoss, e con ningún outro filtro RSS.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
 | 
			
		||||
    <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
 | 
			
		||||
    <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
 | 
			
		||||
    <string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
 | 
			
		||||
    <string name="menu_home_filter">Filters</string>
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
</resources>
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">此应用只适用于 Selfoss 实例,不适用于其他 RSS 。</string>
 | 
			
		||||
    <string name="menu_home_sources">源</string>
 | 
			
		||||
    <string name="update_source">更新源</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
    <string name="error_invalid_password">"Password not long enough"</string>
 | 
			
		||||
    <string name="error_field_required">"Field required"</string>
 | 
			
		||||
    <string name="prompt_url">"Url"</string>
 | 
			
		||||
    <string name="disable_ssl">"Disable SSL"</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="prompt_login">"Username"</string>
 | 
			
		||||
@@ -131,4 +132,6 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,38 +9,49 @@ import org.junit.Test
 | 
			
		||||
 | 
			
		||||
class DatesTest {
 | 
			
		||||
 | 
			
		||||
    private val v3Date = "2013-04-07T13:43:00+01:00"
 | 
			
		||||
    private val v4Date = "2013-04-07 13:43:00"
 | 
			
		||||
    private val bug1Date = "2022-12-24T17:00:08+00"
 | 
			
		||||
    private val newVersionDateVariant =  "2022-12-24T17:00:08+00"
 | 
			
		||||
    private val newVersionDate =         "2013-04-07T13:43:00+01:00"
 | 
			
		||||
    private val oldVersionDate =         "2013-05-07 13:46:00"
 | 
			
		||||
    private val oldVersionDateVariant =  "2021-03-21 10:32:00.000000"
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun v3_date_should_be_parsed() {
 | 
			
		||||
        val date = DateUtils.parseDate(v3Date)
 | 
			
		||||
        val expected =
 | 
			
		||||
            LocalDateTime(2013, 4, 7, 14, 43, 0, 0).toInstant(TimeZone.currentSystemDefault())
 | 
			
		||||
                .toEpochMilliseconds()
 | 
			
		||||
 | 
			
		||||
        assertEquals(date, expected)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun v4_date_should_be_parsed() {
 | 
			
		||||
        val date = DateUtils.parseDate(v4Date)
 | 
			
		||||
    fun new_version_date_should_be_parsed() {
 | 
			
		||||
        val date = DateUtils.parseDate(newVersionDate)
 | 
			
		||||
        val expected =
 | 
			
		||||
            LocalDateTime(2013, 4, 7, 13, 43, 0, 0).toInstant(TimeZone.currentSystemDefault())
 | 
			
		||||
                .toEpochMilliseconds()
 | 
			
		||||
 | 
			
		||||
        assertEquals(date, expected)
 | 
			
		||||
        assertEquals(expected, date)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun bug1_date_should_be_parsed() {
 | 
			
		||||
        val date = DateUtils.parseDate(bug1Date)
 | 
			
		||||
    fun old_version_date_should_be_parsed() {
 | 
			
		||||
        val date = DateUtils.parseDate(oldVersionDate)
 | 
			
		||||
        val expected =
 | 
			
		||||
            LocalDateTime(2022, 12, 24, 18, 0, 8, 0).toInstant(TimeZone.currentSystemDefault())
 | 
			
		||||
            LocalDateTime(2013, 5, 7, 13, 46, 0, 0).toInstant(TimeZone.currentSystemDefault())
 | 
			
		||||
                .toEpochMilliseconds()
 | 
			
		||||
 | 
			
		||||
        assertEquals(date, expected)
 | 
			
		||||
        assertEquals(expected, date)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun old_version_variant_date_should_be_parsed() {
 | 
			
		||||
        val date = DateUtils.parseDate(oldVersionDateVariant)
 | 
			
		||||
        val expected =
 | 
			
		||||
            LocalDateTime(2021, 3, 21, 10, 32, 0, 0).toInstant(TimeZone.currentSystemDefault())
 | 
			
		||||
                .toEpochMilliseconds()
 | 
			
		||||
 | 
			
		||||
        assertEquals(expected, date)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun new_version_variant_date_should_be_parsed() {
 | 
			
		||||
        val date = DateUtils.parseDate(newVersionDateVariant)
 | 
			
		||||
        val expected =
 | 
			
		||||
            LocalDateTime(2022, 12, 24, 17, 0, 8, 0).toInstant(TimeZone.currentSystemDefault())
 | 
			
		||||
                .toEpochMilliseconds()
 | 
			
		||||
 | 
			
		||||
        assertEquals(expected, date)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,18 @@
 | 
			
		||||
buildscript {
 | 
			
		||||
    dependencies {
 | 
			
		||||
        // SqlDelight
 | 
			
		||||
        classpath("com.squareup.sqldelight:gradle-plugin:1.5.4")
 | 
			
		||||
        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("7.4.0").apply(false)
 | 
			
		||||
    id("com.android.library").version("7.4.0").apply(false)
 | 
			
		||||
    kotlin("android").version("1.7.20").apply(false)
 | 
			
		||||
    kotlin("multiplatform").version("1.7.20").apply(false)
 | 
			
		||||
    id("com.android.application").version("8.1.2").apply(false)
 | 
			
		||||
    id("com.android.library").version("8.1.2").apply(false)
 | 
			
		||||
    id("org.jetbrains.kotlin.android").version("1.9.10").apply(false)
 | 
			
		||||
    kotlin("multiplatform").version("1.9.10").apply(false)
 | 
			
		||||
    id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").apply(false)
 | 
			
		||||
    id("org.jetbrains.kotlinx.kover") version "0.6.1"
 | 
			
		||||
    id("org.jetbrains.kotlinx.kover").version("0.6.1").apply(true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
allprojects {
 | 
			
		||||
@@ -20,7 +20,6 @@ allprojects {
 | 
			
		||||
        // maven { url = uri("https://nexus.amine-louveau.fr/repository/maven-public/")}
 | 
			
		||||
        google()
 | 
			
		||||
        mavenCentral()
 | 
			
		||||
        jcenter()
 | 
			
		||||
        maven { url = uri("https://www.jitpack.io") }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,24 +13,16 @@
 | 
			
		||||
#Tue Mar 22 16:50:00 CET 2022
 | 
			
		||||
#Gradle
 | 
			
		||||
org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
 | 
			
		||||
 | 
			
		||||
#Kotlin
 | 
			
		||||
kotlin.code.style=official
 | 
			
		||||
 | 
			
		||||
#Android
 | 
			
		||||
android.useAndroidX=true
 | 
			
		||||
kotlin.native.enableDependencyPropagation=false
 | 
			
		||||
#android.nonTransitiveRClass=true
 | 
			
		||||
android.enableJetifier=true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
android.nonTransitiveRClass=false
 | 
			
		||||
#MPP
 | 
			
		||||
kotlin.mpp.enableCInteropCommonization=true
 | 
			
		||||
kotlin.mpp.enableGranularSourceSetsMetadata=true
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
org.gradle.parallel=true
 | 
			
		||||
org.gradle.caching=true
 | 
			
		||||
ignoreGitVersion=false
 | 
			
		||||
kotlin.native.cacheKind.iosX64=none
 | 
			
		||||
pushCache=true
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
#Mon Jan 23 20:47:46 CET 2023
 | 
			
		||||
#Thu Jul 13 11:41:19 CEST 2023
 | 
			
		||||
distributionBase=GRADLE_USER_HOME
 | 
			
		||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
 | 
			
		||||
distributionPath=wrapper/dists
 | 
			
		||||
zipStorePath=wrapper/dists
 | 
			
		||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
 | 
			
		||||
zipStoreBase=GRADLE_USER_HOME
 | 
			
		||||
zipStorePath=wrapper/dists
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
val pushCache: String by settings
 | 
			
		||||
 | 
			
		||||
pluginManagement {
 | 
			
		||||
    repositories {
 | 
			
		||||
        // maven { url = uri("https://nexus.amine-louveau.fr/repository/maven-public/")}
 | 
			
		||||
@@ -17,17 +15,6 @@ dependencyResolutionManagement {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
buildCache {
 | 
			
		||||
    remote<HttpBuildCache> {
 | 
			
		||||
        url = uri("http://18.0.0.7:3071/cache/")
 | 
			
		||||
        isAllowInsecureProtocol = true
 | 
			
		||||
        isAllowUntrustedServer = true
 | 
			
		||||
        isUseExpectContinue = true
 | 
			
		||||
        isPush = (pushCache == "true")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
rootProject.name = "ReaderForSelfossV2"
 | 
			
		||||
include(":androidApp")
 | 
			
		||||
include(":shared")
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
val ktorVersion = "2.3.2"
 | 
			
		||||
 | 
			
		||||
object SqlDelight {
 | 
			
		||||
    const val runtime = "com.squareup.sqldelight:runtime:1.5.4"
 | 
			
		||||
    const val android = "com.squareup.sqldelight:android-driver:1.5.4"
 | 
			
		||||
@@ -9,12 +11,12 @@ plugins {
 | 
			
		||||
    kotlin("multiplatform")
 | 
			
		||||
    id("com.android.library")
 | 
			
		||||
    id("com.squareup.sqldelight")
 | 
			
		||||
    kotlin("plugin.serialization") version "1.4.10"
 | 
			
		||||
    id("org.jetbrains.kotlinx.kover") version "0.6.1"
 | 
			
		||||
    kotlin("plugin.serialization") version "1.9.0"
 | 
			
		||||
    id("org.jetbrains.kotlinx.kover")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
kotlin {
 | 
			
		||||
    android()
 | 
			
		||||
    androidTarget()
 | 
			
		||||
 | 
			
		||||
    listOf(
 | 
			
		||||
        iosX64(),
 | 
			
		||||
@@ -29,16 +31,18 @@ kotlin {
 | 
			
		||||
    sourceSets {
 | 
			
		||||
        val commonMain by getting {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                implementation("io.ktor:ktor-client-core:2.1.1")
 | 
			
		||||
                implementation("io.ktor:ktor-client-content-negotiation:2.1.1")
 | 
			
		||||
                implementation("io.ktor:ktor-serialization-kotlinx-json:2.1.1")
 | 
			
		||||
                implementation("io.ktor:ktor-client-logging:2.1.1")
 | 
			
		||||
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
 | 
			
		||||
                implementation("io.ktor:ktor-client-auth:2.1.1")
 | 
			
		||||
                implementation("org.jsoup:jsoup:1.14.3")
 | 
			
		||||
                implementation("io.ktor:ktor-client-core:$ktorVersion")
 | 
			
		||||
                implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
 | 
			
		||||
                implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
 | 
			
		||||
                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.jsoup:jsoup:1.15.4")
 | 
			
		||||
 | 
			
		||||
                //Dependency Injection
 | 
			
		||||
                implementation("org.kodein.di:kodein-di:7.12.0")
 | 
			
		||||
                implementation("org.kodein.di:kodein-di:7.14.0")
 | 
			
		||||
 | 
			
		||||
                //Settings
 | 
			
		||||
                implementation("com.russhwolf:multiplatform-settings-no-arg:1.0.0-RC")
 | 
			
		||||
@@ -58,14 +62,15 @@ kotlin {
 | 
			
		||||
        }
 | 
			
		||||
        val androidMain by getting {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                implementation("io.ktor:ktor-client-okhttp:2.1.1")
 | 
			
		||||
                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
 | 
			
		||||
                implementation(SqlDelight.android)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        val androidTest by getting {
 | 
			
		||||
        val androidUnitTest by getting {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                implementation(kotlin("test-junit"))
 | 
			
		||||
                implementation("junit:junit:4.13.2")
 | 
			
		||||
@@ -98,15 +103,14 @@ kotlin {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
    compileSdk = 32
 | 
			
		||||
    compileSdk = 34
 | 
			
		||||
    sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        minSdk = 21
 | 
			
		||||
        targetSdk = 32
 | 
			
		||||
        minSdk = 25
 | 
			
		||||
    }
 | 
			
		||||
    compileOptions {
 | 
			
		||||
        sourceCompatibility = JavaVersion.VERSION_1_8
 | 
			
		||||
        targetCompatibility = JavaVersion.VERSION_1_8
 | 
			
		||||
        sourceCompatibility = JavaVersion.VERSION_17
 | 
			
		||||
        targetCompatibility = JavaVersion.VERSION_17
 | 
			
		||||
    }
 | 
			
		||||
    namespace = "bou.amine.apps.readerforselfossv2"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,17 @@
 | 
			
		||||
package bou.amine.apps.readerforselfossv2.rest
 | 
			
		||||
 | 
			
		||||
import io.ktor.client.engine.cio.CIOEngineConfig
 | 
			
		||||
import java.security.cert.X509Certificate
 | 
			
		||||
import javax.net.ssl.X509TrustManager
 | 
			
		||||
 | 
			
		||||
class NaiveTrustManager : X509TrustManager {
 | 
			
		||||
    override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
 | 
			
		||||
 | 
			
		||||
    override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
 | 
			
		||||
 | 
			
		||||
    override fun getAcceptedIssuers(): Array<out X509Certificate> = arrayOf()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) {
 | 
			
		||||
    config.https.trustManager = NaiveTrustManager()
 | 
			
		||||
}
 | 
			
		||||
@@ -6,16 +6,25 @@ import kotlinx.datetime.*
 | 
			
		||||
 | 
			
		||||
actual class DateUtils {
 | 
			
		||||
    actual companion object {
 | 
			
		||||
 | 
			
		||||
        // Possible formats are
 | 
			
		||||
        // yyyy-mm-dd hh:mm:ss format
 | 
			
		||||
        private val oldVersionFormat = "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(.()\\d*)?".toRegex()
 | 
			
		||||
        // yyyy-MM-dd'T'HH:mm:ss[.SSS]XXX (RFC3339)
 | 
			
		||||
        private val newVersionFormat = "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+\\d{2}(:\\d{2})?".toRegex()
 | 
			
		||||
 | 
			
		||||
        // We may need to consider moving the formatting to platform specific code, even if the tests are doubled
 | 
			
		||||
        // For now, we handle this in a hacky way, because kotlin only accepts iso formats
 | 
			
		||||
        actual fun parseDate(dateString: String): Long {
 | 
			
		||||
            return try {
 | 
			
		||||
                Instant.parse(dateString).toEpochMilliseconds()
 | 
			
		||||
            } catch (e: Exception) {
 | 
			
		||||
                var str = dateString.replace(" ", "T")
 | 
			
		||||
                if (str.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+\\d{2}".toRegex())) {
 | 
			
		||||
                    str = str.split("+")[0]
 | 
			
		||||
                }
 | 
			
		||||
                LocalDateTime.parse(str).toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds()
 | 
			
		||||
            var isoDateString: String = if (dateString.matches(oldVersionFormat)) {
 | 
			
		||||
                dateString.replace(" ", "T")
 | 
			
		||||
            } else if (dateString.matches(newVersionFormat)) {
 | 
			
		||||
                dateString.split("+")[0]
 | 
			
		||||
            } else {
 | 
			
		||||
                throw Exception("Unrecognized format for $dateString")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return LocalDateTime.parse(isoDateString).toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        actual fun parseRelativeDate(dateString: String): String {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,31 +5,46 @@ import bou.amine.apps.readerforselfossv2.model.StatusAndData
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.model.SuccessResponse
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
 | 
			
		||||
import io.github.aakira.napier.Napier
 | 
			
		||||
import io.ktor.client.*
 | 
			
		||||
import io.ktor.client.plugins.*
 | 
			
		||||
import io.ktor.client.plugins.auth.providers.*
 | 
			
		||||
import io.ktor.client.plugins.cache.*
 | 
			
		||||
import io.ktor.client.plugins.contentnegotiation.*
 | 
			
		||||
import io.ktor.client.plugins.cookies.*
 | 
			
		||||
import io.ktor.client.plugins.logging.*
 | 
			
		||||
import io.ktor.client.request.*
 | 
			
		||||
import io.ktor.client.statement.*
 | 
			
		||||
import io.ktor.http.*
 | 
			
		||||
import io.ktor.serialization.kotlinx.json.*
 | 
			
		||||
import io.ktor.util.*
 | 
			
		||||
import io.ktor.utils.io.charsets.*
 | 
			
		||||
import io.ktor.utils.io.core.*
 | 
			
		||||
import io.ktor.client.HttpClient
 | 
			
		||||
import io.ktor.client.engine.cio.CIO
 | 
			
		||||
import io.ktor.client.engine.cio.CIOEngineConfig
 | 
			
		||||
import io.ktor.client.plugins.HttpRequestRetry
 | 
			
		||||
import io.ktor.client.plugins.HttpTimeout
 | 
			
		||||
import io.ktor.client.plugins.auth.providers.BasicAuthCredentials
 | 
			
		||||
import io.ktor.client.plugins.cache.HttpCache
 | 
			
		||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
 | 
			
		||||
import io.ktor.client.plugins.cookies.HttpCookies
 | 
			
		||||
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.headers
 | 
			
		||||
import io.ktor.client.request.parameter
 | 
			
		||||
import io.ktor.client.statement.HttpResponse
 | 
			
		||||
import io.ktor.http.HttpHeaders
 | 
			
		||||
import io.ktor.http.HttpStatusCode
 | 
			
		||||
import io.ktor.http.Parameters
 | 
			
		||||
import io.ktor.serialization.kotlinx.json.json
 | 
			
		||||
import io.ktor.util.encodeBase64
 | 
			
		||||
import io.ktor.utils.io.charsets.Charsets
 | 
			
		||||
import io.ktor.utils.io.core.toByteArray
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
 | 
			
		||||
expect fun setupInsecureHTTPEngine(config: CIOEngineConfig)
 | 
			
		||||
 | 
			
		||||
class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
 | 
			
		||||
    var client = createHttpClient()
 | 
			
		||||
 | 
			
		||||
    private fun createHttpClient(): HttpClient {
 | 
			
		||||
        val client = HttpClient {
 | 
			
		||||
    fun createHttpClient() =
 | 
			
		||||
        HttpClient(CIO) {
 | 
			
		||||
            if (appSettingsService.getSelfSigned()) {
 | 
			
		||||
                engine {
 | 
			
		||||
                    setupInsecureHTTPEngine(this)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            install(ContentNegotiation) {
 | 
			
		||||
                install(HttpCache)
 | 
			
		||||
                json(Json {
 | 
			
		||||
@@ -60,7 +75,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                    Napier.i("Will modify", tag = "HttpSend")
 | 
			
		||||
                    CoroutineScope(Dispatchers.Main).launch {
 | 
			
		||||
                        Napier.i("Will login", tag = "HttpSend")
 | 
			
		||||
                        this@SelfossApi.login()
 | 
			
		||||
                        login()
 | 
			
		||||
                        Napier.i("Did login", tag = "HttpSend")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -68,10 +83,6 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
            expectSuccess = false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return client
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun url(path: String) =
 | 
			
		||||
        "${appSettingsService.getBaseUrl()}$path"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
    // Api related
 | 
			
		||||
    private var _apiVersion: Int = -1
 | 
			
		||||
    private var _publicAccess: Boolean? = null
 | 
			
		||||
    private var _selfSigned: Boolean? = null
 | 
			
		||||
    private var _baseUrl: String = ""
 | 
			
		||||
    private var _userName: String = ""
 | 
			
		||||
    private var _basicUserName: String = ""
 | 
			
		||||
@@ -77,6 +78,22 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
        _publicAccess = settings.getBoolean(API_PUBLIC_ACCESS, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getSelfSigned(): Boolean {
 | 
			
		||||
        if (_selfSigned == null) {
 | 
			
		||||
            refreshSelfSigned()
 | 
			
		||||
        }
 | 
			
		||||
        return _selfSigned!!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateSelfSigned(selfSigned: Boolean) {
 | 
			
		||||
        settings.putBoolean(API_SELF_SIGNED, selfSigned)
 | 
			
		||||
        refreshSelfSigned()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun refreshSelfSigned() {
 | 
			
		||||
        _selfSigned = settings.getBoolean(API_SELF_SIGNED, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getBaseUrl(): String {
 | 
			
		||||
        if (_baseUrl.isEmpty()) {
 | 
			
		||||
            refreshBaseUrl()
 | 
			
		||||
@@ -383,6 +400,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
        refreshBaseUrl()
 | 
			
		||||
        refreshApiVersion()
 | 
			
		||||
        refreshPublicAccess()
 | 
			
		||||
        refreshSelfSigned()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun refreshUserSettings() {
 | 
			
		||||
@@ -468,6 +486,8 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
 | 
			
		||||
        const val API_PUBLIC_ACCESS = "apiPublicAccess"
 | 
			
		||||
 | 
			
		||||
        const val API_SELF_SIGNED = "apiSelfSigned"
 | 
			
		||||
 | 
			
		||||
        const val API_ITEMS_NUMBER = "prefer_api_items_number"
 | 
			
		||||
 | 
			
		||||
        const val API_TIMEOUT = "api_timeout"
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,6 @@
 | 
			
		||||
package bou.amine.apps.readerforselfossv2.rest
 | 
			
		||||
 | 
			
		||||
import io.ktor.client.engine.cio.CIOEngineConfig
 | 
			
		||||
 | 
			
		||||
actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) {
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,6 @@
 | 
			
		||||
package bou.amine.apps.readerforselfossv2.rest
 | 
			
		||||
 | 
			
		||||
import io.ktor.client.engine.cio.CIOEngineConfig
 | 
			
		||||
 | 
			
		||||
actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) {
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user