Init.
This commit is contained in:
2
shared/src/androidMain/AndroidManifest.xml
Normal file
2
shared/src/androidMain/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="bou.amine.apps.readerforselfossv2" />
|
@ -0,0 +1,5 @@
|
||||
package bou.amine.apps.readerforselfossv2
|
||||
|
||||
actual class Platform actual constructor() {
|
||||
actual val platform: String = "Android ${android.os.Build.VERSION.SDK_INT}"
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package bou.amine.apps.readerforselfossv2
|
||||
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class AndroidGreetingTest {
|
||||
|
||||
@Test
|
||||
fun testExample() {
|
||||
assertTrue("Check Android is mentioned", Greeting().greeting().contains("Android"))
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package bou.amine.apps.readerforselfossv2
|
||||
|
||||
class Greeting {
|
||||
fun greeting(): String {
|
||||
return "Hello, ${Platform().platform}!"
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package bou.amine.apps.readerforselfossv2
|
||||
|
||||
expect class Platform() {
|
||||
val platform: String
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
package bou.amine.apps.readerforselfossv2.rest
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.text.Html
|
||||
import bou.amine.apps.readerforselfossv2.rest.SelfossModel.SelfossModel.constructUrl
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.features.*
|
||||
import io.ktor.client.features.json.*
|
||||
import io.ktor.client.features.json.serializer.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.request.forms.*
|
||||
import io.ktor.http.*
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.jvm.JvmField
|
||||
|
||||
class SelfossApi {
|
||||
/**
|
||||
* TODO:
|
||||
* Self signed certs
|
||||
* Timeout + 408
|
||||
* Auth digest/basic
|
||||
* Loging
|
||||
*/
|
||||
|
||||
val baseUrl = "http://10.0.2.2:8888"
|
||||
val userName = ""
|
||||
val password = ""
|
||||
val client = HttpClient() {
|
||||
install(JsonFeature) {
|
||||
serializer = KotlinxSerializer(kotlinx.serialization.json.Json {
|
||||
prettyPrint = true
|
||||
isLenient = true
|
||||
ignoreUnknownKeys = true
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun url(path: String) =
|
||||
"$baseUrl$path"
|
||||
|
||||
|
||||
suspend fun login() =
|
||||
client.get<String>(url("/login"))// Todo: params
|
||||
|
||||
suspend fun getItems(type: String,
|
||||
items: Int,
|
||||
offset: Int,
|
||||
tag: String? = "",
|
||||
source: Long? = null,
|
||||
search: String? = "",
|
||||
updatedSince: String? = ""): List<SelfossModel.Item> =
|
||||
client.get(url("/items")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
parameter("type", type)
|
||||
parameter("tag", tag)
|
||||
parameter("source", source)
|
||||
parameter("search", search)
|
||||
parameter("updatedsince", updatedSince)
|
||||
parameter("items", items)
|
||||
parameter("offset", offset)
|
||||
}
|
||||
|
||||
suspend fun stats(): SelfossModel.Stats =
|
||||
client.get(url("/stats")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
}
|
||||
|
||||
suspend fun tags(): List<SelfossModel.Tag> =
|
||||
client.get(url("/tags")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
}
|
||||
|
||||
suspend fun update(): String =
|
||||
client.get(url("/update")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
}
|
||||
|
||||
suspend fun spouts(): Map<String, SelfossModel.Spout> =
|
||||
client.get(url("/sources/spouts")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
}
|
||||
|
||||
suspend fun sources(): List<SelfossModel.Source> =
|
||||
client.get(url("/sources/list")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
}
|
||||
|
||||
suspend fun version(): SelfossModel.ApiVersion =
|
||||
client.get(url("/api/about"))
|
||||
|
||||
suspend fun markAsRead(id: String): SelfossModel.SuccessResponse =
|
||||
client.submitForm(
|
||||
url = url("/mark/$id"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", userName)
|
||||
append("password", password)
|
||||
},
|
||||
encodeInQuery = true
|
||||
)
|
||||
|
||||
suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse =
|
||||
client.submitForm(
|
||||
url = url("/unmark/$id"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", userName)
|
||||
append("password", password)
|
||||
},
|
||||
encodeInQuery = true
|
||||
)
|
||||
|
||||
suspend fun starr(id: String): SelfossModel.SuccessResponse =
|
||||
client.submitForm(
|
||||
url = url("/starr/$id"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", userName)
|
||||
append("password", password)
|
||||
},
|
||||
encodeInQuery = true
|
||||
)
|
||||
|
||||
suspend fun unstarr(id: String): SelfossModel.SuccessResponse =
|
||||
client.submitForm(
|
||||
url = url("/unstarr/$id"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", userName)
|
||||
append("password", password)
|
||||
},
|
||||
encodeInQuery = true
|
||||
)
|
||||
|
||||
suspend fun markAllAsRead(ids: List<String>): SelfossModel.SuccessResponse =
|
||||
client.submitForm(
|
||||
url = url("/mark"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", userName)
|
||||
append("password", password)
|
||||
append("ids[]", ids.joinToString(","))
|
||||
},
|
||||
encodeInQuery = true
|
||||
)
|
||||
|
||||
suspend fun createSource(title: String, url: String, spout: String, tags: String, filter: String): SelfossModel.SuccessResponse =
|
||||
client.submitForm(
|
||||
url = url("/source"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", userName)
|
||||
append("password", password)
|
||||
append("title", title)
|
||||
append("url", url)
|
||||
append("spout", spout)
|
||||
append("tags", tags)
|
||||
append("filter", filter)
|
||||
},
|
||||
encodeInQuery = true
|
||||
)
|
||||
|
||||
suspend fun createSource2(title: String, url: String, spout: String, tags: String, filter: String): SelfossModel.SuccessResponse =
|
||||
client.submitForm(
|
||||
url = url("/source"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", userName)
|
||||
append("password", password)
|
||||
append("title", title)
|
||||
append("url", url)
|
||||
append("spout", spout)
|
||||
append("tags[]", tags)
|
||||
append("filter", filter)
|
||||
},
|
||||
encodeInQuery = true
|
||||
)
|
||||
|
||||
suspend fun deleteSource(id: String) =
|
||||
client.delete<SelfossModel.SuccessResponse>(url("/source/$id")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
package bou.amine.apps.readerforselfossv2.rest
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.text.Html
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.jvm.JvmField
|
||||
import org.jsoup.Jsoup
|
||||
import java.util.Locale.US
|
||||
|
||||
class SelfossModel {
|
||||
|
||||
@Serializable
|
||||
data class Tag(
|
||||
val tag: String,
|
||||
val color: String,
|
||||
val unread: Int
|
||||
) {
|
||||
fun getTitleDecoded(): String {
|
||||
return Html.fromHtml(tag).toString()
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class SuccessResponse(val success: Boolean) {
|
||||
val isSuccess: Boolean
|
||||
get() = success
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class Stats(
|
||||
val total: Int,
|
||||
val unread: Int,
|
||||
val starred: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Spout(
|
||||
val name: String,
|
||||
val description: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ApiVersion(
|
||||
val version: String?,
|
||||
val apiversion: String?
|
||||
) {
|
||||
fun getApiMajorVersion() : Int {
|
||||
var versionNumber = 0
|
||||
if (apiversion != null) {
|
||||
versionNumber = apiversion.substringBefore(".").toInt()
|
||||
}
|
||||
return versionNumber
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Source(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val tags: String,
|
||||
val spout: String,
|
||||
val error: String,
|
||||
val icon: String
|
||||
) {
|
||||
fun getIcon(baseUrl: String): String {
|
||||
return constructUrl(baseUrl, "favicons", icon)
|
||||
}
|
||||
|
||||
fun getTitleDecoded(): String {
|
||||
return Html.fromHtml(title).toString()
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Item(
|
||||
val id: String,
|
||||
val datetime: String,
|
||||
val title: String,
|
||||
val content: String,
|
||||
var unread: Int,
|
||||
var starred: Int,
|
||||
val thumbnail: String?,
|
||||
val icon: String?,
|
||||
val link: String,
|
||||
val sourcetitle: String,
|
||||
val tags: String
|
||||
) {
|
||||
|
||||
fun getIcon(baseUrl: String): String {
|
||||
return constructUrl(baseUrl, "favicons", icon)
|
||||
}
|
||||
|
||||
fun getThumbnail(baseUrl: String): String {
|
||||
return constructUrl(baseUrl, "thumbnails", thumbnail)
|
||||
}
|
||||
|
||||
fun getImages() : ArrayList<String> {
|
||||
val allImages = ArrayList<String>()
|
||||
|
||||
for ( image in Jsoup.parse(content).getElementsByTag("img")) {
|
||||
val url = image.attr("src")
|
||||
if (url.lowercase(US).contains(".jpg") ||
|
||||
url.lowercase(US).contains(".jpeg") ||
|
||||
url.lowercase(US).contains(".png") ||
|
||||
url.lowercase(US).contains(".webp"))
|
||||
{
|
||||
allImages.add(url)
|
||||
}
|
||||
}
|
||||
return allImages
|
||||
}
|
||||
|
||||
fun getTitleDecoded(): String {
|
||||
return Html.fromHtml(title).toString()
|
||||
}
|
||||
|
||||
fun getSourceTitle(): String {
|
||||
return Html.fromHtml(sourcetitle).toString()
|
||||
}
|
||||
|
||||
// TODO: maybe find a better way to handle these kind of urls
|
||||
fun getLinkDecoded(): String {
|
||||
var stringUrl: String
|
||||
stringUrl =
|
||||
if (link.startsWith("http://news.google.com/news/") || link.startsWith("https://news.google.com/news/")) {
|
||||
if (link.contains("&url=")) {
|
||||
link.substringAfter("&url=")
|
||||
} else {
|
||||
this.link.replace("&", "&")
|
||||
}
|
||||
} else {
|
||||
this.link.replace("&", "&")
|
||||
}
|
||||
|
||||
// handle :443 => https
|
||||
if (stringUrl.contains(":443")) {
|
||||
stringUrl = stringUrl.replace(":443", "").replace("http://", "https://")
|
||||
}
|
||||
|
||||
// handle url not starting with http
|
||||
if (stringUrl.startsWith("//")) {
|
||||
stringUrl = "http:$stringUrl"
|
||||
}
|
||||
|
||||
return stringUrl
|
||||
}
|
||||
}
|
||||
|
||||
companion object SelfossModel {
|
||||
private fun String?.isEmptyOrNullOrNullString(): Boolean =
|
||||
this == null || this == "null" || this.isEmpty()
|
||||
|
||||
fun constructUrl(baseUrl: String, path: String, file: String?): String {
|
||||
return if (file.isEmptyOrNullOrNullString()) {
|
||||
""
|
||||
} else {
|
||||
val baseUriBuilder = Uri.parse(baseUrl).buildUpon()
|
||||
baseUriBuilder.appendPath(path).appendPath(file)
|
||||
|
||||
baseUriBuilder.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package bou.amine.apps.readerforselfossv2
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class CommonGreetingTest {
|
||||
|
||||
@Test
|
||||
fun testExample() {
|
||||
assertTrue(Greeting().greeting().contains("Hello"), "Check 'Hello' is mentioned")
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package bou.amine.apps.readerforselfossv2
|
||||
|
||||
import platform.UIKit.UIDevice
|
||||
|
||||
actual class Platform actual constructor() {
|
||||
actual val platform: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package bou.amine.apps.readerforselfossv2
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class IosGreetingTest {
|
||||
|
||||
@Test
|
||||
fun testExample() {
|
||||
assertTrue(Greeting().greeting().contains("iOS"), "Check iOS is mentioned")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user