Compare commits

...

11 Commits

Author SHA1 Message Date
davidoskky 0aac2cba0b Android gradle plugin
continuous-integration/drone/pr Build is passing
2023-09-07 11:55:15 +02:00
davidoskky 42e1c1ebbd Downgrade gradle version
continuous-integration/drone/pr Build is failing
2023-08-22 16:49:48 +02:00
davidoskky a67a2a0927 Use forEachIndexed to support older apis
continuous-integration/drone/pr Build is failing
2023-08-21 23:25:35 +02:00
davidoskky 24beee070c Remove unused indexed function call
continuous-integration/drone/pr Build is failing
2023-08-19 11:56:54 +02:00
davidoskky 1e6bc57751 Revert to Java version 11 2023-08-19 11:56:54 +02:00
davidoskky 85d966f54b Fix naming and reintroduce the xmlns
continuous-integration/drone/pr Build is failing
2023-08-10 21:56:07 +02:00
davidoskky 3fff8eeeb6 Handle most HTTP client creation in common code 2023-08-10 21:43:13 +02:00
davidoskky 5eb5a5658f Add translations
continuous-integration/drone/pr Build is passing
2023-07-15 13:48:23 +02:00
davidoskky 3646658cb1 Fix R8 compilation problem
continuous-integration/drone/pr Build is failing
2023-07-15 13:25:51 +02:00
davidoskky b793301706 Add a login switch to disable SSL verification
continuous-integration/drone/pr Build is failing
2023-07-13 14:55:48 +02:00
davidoskky 313042955c Upgrade to gradle 8 2023-07-13 14:55:48 +02:00
31 changed files with 171 additions and 130 deletions
+4 -5
View File
@@ -8,7 +8,7 @@ plugins {
kotlin("android") kotlin("android")
kotlin("kapt") kotlin("kapt")
id("com.mikepenz.aboutlibraries.plugin") 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 { fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
@@ -16,7 +16,7 @@ fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
project.exec { project.exec {
commandLine = cmd.split(" ") commandLine = cmd.split(" ")
standardOutput = outputStream standardOutput = outputStream
isIgnoreExitValue = ignore ?: false isIgnoreExitValue = ignore
} }
outputStream.toString() outputStream.toString()
} }
@@ -24,9 +24,8 @@ fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
} }
fun gitVersion(): String { fun gitVersion(): String {
var process = ""
val maybeTagOfCurrentCommit = execWithOutput("git -C ../ describe --contains HEAD", true) val maybeTagOfCurrentCommit = execWithOutput("git -C ../ describe --contains HEAD", true)
process = if (maybeTagOfCurrentCommit.isEmpty()) { var process = if (maybeTagOfCurrentCommit.isEmpty()) {
println("No tag on current commit. Will take the latest one.") 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") execWithOutput("git -C ../ for-each-ref refs/tags --sort=-refname --format='%(refname:short)' --count=1")
} else { } else {
@@ -171,7 +170,7 @@ dependencies {
//PhotoView //PhotoView
implementation("com.github.chrisbanes:PhotoView:2.3.0") implementation("com.github.chrisbanes:PhotoView:2.3.0")
implementation("androidx.core:core-ktx:1.8.0") implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
+1
View File
@@ -55,6 +55,7 @@
# maybe remove later ? # maybe remove later ?
-keep class * extends androidx.fragment.app.Fragment -keep class * extends androidx.fragment.app.Fragment
-dontwarn org.slf4j.impl.StaticLoggerBinder
# Keep `Companion` object fields of serializable classes. # Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects. # This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
@@ -139,9 +139,12 @@ class LoginActivity : AppCompatActivity(), DIAware {
showProgress(true) showProgress(true)
appSettingsService.updateSelfSigned(binding.selfSigned.isChecked)
repository.refreshLoginInformation(url, login, password) repository.refreshLoginInformation(url, login, password)
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
repository.updateApiInformation()
val result = repository.login() val result = repository.login()
if (result) { if (result) {
val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance() val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance()
@@ -82,7 +82,7 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
) { ) {
val sourceGroup = binding.sourcesGroup val sourceGroup = binding.sourcesGroup
repository.getSourcesDetailsOrStats().forEach { source -> repository.getSourcesDetailsOrStats().forEachIndexed { _, source ->
val c = Chip(context) val c = Chip(context)
c.ellipsize = TextUtils.TruncateAt.END c.ellipsize = TextUtils.TruncateAt.END
@@ -144,7 +144,7 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
val tags = repository.getTags() val tags = repository.getTags()
tags.forEach { tag -> tags.forEachIndexed { _, tag ->
val c = Chip(context) val c = Chip(context)
c.ellipsize = TextUtils.TruncateAt.END c.ellipsize = TextUtils.TruncateAt.END
c.text = tag.tag c.text = tag.tag
@@ -55,7 +55,7 @@
android:id="@+id/selfSigned" android:id="@+id/selfSigned"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/disableSSL" android:text="@string/disable_ssl"
android:textAlignment="viewStart" /> android:textAlignment="viewStart" />
<com.google.android.material.switchmaterial.SwitchMaterial <com.google.android.material.switchmaterial.SwitchMaterial
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Lettore RSS per Selfoss"</string> <string name="app_name">"Lettore RSS per Selfoss"</string>
<string name="title_activity_login">"Accedi"</string> <string name="title_activity_login">"Accedi"</string>
<string name="prompt_password">"Password"</string> <string name="prompt_password">"Password"</string>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <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="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="crash_toast_text">A crash occured. Sending the details to the developper.</string>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">更新源</string> <string name="update_source">更新源</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
@@ -130,4 +130,5 @@
<string name="update_source">Update source</string> <string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string> <string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string> <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="disable_ssl">Disable SSL</string>
</resources> </resources>
+2 -2
View File
@@ -1,4 +1,4 @@
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">"Reader for Selfoss"</string> <string name="app_name">"Reader for Selfoss"</string>
<string name="title_activity_login">"Log in"</string> <string name="title_activity_login">"Log in"</string>
<string name="prompt_password">"Password"</string> <string name="prompt_password">"Password"</string>
@@ -6,7 +6,7 @@
<string name="error_invalid_password">"Password not long enough"</string> <string name="error_invalid_password">"Password not long enough"</string>
<string name="error_field_required">"Field required"</string> <string name="error_field_required">"Field required"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disableSSL">"Disable SSL"</string> <string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Login required ?"</string> <string name="withLoginSwitch">"Login required ?"</string>
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string> <string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
<string name="prompt_login">"Username"</string> <string name="prompt_login">"Username"</string>
+5 -5
View File
@@ -7,12 +7,12 @@ buildscript {
plugins { plugins {
//trick: for the same plugin versions in all sub-modules //trick: for the same plugin versions in all sub-modules
id("com.android.application").version("7.4.0").apply(false) id("com.android.application").version("8.1.0").apply(false)
id("com.android.library").version("7.4.0").apply(false) id("com.android.library").version("8.1.0").apply(false)
kotlin("android").version("1.7.20").apply(false) id("org.jetbrains.kotlin.android").version("1.9.10").apply(false)
kotlin("multiplatform").version("1.7.20").apply(false) kotlin("multiplatform").version("1.9.10").apply(false)
id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").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 { allprojects {
+1 -8
View File
@@ -13,22 +13,15 @@
#Tue Mar 22 16:50:00 CET 2022 #Tue Mar 22 16:50:00 CET 2022
#Gradle #Gradle
org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M" org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
#Kotlin #Kotlin
kotlin.code.style=official kotlin.code.style=official
#Android #Android
android.useAndroidX=true android.useAndroidX=true
kotlin.native.enableDependencyPropagation=false
#android.nonTransitiveRClass=true #android.nonTransitiveRClass=true
android.enableJetifier=true android.enableJetifier=true
android.nonTransitiveRClass=false
#MPP #MPP
kotlin.mpp.enableCInteropCommonization=true kotlin.mpp.enableCInteropCommonization=true
kotlin.mpp.enableGranularSourceSetsMetadata=true
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.caching=true org.gradle.caching=true
ignoreGitVersion=false ignoreGitVersion=false
+3 -3
View File
@@ -1,6 +1,6 @@
#Mon Jan 23 20:47:46 CET 2023 #Thu Jul 13 11:41:19 CEST 2023
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+18 -15
View File
@@ -1,3 +1,5 @@
val ktorVersion = "2.3.2"
object SqlDelight { object SqlDelight {
const val runtime = "com.squareup.sqldelight:runtime:1.5.4" const val runtime = "com.squareup.sqldelight:runtime:1.5.4"
const val android = "com.squareup.sqldelight:android-driver:1.5.4" const val android = "com.squareup.sqldelight:android-driver:1.5.4"
@@ -9,12 +11,13 @@ plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("com.android.library") id("com.android.library")
id("com.squareup.sqldelight") id("com.squareup.sqldelight")
kotlin("plugin.serialization") version "1.4.10" kotlin("plugin.serialization") version "1.9.0"
id("org.jetbrains.kotlinx.kover") version "0.6.1" id("org.jetbrains.kotlinx.kover")
} }
kotlin { kotlin {
android() androidTarget()
jvmToolchain(11)
listOf( listOf(
iosX64(), iosX64(),
@@ -29,12 +32,14 @@ kotlin {
sourceSets { sourceSets {
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
implementation("io.ktor:ktor-client-core:2.2.4") implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-content-negotiation:2.2.4") implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.2.4") implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("io.ktor:ktor-client-logging:2.2.4") 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.6.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
implementation("io.ktor:ktor-client-auth:2.2.4")
implementation("org.jsoup:jsoup:1.15.4") implementation("org.jsoup:jsoup:1.15.4")
//Dependency Injection //Dependency Injection
@@ -58,15 +63,14 @@ kotlin {
} }
val androidMain by getting { val androidMain by getting {
dependencies { dependencies {
implementation("com.squareup.okhttp3:okhttp:4.10.0") implementation("com.squareup.okhttp3:okhttp:4.11.0")
implementation("io.ktor:ktor-client-okhttp:2.2.4")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
// Sql // Sql
implementation(SqlDelight.android) implementation(SqlDelight.android)
} }
} }
val androidTest by getting { val androidUnitTest by getting {
dependencies { dependencies {
implementation(kotlin("test-junit")) implementation(kotlin("test-junit"))
implementation("junit:junit:4.13.2") implementation("junit:junit:4.13.2")
@@ -99,15 +103,14 @@ kotlin {
} }
android { android {
compileSdk = 32 compileSdk = 33
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig { defaultConfig {
minSdk = 21 minSdk = 21
targetSdk = 32
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_11
} }
namespace = "bou.amine.apps.readerforselfossv2" 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()
}
@@ -1,82 +0,0 @@
package bou.amine.apps.readerforselfossv2.rest
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import io.github.aakira.napier.Napier
import io.ktor.client.*
import io.ktor.client.engine.okhttp.*
import io.ktor.client.plugins.*
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.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import org.apache.http.conn.ssl.AllowAllHostnameVerifier
import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext
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 createHttpClient(appSettingsService: AppSettingsService, api: SelfossApi) =
HttpClient(OkHttp) {
engine {
val trustManager = NaiveTrustManager()
val sslContext = SSLContext.getInstance("TLS").apply {
init(null, arrayOf(trustManager), null)
}
preconfigured = OkHttpClient().newBuilder()
.sslSocketFactory(
sslSocketFactory = sslContext.socketFactory,
trustManager = trustManager
)
.hostnameVerifier(AllowAllHostnameVerifier())
.build()
}
install(ContentNegotiation) {
install(HttpCache)
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
})
}
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
Napier.d(message, tag = "LogApiCalls")
}
}
level = LogLevel.INFO
}
install(HttpTimeout) {
requestTimeoutMillis = appSettingsService.getApiTimeout()
}
install(HttpCookies)
install(HttpRequestRetry) {
maxRetries = 2
retryIf { _, response ->
response.status == HttpStatusCode.Forbidden && api.shouldHavePostLogin() && api.hasLoginInfo()
}
modifyRequest {
Napier.i("Will modify", tag = "HttpSend")
CoroutineScope(Dispatchers.Main).launch {
Napier.i("Will login", tag = "HttpSend")
api.login()
Napier.i("Did login", tag = "HttpSend")
}
}
}
expectSuccess = false
}
@@ -4,12 +4,83 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.model.StatusAndData import bou.amine.apps.readerforselfossv2.model.StatusAndData
import bou.amine.apps.readerforselfossv2.model.SuccessResponse import bou.amine.apps.readerforselfossv2.model.SuccessResponse
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import io.ktor.client.* import io.github.aakira.napier.Napier
import io.ktor.client.request.* import io.ktor.client.HttpClient
import io.ktor.client.statement.* import io.ktor.client.engine.cio.CIO
import io.ktor.http.* 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 createHttpClient(appSettingsService: AppSettingsService, api: SelfossApi): HttpClient expect fun setupInsecureHTTPEngine(config: CIOEngineConfig)
fun createHttpClient(
appSettingsService: AppSettingsService,
api: SelfossApi
) =
HttpClient(CIO) {
if (appSettingsService.getSelfSigned()) {
engine {
setupInsecureHTTPEngine(this)
}
}
install(ContentNegotiation) {
install(HttpCache)
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
})
}
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
Napier.d(message, tag = "LogApiCalls")
}
}
level = LogLevel.INFO
}
install(HttpTimeout) {
requestTimeoutMillis = appSettingsService.getApiTimeout()
}
install(HttpCookies)
install(HttpRequestRetry) {
maxRetries = 2
retryIf { _, response ->
response.status == HttpStatusCode.Forbidden && api.shouldHavePostLogin() && api.hasLoginInfo()
}
modifyRequest {
Napier.i("Will modify", tag = "HttpSend")
CoroutineScope(Dispatchers.Main).launch {
Napier.i("Will login", tag = "HttpSend")
api.login()
Napier.i("Did login", tag = "HttpSend")
}
}
}
expectSuccess = false
}
class SelfossApi(private val appSettingsService: AppSettingsService) { class SelfossApi(private val appSettingsService: AppSettingsService) {
@@ -8,6 +8,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
// Api related // Api related
private var _apiVersion: Int = -1 private var _apiVersion: Int = -1
private var _publicAccess: Boolean? = null private var _publicAccess: Boolean? = null
private var _selfSigned: Boolean? = null
private var _baseUrl: String = "" private var _baseUrl: String = ""
private var _userName: String = "" private var _userName: String = ""
private var _basicUserName: String = "" private var _basicUserName: String = ""
@@ -77,6 +78,22 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
_publicAccess = settings.getBoolean(API_PUBLIC_ACCESS, 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 { fun getBaseUrl(): String {
if (_baseUrl.isEmpty()) { if (_baseUrl.isEmpty()) {
refreshBaseUrl() refreshBaseUrl()
@@ -383,6 +400,7 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
refreshBaseUrl() refreshBaseUrl()
refreshApiVersion() refreshApiVersion()
refreshPublicAccess() refreshPublicAccess()
refreshSelfSigned()
} }
fun refreshUserSettings() { fun refreshUserSettings() {
@@ -468,6 +486,8 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
const val API_PUBLIC_ACCESS = "apiPublicAccess" const val API_PUBLIC_ACCESS = "apiPublicAccess"
const val API_SELF_SIGNED = "apiSelfSigned"
const val API_ITEMS_NUMBER = "prefer_api_items_number" const val API_ITEMS_NUMBER = "prefer_api_items_number"
const val API_TIMEOUT = "api_timeout" const val API_TIMEOUT = "api_timeout"