WIP. Everything isn't working yet, but api calls are made.
This commit is contained in:
@ -0,0 +1,9 @@
|
||||
package bou.amine.apps.readerforselfossv2.dao
|
||||
|
||||
interface DeviceDatabase<ItemEntity> {
|
||||
suspend fun items(): List<ItemEntity>
|
||||
suspend fun insertAllItems(vararg items: ItemEntity)
|
||||
suspend fun deleteAllItems()
|
||||
suspend fun delete(item: ItemEntity)
|
||||
suspend fun updateItem(item: ItemEntity)
|
||||
}
|
@ -1,188 +1,229 @@
|
||||
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 bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||
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.call.*
|
||||
import io.ktor.client.engine.*
|
||||
import io.ktor.client.engine.ProxyBuilder.http
|
||||
import io.ktor.client.plugins.auth.*
|
||||
import io.ktor.client.plugins.auth.providers.*
|
||||
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
|
||||
import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class SelfossApi {
|
||||
/**
|
||||
* TODO:
|
||||
* Self signed certs
|
||||
* Timeout + 408
|
||||
* Auth digest/basic
|
||||
* Loging
|
||||
*/
|
||||
class SelfossApi(private val apiDetailsService: ApiDetailsService) {
|
||||
|
||||
val baseUrl = "http://10.0.2.2:8888"
|
||||
val userName = ""
|
||||
val password = ""
|
||||
val client = HttpClient() {
|
||||
install(JsonFeature) {
|
||||
serializer = KotlinxSerializer(kotlinx.serialization.json.Json {
|
||||
private val client = HttpClient() {
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
prettyPrint = true
|
||||
isLenient = true
|
||||
ignoreUnknownKeys = true
|
||||
})
|
||||
}
|
||||
install(Logging) {
|
||||
logger = object: Logger {
|
||||
override fun log(message: String) {
|
||||
apiDetailsService.logApiCalls(message)
|
||||
}
|
||||
}
|
||||
level = LogLevel.ALL
|
||||
}
|
||||
/* TODO: Auth as basic
|
||||
if (apiDetailsService.getUserName().isNotEmpty() && apiDetailsService.getPassword().isNotEmpty()) {
|
||||
|
||||
install(Auth) {
|
||||
basic {
|
||||
credentials {
|
||||
BasicAuthCredentials(username = apiDetailsService.getUserName(), password = apiDetailsService.getPassword())
|
||||
}
|
||||
sendWithoutRequest {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
expectSuccess = false
|
||||
}
|
||||
|
||||
private fun url(path: String) =
|
||||
"$baseUrl$path"
|
||||
"${apiDetailsService.getBaseUrl()}$path"
|
||||
|
||||
|
||||
suspend fun login() =
|
||||
client.get<String>(url("/login"))// Todo: params
|
||||
suspend fun login(): SelfossModel.SuccessResponse? =
|
||||
client.get(url("/login")) {
|
||||
parameter("username", apiDetailsService.getUserName())
|
||||
parameter("password", apiDetailsService.getPassword())
|
||||
}.body()
|
||||
|
||||
suspend fun getItems(type: String,
|
||||
items: Int,
|
||||
offset: Int,
|
||||
tag: String? = "",
|
||||
source: Long? = null,
|
||||
search: String? = "",
|
||||
updatedSince: String? = ""): List<SelfossModel.Item> =
|
||||
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)
|
||||
}
|
||||
parameter("username", apiDetailsService.getUserName())
|
||||
parameter("password", apiDetailsService.getPassword())
|
||||
parameter("type", type)
|
||||
parameter("tag", tag)
|
||||
parameter("source", source)
|
||||
parameter("search", search)
|
||||
parameter("updatedsince", updatedSince)
|
||||
parameter("items", items)
|
||||
parameter("offset", offset)
|
||||
}.body()
|
||||
|
||||
suspend fun stats(): SelfossModel.Stats =
|
||||
suspend fun stats(): SelfossModel.Stats? =
|
||||
client.get(url("/stats")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
}
|
||||
parameter("username", apiDetailsService.getUserName())
|
||||
parameter("password", apiDetailsService.getPassword())
|
||||
}.body()
|
||||
|
||||
suspend fun tags(): List<SelfossModel.Tag> =
|
||||
suspend fun tags(): List<SelfossModel.Tag>? =
|
||||
client.get(url("/tags")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
}
|
||||
parameter("username", apiDetailsService.getUserName())
|
||||
parameter("password", apiDetailsService.getPassword())
|
||||
}.body()
|
||||
|
||||
suspend fun update(): String =
|
||||
suspend fun update(): SelfossModel.SuccessResponse? =
|
||||
client.get(url("/update")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
}
|
||||
parameter("username", apiDetailsService.getUserName())
|
||||
parameter("password", apiDetailsService.getPassword())
|
||||
}.body()
|
||||
|
||||
suspend fun spouts(): Map<String, SelfossModel.Spout> =
|
||||
client.get(url("/sources/spouts")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
}
|
||||
suspend fun spouts(): Map<String, SelfossModel.Spout>? =
|
||||
client.get(url("/a/spouts")) {
|
||||
parameter("username", apiDetailsService.getUserName())
|
||||
parameter("password", apiDetailsService.getPassword())
|
||||
}.body()
|
||||
|
||||
suspend fun sources(): List<SelfossModel.Source> =
|
||||
suspend fun sources(): ArrayList<SelfossModel.Source>? =
|
||||
client.get(url("/sources/list")) {
|
||||
parameter("username", userName)
|
||||
parameter("password", password)
|
||||
parameter("username", apiDetailsService.getUserName())
|
||||
parameter("password", apiDetailsService.getPassword())
|
||||
}.body()
|
||||
|
||||
suspend fun version(): SelfossModel.ApiVersion? =
|
||||
client.get(url("/api/about")).body()
|
||||
|
||||
suspend fun markAsRead(id: String): SelfossModel.SuccessResponse? =
|
||||
client.submitForm(
|
||||
url = url("/mark/$id"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", apiDetailsService.getUserName())
|
||||
append("password", apiDetailsService.getPassword())
|
||||
},
|
||||
encodeInQuery = true
|
||||
).body()
|
||||
|
||||
suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse? =
|
||||
client.submitForm(
|
||||
url = url("/unmark/$id"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", apiDetailsService.getUserName())
|
||||
append("password", apiDetailsService.getPassword())
|
||||
},
|
||||
encodeInQuery = true
|
||||
).body()
|
||||
|
||||
suspend fun starr(id: String): SelfossModel.SuccessResponse? =
|
||||
client.submitForm(
|
||||
url = url("/starr/$id"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", apiDetailsService.getUserName())
|
||||
append("password", apiDetailsService.getPassword())
|
||||
},
|
||||
encodeInQuery = true
|
||||
).body()
|
||||
|
||||
suspend fun unstarr(id: String): SelfossModel.SuccessResponse? =
|
||||
client.submitForm(
|
||||
url = url("/unstarr/$id"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", apiDetailsService.getUserName())
|
||||
append("password", apiDetailsService.getPassword())
|
||||
},
|
||||
encodeInQuery = true
|
||||
).body()
|
||||
|
||||
suspend fun markAllAsRead(ids: List<String>): SelfossModel.SuccessResponse? =
|
||||
client.submitForm(
|
||||
url = url("/mark"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", apiDetailsService.getUserName())
|
||||
append("password", apiDetailsService.getPassword())
|
||||
append("ids[]", ids.joinToString(","))
|
||||
},
|
||||
encodeInQuery = true
|
||||
).body()
|
||||
|
||||
suspend fun createSourceForVersion(
|
||||
title: String,
|
||||
url: String,
|
||||
spout: String,
|
||||
tags: String,
|
||||
filter: String,
|
||||
version: Int
|
||||
): SelfossModel.SuccessResponse? =
|
||||
if (version > 1) {
|
||||
createSource(title, url, spout, tags, filter)
|
||||
} else {
|
||||
createSource2(title, url, spout, tags, filter)
|
||||
}
|
||||
|
||||
suspend fun version(): SelfossModel.ApiVersion =
|
||||
client.get(url("/api/about"))
|
||||
|
||||
suspend fun markAsRead(id: String): SelfossModel.SuccessResponse =
|
||||
private suspend fun createSource(
|
||||
title: String,
|
||||
url: String,
|
||||
spout: String,
|
||||
tags: String,
|
||||
filter: String
|
||||
): SelfossModel.SuccessResponse? =
|
||||
client.submitForm(
|
||||
url = url("/mark/$id"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", userName)
|
||||
append("password", password)
|
||||
},
|
||||
encodeInQuery = true
|
||||
)
|
||||
url = url("/source"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", apiDetailsService.getUserName())
|
||||
append("password", apiDetailsService.getPassword())
|
||||
append("title", title)
|
||||
append("url", url)
|
||||
append("spout", spout)
|
||||
append("tags", tags)
|
||||
append("filter", filter)
|
||||
},
|
||||
encodeInQuery = true
|
||||
).body()
|
||||
|
||||
suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse =
|
||||
private suspend fun createSource2(
|
||||
title: String,
|
||||
url: String,
|
||||
spout: String,
|
||||
tags: String,
|
||||
filter: String
|
||||
): SelfossModel.SuccessResponse? =
|
||||
client.submitForm(
|
||||
url = url("/unmark/$id"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", userName)
|
||||
append("password", password)
|
||||
},
|
||||
encodeInQuery = true
|
||||
)
|
||||
url = url("/source"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", apiDetailsService.getUserName())
|
||||
append("password", apiDetailsService.getPassword())
|
||||
append("title", title)
|
||||
append("url", url)
|
||||
append("spout", spout)
|
||||
append("tags[]", tags)
|
||||
append("filter", filter)
|
||||
},
|
||||
encodeInQuery = true
|
||||
).body()
|
||||
|
||||
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)
|
||||
}
|
||||
suspend fun deleteSource(id: String): SelfossModel.SuccessResponse? =
|
||||
client.delete(url("/source/$id")) {
|
||||
parameter("username", apiDetailsService.getUserName())
|
||||
parameter("password", apiDetailsService.getPassword())
|
||||
}.body()
|
||||
}
|
@ -1,15 +1,8 @@
|
||||
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 {
|
||||
|
||||
@ -61,19 +54,11 @@ class SelfossModel {
|
||||
data class Source(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val tags: String,
|
||||
val tags: List<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(
|
||||
@ -88,81 +73,5 @@ class SelfossModel {
|
||||
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,9 @@
|
||||
package bou.amine.apps.readerforselfossv2.service
|
||||
|
||||
interface ApiDetailsService {
|
||||
fun logApiCalls(message: String)
|
||||
fun getApiVersion(): Int
|
||||
fun getBaseUrl(): String
|
||||
fun getUserName(): String
|
||||
fun getPassword(): String
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package bou.amine.apps.readerforselfossv2.service
|
||||
|
||||
import bou.amine.apps.readerforselfossv2.dao.DeviceDatabase
|
||||
import bou.amine.apps.readerforselfossv2.utils.parseDate
|
||||
import bou.amine.apps.readerforselfossv2.rest.SelfossModel
|
||||
|
||||
abstract class DeviceDataBaseService<ItemEntity>(val db: DeviceDatabase<ItemEntity>, private val searchService: SearchService) {
|
||||
var itemsCaching = false
|
||||
var items: ArrayList<SelfossModel.Item> = arrayListOf()
|
||||
get() {
|
||||
return ArrayList(field)
|
||||
}
|
||||
set(value) {
|
||||
field = ArrayList(value)
|
||||
}
|
||||
|
||||
abstract suspend fun updateDatabase()
|
||||
abstract suspend fun clearDBItems()
|
||||
abstract fun appendNewItems(items: List<SelfossModel.Item>)
|
||||
abstract fun getFromDB()
|
||||
|
||||
fun sortItems() {
|
||||
val tmpItems = ArrayList(items.sortedByDescending { it.parseDate(searchService.dateUtils) })
|
||||
items = tmpItems
|
||||
}
|
||||
|
||||
// This filtered items from items val. Do not use
|
||||
fun getFocusedItems() {}
|
||||
fun computeBadges() {
|
||||
searchService.badgeUnread = items.filter { item -> item.unread == 1 }.size
|
||||
searchService.badgeStarred = items.filter { item -> item.starred == 1 }.size
|
||||
searchService.badgeAll = items.size
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package bou.amine.apps.readerforselfossv2.service
|
||||
|
||||
import bou.amine.apps.readerforselfossv2.utils.DateUtils
|
||||
|
||||
class SearchService(val dateUtils: DateUtils) {
|
||||
var displayedItems: String = "unread"
|
||||
set(value) {
|
||||
field = when (value) {
|
||||
"all" -> "all"
|
||||
"unread" -> "unread"
|
||||
"read" -> "read"
|
||||
"starred" -> "starred"
|
||||
else -> "all"
|
||||
}
|
||||
}
|
||||
|
||||
var position = 0
|
||||
var searchFilter: String? = null
|
||||
var sourceIDFilter: Long? = null
|
||||
var sourceFilter: String? = null
|
||||
var tagFilter: String? = null
|
||||
var itemsCaching = false
|
||||
|
||||
var fetchedUnread = false
|
||||
var fetchedAll = false
|
||||
var fetchedStarred = false
|
||||
|
||||
var badgeUnread = -1
|
||||
var badgeAll = -1
|
||||
var badgeStarred = -1
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package bou.amine.apps.readerforselfossv2.service
|
||||
|
||||
|
||||
import bou.amine.apps.readerforselfossv2.rest.SelfossApi
|
||||
import bou.amine.apps.readerforselfossv2.rest.SelfossModel
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class SelfossService<ItemEntity>(val api: SelfossApi, private val dbService: DeviceDataBaseService<ItemEntity>, private val searchService: SearchService) {
|
||||
|
||||
suspend fun getAndStoreAllItems(isNetworkAvailable: Boolean) = withContext(
|
||||
Dispatchers.Default) {
|
||||
if (isNetworkAvailable) {
|
||||
launch {
|
||||
try {
|
||||
enqueueArticles(allNewItems(), true)
|
||||
} catch (e: Throwable) {}
|
||||
}
|
||||
launch {
|
||||
try {
|
||||
enqueueArticles(allReadItems(), false)
|
||||
} catch (e: Throwable) {}
|
||||
}
|
||||
launch {
|
||||
try {
|
||||
enqueueArticles(allStarredItems(), false)
|
||||
} catch (e: Throwable) {}
|
||||
}
|
||||
} else {
|
||||
launch { dbService.updateDatabase() }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun refreshFocusedItems(itemsNumber: Int, isNetworkAvailable: Boolean) = withContext(
|
||||
Dispatchers.Default) {
|
||||
if (isNetworkAvailable) {
|
||||
val response = when (searchService.displayedItems) {
|
||||
"read" -> readItems(itemsNumber, 0)
|
||||
"unread" -> newItems(itemsNumber, 0)
|
||||
"starred" -> starredItems(itemsNumber, 0)
|
||||
else -> readItems(itemsNumber, 0)
|
||||
}
|
||||
|
||||
if (response != null) {
|
||||
// TODO:
|
||||
// dbService.refreshFocusedItems(response.body() as ArrayList<SelfossModel.Item>)
|
||||
dbService.updateDatabase()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getReadItems(itemsNumber: Int, offset: Int, isNetworkAvailable: Boolean) = withContext(
|
||||
Dispatchers.Default) {
|
||||
if (isNetworkAvailable) {
|
||||
try {
|
||||
enqueueArticles(readItems( itemsNumber, offset), false)
|
||||
searchService.fetchedAll = true
|
||||
dbService.updateDatabase()
|
||||
} catch (e: Throwable) {}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getUnreadItems(itemsNumber: Int, offset: Int, isNetworkAvailable: Boolean) = withContext(
|
||||
Dispatchers.Default) {
|
||||
if (isNetworkAvailable) {
|
||||
try {
|
||||
if (!searchService.fetchedUnread) {
|
||||
dbService.clearDBItems()
|
||||
}
|
||||
enqueueArticles(newItems(itemsNumber, offset), false)
|
||||
searchService.fetchedUnread = true
|
||||
} catch (e: Throwable) {}
|
||||
}
|
||||
dbService.updateDatabase()
|
||||
}
|
||||
|
||||
suspend fun getStarredItems(itemsNumber: Int, offset: Int, isNetworkAvailable: Boolean) = withContext(
|
||||
Dispatchers.Default) {
|
||||
if (isNetworkAvailable) {
|
||||
try {
|
||||
enqueueArticles(starredItems(itemsNumber, offset), false)
|
||||
searchService.fetchedStarred = true
|
||||
dbService.updateDatabase()
|
||||
} catch (e: Throwable) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun readAll(isNetworkAvailable: Boolean): Boolean {
|
||||
var success = false
|
||||
if (isNetworkAvailable) {
|
||||
// Do api call to read all
|
||||
} else {
|
||||
// Do db call to read all
|
||||
}
|
||||
// refresh view
|
||||
return success
|
||||
}
|
||||
|
||||
suspend fun reloadBadges(isNetworkAvailable: Boolean) = withContext(Dispatchers.Default) {
|
||||
if (isNetworkAvailable) {
|
||||
try {
|
||||
val response = api.stats()
|
||||
|
||||
if (response != null) {
|
||||
searchService.badgeUnread = response.unread
|
||||
searchService.badgeAll = response.total
|
||||
searchService.badgeStarred = response.starred
|
||||
}
|
||||
} catch (e: Throwable) {}
|
||||
} else {
|
||||
dbService.computeBadges()
|
||||
}
|
||||
}
|
||||
|
||||
private fun enqueueArticles(response: List<SelfossModel.Item>?, clearDatabase: Boolean) {
|
||||
if (response != null) {
|
||||
if (clearDatabase) {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
dbService.clearDBItems()
|
||||
}
|
||||
}
|
||||
dbService.appendNewItems(response)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun allNewItems(): List<SelfossModel.Item>? =
|
||||
readItems(200, 0)
|
||||
|
||||
private suspend fun allReadItems(): List<SelfossModel.Item>? =
|
||||
newItems(200, 0)
|
||||
|
||||
private suspend fun allStarredItems(): List<SelfossModel.Item>? =
|
||||
starredItems(200, 0)
|
||||
|
||||
private suspend fun readItems(
|
||||
itemsNumber: Int,
|
||||
offset: Int
|
||||
): List<SelfossModel.Item>? =
|
||||
api.getItems("read", itemsNumber, offset, searchService.tagFilter, searchService.sourceIDFilter, searchService.searchFilter)
|
||||
|
||||
private suspend fun newItems(
|
||||
itemsNumber: Int,
|
||||
offset: Int
|
||||
): List<SelfossModel.Item>? =
|
||||
api.getItems("unread", itemsNumber, offset, searchService.tagFilter, searchService.sourceIDFilter, searchService.searchFilter)
|
||||
|
||||
private suspend fun starredItems(
|
||||
itemsNumber: Int,
|
||||
offset: Int
|
||||
): List<SelfossModel.Item>? =
|
||||
api.getItems("starred", itemsNumber, offset, searchService.tagFilter, searchService.sourceIDFilter, searchService.searchFilter)
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package bou.amine.apps.readerforselfossv2.utils
|
||||
|
||||
import android.text.format.DateUtils
|
||||
import bou.amine.apps.readerforselfossv2.rest.SelfossModel
|
||||
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
fun SelfossModel.Item.parseDate(dateUtils: bou.amine.apps.readerforselfossv2.utils.DateUtils): Instant =
|
||||
dateUtils.parseDate(this.datetime)
|
||||
|
||||
fun SelfossModel.Item.parseRelativeDate(dateUtils: bou.amine.apps.readerforselfossv2.utils.DateUtils): String =
|
||||
dateUtils.parseRelativeDate(this.datetime)
|
||||
|
||||
class DateUtils(private val apiDetailsService: ApiDetailsService) {
|
||||
fun parseDate(dateString: String): Instant {
|
||||
|
||||
val FORMATTERV1 = "yyyy-MM-dd HH:mm:ss"
|
||||
|
||||
return if (apiDetailsService.getApiVersion() >= 4) {
|
||||
OffsetDateTime.parse(dateString).toInstant()
|
||||
} else {
|
||||
LocalDateTime.parse(dateString, DateTimeFormatter.ofPattern(FORMATTERV1)).toInstant(ZoneOffset.UTC)
|
||||
}
|
||||
}
|
||||
|
||||
fun parseRelativeDate(dateString: String): String {
|
||||
|
||||
val date = parseDate(dateString)
|
||||
|
||||
return " " + DateUtils.getRelativeTimeSpanString(
|
||||
date.toEpochMilli(),
|
||||
Instant.now().toEpochMilli(),
|
||||
60000L, // DateUtils.MINUTE_IN_MILLIS,
|
||||
262144 // DateUtils.FORMAT_ABBREV_RELATIVE
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package bou.amine.apps.readerforselfossv2.utils
|
||||
|
||||
fun String?.isEmptyOrNullOrNullString(): Boolean =
|
||||
this == null || this == "null" || this.isEmpty()
|
||||
|
||||
fun String.longHash(): Long {
|
||||
var h = 98764321261L
|
||||
val l = this.length
|
||||
val chars = this.toCharArray()
|
||||
|
||||
for (i in 0 until l) {
|
||||
h = 31 * h + chars[i].code.toLong()
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
fun String.toStringUriWithHttp(): String =
|
||||
if (!this.startsWith("https://") && !this.startsWith("http://")) {
|
||||
"http://" + this
|
||||
} else {
|
||||
this
|
||||
}
|
Reference in New Issue
Block a user