Compare commits
23 Commits
v123030681
...
v123051471
Author | SHA1 | Date | |
---|---|---|---|
7f96798f13 | |||
6e5704a45b | |||
495591159f | |||
718fe7c5ee | |||
ecd23213f9 | |||
e6baed8cb4 | |||
c87abec0b9 | |||
0aba41d8bf | |||
2a2d1047b4 | |||
66ef1ccf32 | |||
677ede5bc7 | |||
996a7ed22c | |||
85208c4e5a | |||
5cfec50cba | |||
76ad71e1dc | |||
0277fb507c | |||
8d7d3174aa | |||
00eb3333fe | |||
629ca01d99 | |||
c2d8681ce8 | |||
08f79cb148 | |||
e21906e70d | |||
9d2cc32bc9 |
21
.drone.yml
21
.drone.yml
@ -137,6 +137,27 @@ steps:
|
|||||||
from_secret: giteaAPI
|
from_secret: giteaAPI
|
||||||
base_url: https://gitea.amine-louveau.fr
|
base_url: https://gitea.amine-louveau.fr
|
||||||
files: signed.apk
|
files: signed.apk
|
||||||
|
|
||||||
|
- name: notify
|
||||||
|
image: drillster/drone-email
|
||||||
|
failure: ignore
|
||||||
|
settings:
|
||||||
|
host:
|
||||||
|
from_secret: smtpHOST
|
||||||
|
port:
|
||||||
|
from_secret: smtpPORT
|
||||||
|
username:
|
||||||
|
from_secret: smtpUSERNAME
|
||||||
|
password:
|
||||||
|
from_secret: smtpPASSWORD
|
||||||
|
from:
|
||||||
|
from_secret: smtpFROM
|
||||||
|
subject: Mapping file
|
||||||
|
recipients:
|
||||||
|
from_secret: smtpTO
|
||||||
|
recipients_only: true
|
||||||
|
skip_verify: true
|
||||||
|
attachment: androidApp/build/outputs/mapping/githubConfigRelease/mapping.txt
|
||||||
trigger:
|
trigger:
|
||||||
event:
|
event:
|
||||||
- tag
|
- tag
|
62
CHANGELOG.md
62
CHANGELOG.md
@ -1,3 +1,65 @@
|
|||||||
|
**v123051331**
|
||||||
|
|
||||||
|
- fix: illegal input.
|
||||||
|
- Changelog for v123051321 [CI SKIP]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
**v123051321**
|
||||||
|
|
||||||
|
- debug: Debug null context.
|
||||||
|
- Changelog for v123051301 [CI SKIP]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
**v123051301**
|
||||||
|
|
||||||
|
- feat: Basic auth from url. Fixes #142 (#143)
|
||||||
|
- debug: Debug index out of bound exception.
|
||||||
|
- Changelog for v123051211 [CI SKIP]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
**v123051211**
|
||||||
|
|
||||||
|
- fix: Sometimes url isn't even defined.
|
||||||
|
- Changelog for v123041021 [CI SKIP]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
**v123041021**
|
||||||
|
|
||||||
|
- fix: 'Enable Core Library Desugaring to support older Android versions' (#138) from davidoskky/ReaderForSelfoss-multiplatform:desugaring into master
|
||||||
|
- Enable Core Library Desugaring to support older Android versions
|
||||||
|
- Changelog for v123030851 [CI SKIP]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
**v123030851**
|
||||||
|
|
||||||
|
- chore: replace textDrawable library (#136)
|
||||||
|
- refactor: Remove slow login check. Closes #135.
|
||||||
|
- ci: send the mapping file after a release.
|
||||||
|
- Changelog for v123030751 [CI SKIP]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
**v123030751**
|
||||||
|
|
||||||
|
- debug: added a lot to pinpoint the url issue.
|
||||||
|
- feat: Use /sources/stats in the home (#133)
|
||||||
|
- Changelog for v123030681 [CI SKIP]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
**v123030681**
|
||||||
|
|
||||||
|
- fix: Unread and starred can be null.
|
||||||
|
- Fixed version number issue.
|
||||||
|
- Changelog for v123030621 [CI SKIP]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
**v123030621**
|
**v123030621**
|
||||||
|
|
||||||
- fix: url required issue.
|
- fix: url required issue.
|
||||||
|
@ -56,6 +56,7 @@ fun versionNameFromGit(): String {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
// Flag to enable support for the new language APIs
|
// Flag to enable support for the new language APIs
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
@ -112,6 +113,8 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
|
||||||
|
|
||||||
implementation(project(":shared"))
|
implementation(project(":shared"))
|
||||||
implementation("com.google.android.material:material:1.5.0")
|
implementation("com.google.android.material:material:1.5.0")
|
||||||
implementation("androidx.appcompat:appcompat:1.4.1")
|
implementation("androidx.appcompat:appcompat:1.4.1")
|
||||||
@ -142,7 +145,6 @@ dependencies {
|
|||||||
|
|
||||||
// Material-ish things
|
// Material-ish things
|
||||||
implementation("com.ashokvarma.android:bottom-navigation-bar:2.2.0")
|
implementation("com.ashokvarma.android:bottom-navigation-bar:2.2.0")
|
||||||
implementation("com.amulyakhare:com.amulyakhare.textdrawable:1.0.1")
|
|
||||||
|
|
||||||
// glide
|
// glide
|
||||||
kapt("com.github.bumptech.glide:compiler:4.15.0")
|
kapt("com.github.bumptech.glide:compiler:4.15.0")
|
||||||
@ -174,7 +176,7 @@ dependencies {
|
|||||||
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
|
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
|
||||||
|
|
||||||
// Network information
|
// Network information
|
||||||
implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0")
|
implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0")
|
||||||
|
|
||||||
// SQLDELIGHT
|
// SQLDELIGHT
|
||||||
implementation("com.squareup.sqldelight:android-driver:1.5.4")
|
implementation("com.squareup.sqldelight:android-driver:1.5.4")
|
||||||
|
@ -57,29 +57,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
|||||||
|
|
||||||
if (appSettingsService.getBaseUrl().isNotEmpty()) {
|
if (appSettingsService.getBaseUrl().isNotEmpty()) {
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
// This should be reverted when "old" users connected with a non-selfoss rss
|
goToMain()
|
||||||
// are handled. Revert to "simple" way.
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
|
||||||
try {
|
|
||||||
val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance()
|
|
||||||
if (!errorFetching && !displaySelfossOnly) {
|
|
||||||
goToMain()
|
|
||||||
} else {
|
|
||||||
showProgress(false)
|
|
||||||
if (displaySelfossOnly) {
|
|
||||||
Toast.makeText(
|
|
||||||
applicationContext,
|
|
||||||
R.string.application_selfoss_only,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
repository.logout()
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
repository.logout()
|
|
||||||
showProgress(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleActions()
|
handleActions()
|
||||||
|
@ -71,7 +71,11 @@ class ReaderActivity : AppCompatActivity(), DIAware {
|
|||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
readItem(allItems[currentItem])
|
try {
|
||||||
|
readItem(allItems[currentItem])
|
||||||
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
binding.pager.adapter = ScreenSlidePagerAdapter(this)
|
binding.pager.adapter = ScreenSlidePagerAdapter(this)
|
||||||
binding.pager.setCurrentItem(currentItem, false)
|
binding.pager.setCurrentItem(currentItem, false)
|
||||||
|
@ -49,13 +49,13 @@ class SourcesActivity : AppCompatActivity(), DIAware {
|
|||||||
super.onResume()
|
super.onResume()
|
||||||
val mLayoutManager = LinearLayoutManager(this)
|
val mLayoutManager = LinearLayoutManager(this)
|
||||||
|
|
||||||
var items: ArrayList<SelfossModel.Source>
|
var items: ArrayList<SelfossModel.SourceDetail>
|
||||||
|
|
||||||
binding.recyclerView.setHasFixedSize(true)
|
binding.recyclerView.setHasFixedSize(true)
|
||||||
binding.recyclerView.layoutManager = mLayoutManager
|
binding.recyclerView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val response = repository.getSources()
|
val response = repository.getSourcesDetails()
|
||||||
if (response.isNotEmpty()) {
|
if (response.isNotEmpty()) {
|
||||||
items = response
|
items = response
|
||||||
val mAdapter = SourcesListAdapter(
|
val mAdapter = SourcesListAdapter(
|
||||||
|
@ -24,7 +24,7 @@ import org.kodein.di.instance
|
|||||||
|
|
||||||
class UpsertSourceActivity : AppCompatActivity(), DIAware {
|
class UpsertSourceActivity : AppCompatActivity(), DIAware {
|
||||||
|
|
||||||
private var existingSource: SelfossModel.Source? = null
|
private var existingSource: SelfossModel.SourceDetail? = null
|
||||||
private var mSpoutsValue: String? = null
|
private var mSpoutsValue: String? = null
|
||||||
|
|
||||||
private lateinit var binding: ActivityUpsertSourceBinding
|
private lateinit var binding: ActivityUpsertSourceBinding
|
||||||
@ -68,7 +68,7 @@ class UpsertSourceActivity : AppCompatActivity(), DIAware {
|
|||||||
|
|
||||||
private fun initFields(items: Map<String, SelfossModel.Spout>) {
|
private fun initFields(items: Map<String, SelfossModel.Spout>) {
|
||||||
binding.nameInput.setText(existingSource!!.title)
|
binding.nameInput.setText(existingSource!!.title)
|
||||||
binding.tags.setText(existingSource!!.tags.joinToString(", "))
|
binding.tags.setText(existingSource!!.tags?.joinToString(", "))
|
||||||
binding.sourceUri.setText(existingSource!!.params?.url)
|
binding.sourceUri.setText(existingSource!!.params?.url)
|
||||||
binding.spoutsSpinner.setSelection(items.keys.indexOf(existingSource!!.spout))
|
binding.spoutsSpinner.setSelection(items.keys.indexOf(existingSource!!.spout))
|
||||||
binding.progress.visibility = View.GONE
|
binding.progress.visibility = View.GONE
|
||||||
|
@ -9,10 +9,9 @@ import android.widget.ImageView.ScaleType
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
|
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask
|
import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
|
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.shareLink
|
import bou.amine.apps.readerforselfossv2.android.utils.shareLink
|
||||||
@ -22,8 +21,6 @@ import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
|||||||
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getThumbnail
|
import bou.amine.apps.readerforselfossv2.utils.getThumbnail
|
||||||
import com.amulyakhare.textdrawable.TextDrawable
|
|
||||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -38,7 +35,6 @@ class ItemCardAdapter(
|
|||||||
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
||||||
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
|
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
|
||||||
private val c: Context = app.baseContext
|
private val c: Context = app.baseContext
|
||||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
|
||||||
private val imageMaxHeight: Int =
|
private val imageMaxHeight: Int =
|
||||||
c.resources.getDimension(R.dimen.card_image_max_height).toInt()
|
c.resources.getDimension(R.dimen.card_image_max_height).toInt()
|
||||||
|
|
||||||
@ -83,16 +79,9 @@ class ItemCardAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
||||||
val color = generator.getColor(itm.title.getHtmlDecoded())
|
binding.sourceImage.setBackgroundAndText(itm.sourcetitle.getHtmlDecoded())
|
||||||
|
|
||||||
val drawable =
|
|
||||||
TextDrawable
|
|
||||||
.builder()
|
|
||||||
.round()
|
|
||||||
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
|
|
||||||
binding.sourceImage.setImageDrawable(drawable)
|
|
||||||
} else {
|
} else {
|
||||||
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.sourceImage)
|
c.circularDrawable(itm.getIcon(repository.baseUrl), binding.sourceImage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,8 @@ import android.view.ViewGroup
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
|
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
|
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
@ -18,8 +16,6 @@ import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
|||||||
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getThumbnail
|
import bou.amine.apps.readerforselfossv2.utils.getThumbnail
|
||||||
import com.amulyakhare.textdrawable.TextDrawable
|
|
||||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.DI
|
||||||
import org.kodein.di.android.closestDI
|
import org.kodein.di.android.closestDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
@ -29,7 +25,6 @@ class ItemListAdapter(
|
|||||||
override var items: ArrayList<SelfossModel.Item>,
|
override var items: ArrayList<SelfossModel.Item>,
|
||||||
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
||||||
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
|
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
|
||||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
|
||||||
private val c: Context = app.baseContext
|
private val c: Context = app.baseContext
|
||||||
|
|
||||||
override val di: DI by closestDI(app)
|
override val di: DI by closestDI(app)
|
||||||
@ -56,20 +51,12 @@ class ItemListAdapter(
|
|||||||
if (itm.getThumbnail(repository.baseUrl).isEmpty()) {
|
if (itm.getThumbnail(repository.baseUrl).isEmpty()) {
|
||||||
|
|
||||||
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
||||||
val color = generator.getColor(itm.title.getHtmlDecoded())
|
binding.itemImage.setBackgroundAndText(itm.sourcetitle.getHtmlDecoded())
|
||||||
|
|
||||||
val drawable =
|
|
||||||
TextDrawable
|
|
||||||
.builder()
|
|
||||||
.round()
|
|
||||||
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
|
|
||||||
|
|
||||||
binding.itemImage.setImageDrawable(drawable)
|
|
||||||
} else {
|
} else {
|
||||||
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.itemImage)
|
c.circularDrawable(itm.getIcon(repository.baseUrl), binding.itemImage)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.bitmapCenterCrop(itm.getThumbnail(repository.baseUrl), binding.itemImage)
|
c.circularDrawable(itm.getThumbnail(repository.baseUrl), binding.itemImage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,17 +10,14 @@ import android.widget.Button
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import bou.amine.apps.readerforselfossv2.android.UpsertSourceActivity
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
|
import bou.amine.apps.readerforselfossv2.android.UpsertSourceActivity
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.SourceListItemBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.SourceListItemBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
|
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
import bou.amine.apps.readerforselfossv2.utils.getIcon
|
||||||
import com.amulyakhare.textdrawable.TextDrawable
|
|
||||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -31,10 +28,9 @@ import org.kodein.di.instance
|
|||||||
|
|
||||||
class SourcesListAdapter(
|
class SourcesListAdapter(
|
||||||
private val app: Activity,
|
private val app: Activity,
|
||||||
private val items: ArrayList<SelfossModel.Source>
|
private val items: ArrayList<SelfossModel.SourceDetail>
|
||||||
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>(), DIAware {
|
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>(), DIAware {
|
||||||
private val c: Context = app.baseContext
|
private val c: Context = app.baseContext
|
||||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
|
||||||
private lateinit var binding: SourceListItemBinding
|
private lateinit var binding: SourceListItemBinding
|
||||||
|
|
||||||
override val di: DI by closestDI(app)
|
override val di: DI by closestDI(app)
|
||||||
@ -49,19 +45,12 @@ class SourcesListAdapter(
|
|||||||
val itm = items[position]
|
val itm = items[position]
|
||||||
|
|
||||||
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
||||||
val color = generator.getColor(itm.title.getHtmlDecoded())
|
binding.itemImage.setBackgroundAndText(itm.title.getHtmlDecoded())
|
||||||
|
|
||||||
val drawable =
|
|
||||||
TextDrawable
|
|
||||||
.builder()
|
|
||||||
.round()
|
|
||||||
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
|
|
||||||
binding.itemImage.setImageDrawable(drawable)
|
|
||||||
} else {
|
} else {
|
||||||
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.itemImage)
|
c.circularDrawable(itm.getIcon(repository.baseUrl), binding.itemImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itm.error.isNotBlank()) {
|
if (!itm.error.isNullOrBlank()) {
|
||||||
binding.errorText.visibility = View.VISIBLE
|
binding.errorText.visibility = View.VISIBLE
|
||||||
binding.errorText.text = itm.error
|
binding.errorText.text = itm.error
|
||||||
} else {
|
} else {
|
||||||
|
@ -31,7 +31,6 @@ import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask
|
|||||||
import bou.amine.apps.readerforselfossv2.android.utils.shareLink
|
import bou.amine.apps.readerforselfossv2.android.utils.shareLink
|
||||||
import bou.amine.apps.readerforselfossv2.model.MercuryModel
|
import bou.amine.apps.readerforselfossv2.model.MercuryModel
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.model.StatusAndData
|
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.rest.MercuryApi
|
import bou.amine.apps.readerforselfossv2.rest.MercuryApi
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
@ -260,21 +259,8 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
try {
|
try {
|
||||||
val response = mercuryApi.query(url)
|
val response = mercuryApi.query(url)
|
||||||
if (response.success && response.data != null && !response.data?.content.isNullOrEmpty()) {
|
if (response.success && response.data != null) {
|
||||||
binding.titleView.text = response.data!!.title.orEmpty()
|
handleMercuryData(response.data!!)
|
||||||
if (typeface != null) {
|
|
||||||
binding.titleView.typeface = typeface
|
|
||||||
}
|
|
||||||
URL(response.data!!.url)
|
|
||||||
url = response.data!!.url
|
|
||||||
|
|
||||||
contentText = response.data!!.content.orEmpty()
|
|
||||||
htmlToWebview()
|
|
||||||
|
|
||||||
handleLeadImage(response)
|
|
||||||
|
|
||||||
binding.nestedScrollView.scrollTo(0, 0)
|
|
||||||
binding.progressBar.visibility = View.GONE
|
|
||||||
} else {
|
} else {
|
||||||
openInBrowserAfterFailing()
|
openInBrowserAfterFailing()
|
||||||
}
|
}
|
||||||
@ -287,14 +273,35 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleLeadImage(response: StatusAndData<MercuryModel.ParsedContent>) {
|
private fun handleMercuryData(data: MercuryModel.ParsedContent) {
|
||||||
if (!response.data?.lead_image_url.isNullOrEmpty() && context != null) {
|
if (data.error == true || data.failed == true) {
|
||||||
|
openInBrowserAfterFailing()
|
||||||
|
} else {
|
||||||
|
binding.titleView.text = data.title.orEmpty()
|
||||||
|
if (typeface != null) {
|
||||||
|
binding.titleView.typeface = typeface
|
||||||
|
}
|
||||||
|
URL(data.url)
|
||||||
|
url = data.url!!
|
||||||
|
|
||||||
|
contentText = data.content.orEmpty()
|
||||||
|
htmlToWebview()
|
||||||
|
|
||||||
|
handleLeadImage(data?.lead_image_url)
|
||||||
|
|
||||||
|
binding.nestedScrollView.scrollTo(0, 0)
|
||||||
|
binding.progressBar.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleLeadImage(lead_image_url: String?) {
|
||||||
|
if (!lead_image_url.isNullOrEmpty() && context != null) {
|
||||||
binding.imageView.visibility = View.VISIBLE
|
binding.imageView.visibility = View.VISIBLE
|
||||||
Glide
|
Glide
|
||||||
.with(requireContext())
|
.with(requireContext())
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.load(
|
.load(
|
||||||
response.data!!.lead_image_url.orEmpty()
|
lead_image_url
|
||||||
)
|
)
|
||||||
.apply(RequestOptions.fitCenterTransform())
|
.apply(RequestOptions.fitCenterTransform())
|
||||||
.into(binding.imageView)
|
.into(binding.imageView)
|
||||||
@ -406,7 +413,7 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
val itemUrl = URL(url)
|
val itemUrl = URL(url)
|
||||||
baseUrl = itemUrl.protocol + "://" + itemUrl.host
|
baseUrl = itemUrl.protocol + "://" + itemUrl.host
|
||||||
} catch (e: MalformedURLException) {
|
} catch (e: MalformedURLException) {
|
||||||
e.sendSilentlyWithAcraWithName("htmlToWebview > item url")
|
e.sendSilentlyWithAcraWithName("htmlToWebview > $url")
|
||||||
}
|
}
|
||||||
|
|
||||||
val fontName = when (font) {
|
val fontName = when (font) {
|
||||||
@ -523,7 +530,12 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
|
|
||||||
private fun openInBrowserAfterFailing() {
|
private fun openInBrowserAfterFailing() {
|
||||||
binding.progressBar.visibility = View.GONE
|
binding.progressBar.visibility = View.GONE
|
||||||
requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item)
|
if (context != null) {
|
||||||
|
requireContext().openInBrowserAsNewTask(this@ArticleFragment.item)
|
||||||
|
} else {
|
||||||
|
Exception("openInBrowserAfterFailing context is null").sendSilentlyWithAcraWithName("openInBrowserAfterFailing > $context")
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -541,8 +553,8 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun performClick(): Boolean {
|
fun performClick(): Boolean {
|
||||||
if (binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
|
if (allImages != null && (binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
|
||||||
binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
|
binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val position: Int = allImages.indexOf(binding.webcontent.hitTestResult.extra)
|
val position: Int = allImages.indexOf(binding.webcontent.hitTestResult.extra)
|
||||||
|
@ -82,7 +82,7 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
|
|||||||
) {
|
) {
|
||||||
val sourceGroup = binding.sourcesGroup
|
val sourceGroup = binding.sourcesGroup
|
||||||
|
|
||||||
repository.getSources().forEach { source ->
|
repository.getSourcesDetailsOrStats().forEach { source ->
|
||||||
val c = Chip(context)
|
val c = Chip(context)
|
||||||
c.ellipsize = TextUtils.TruncateAt.END
|
c.ellipsize = TextUtils.TruncateAt.END
|
||||||
|
|
||||||
@ -127,9 +127,9 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
|
|||||||
selectedChip = c
|
selectedChip = c
|
||||||
}
|
}
|
||||||
|
|
||||||
c.isEnabled = source.error.isBlank()
|
c.isEnabled = source.error.isNullOrBlank()
|
||||||
|
|
||||||
if (source.error.isNotBlank() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (!source.error.isNullOrBlank() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
c.tooltipText = source.error
|
c.tooltipText = source.error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,21 +149,23 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
|
|||||||
c.ellipsize = TextUtils.TruncateAt.END
|
c.ellipsize = TextUtils.TruncateAt.END
|
||||||
c.text = tag.tag
|
c.text = tag.tag
|
||||||
|
|
||||||
try {
|
if (tag.color.isNotEmpty()) {
|
||||||
val gd = GradientDrawable()
|
try {
|
||||||
val gdColor = try {
|
val gd = GradientDrawable()
|
||||||
Color.parseColor(tag.color)
|
val gdColor = try {
|
||||||
} catch (e: IllegalArgumentException) {
|
Color.parseColor(tag.color)
|
||||||
e.sendSilentlyWithAcraWithName("color issue " + tag.color)
|
} catch (e: IllegalArgumentException) {
|
||||||
resources.getColor(R.color.colorPrimary)
|
e.sendSilentlyWithAcraWithName("color issue " + tag.color)
|
||||||
|
resources.getColor(R.color.colorPrimary)
|
||||||
|
}
|
||||||
|
gd.setColor(gdColor)
|
||||||
|
gd.shape = GradientDrawable.RECTANGLE
|
||||||
|
gd.setSize(30, 30)
|
||||||
|
gd.cornerRadius = 30F
|
||||||
|
c.chipIcon = gd
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.sendSilentlyWithAcraWithName("tags > GradientDrawable")
|
||||||
}
|
}
|
||||||
gd.setColor(gdColor)
|
|
||||||
gd.shape = GradientDrawable.RECTANGLE
|
|
||||||
gd.setSize(30, 30)
|
|
||||||
gd.cornerRadius = 30F
|
|
||||||
c.chipIcon = gd
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.sendSilentlyWithAcraWithName("tags > GradientDrawable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setOnCloseIconClickListener {
|
c.setOnCloseIconClickListener {
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
package bou.amine.apps.readerforselfossv2.android.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.drawable.GradientDrawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
|
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
|
||||||
|
import com.google.android.material.imageview.ShapeableImageView
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
class CircleImageView @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : RelativeLayout(context, attrs, defStyleAttr) {
|
||||||
|
val view: View
|
||||||
|
val imageView: ShapeableImageView
|
||||||
|
val textView: TextView
|
||||||
|
|
||||||
|
private val colorScheme = listOf(
|
||||||
|
-0x1a8c8d,
|
||||||
|
-0xf9d6e,
|
||||||
|
-0x459738,
|
||||||
|
-0x6a8a33,
|
||||||
|
-0x867935,
|
||||||
|
-0x9b4a0a,
|
||||||
|
-0xb03c09,
|
||||||
|
-0xb22f1f,
|
||||||
|
-0xb24954,
|
||||||
|
-0x7e387c,
|
||||||
|
-0x512a7f,
|
||||||
|
-0x759b,
|
||||||
|
-0x2b1ea9,
|
||||||
|
-0x2ab1,
|
||||||
|
-0x48b3,
|
||||||
|
-0x5e7781,
|
||||||
|
-0x6f5b52
|
||||||
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
view = LayoutInflater.from(context).inflate(R.layout.circle_image_view, this, true)
|
||||||
|
imageView = view.findViewById(R.id.circleImage)
|
||||||
|
textView = view.findViewById(R.id.circleText)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBackgroundAndText(text: String) {
|
||||||
|
val circleDrawable = GradientDrawable()
|
||||||
|
val color = colorFromIdentifier(text)
|
||||||
|
circleDrawable.setColor(color)
|
||||||
|
imageView.setImageDrawable(circleDrawable)
|
||||||
|
|
||||||
|
textView.text = text.toTextDrawableString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun colorFromIdentifier(key: String): Int {
|
||||||
|
return colorScheme[abs(key.hashCode()) % colorScheme.size]
|
||||||
|
}
|
||||||
|
}
|
@ -3,10 +3,9 @@ package bou.amine.apps.readerforselfossv2.android.utils.glide
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
import bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.bumptech.glide.request.target.BitmapImageViewTarget
|
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@ -18,21 +17,13 @@ fun Context.bitmapCenterCrop(url: String, iv: ImageView) =
|
|||||||
.apply(RequestOptions.centerCropTransform())
|
.apply(RequestOptions.centerCropTransform())
|
||||||
.into(iv)
|
.into(iv)
|
||||||
|
|
||||||
fun Context.circularBitmapDrawable(url: String, iv: ImageView) =
|
fun Context.circularDrawable(url: String, view: CircleImageView) {
|
||||||
|
view.textView.text =""
|
||||||
|
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.asBitmap()
|
|
||||||
.load(url)
|
.load(url)
|
||||||
.apply(RequestOptions.centerCropTransform())
|
.into(view.imageView)
|
||||||
.into(object : BitmapImageViewTarget(iv) {
|
}
|
||||||
override fun setResource(resource: Bitmap?) {
|
|
||||||
val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(
|
|
||||||
resources,
|
|
||||||
resource
|
|
||||||
)
|
|
||||||
circularBitmapDrawable.isCircular = true
|
|
||||||
iv.setImageDrawable(circularBitmapDrawable)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
fun getBitmapInputStream(bitmap:Bitmap,compressFormat: Bitmap.CompressFormat): InputStream {
|
fun getBitmapInputStream(bitmap:Bitmap,compressFormat: Bitmap.CompressFormat): InputStream {
|
||||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/itemImage">
|
app:layout_constraintTop_toBottomOf="@+id/itemImage">
|
||||||
|
|
||||||
<ImageView
|
<bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
|
||||||
android:id="@+id/sourceImage"
|
android:id="@+id/sourceImage"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
|
26
androidApp/src/main/res/layout/circle_image_view.xml
Normal file
26
androidApp/src/main/res/layout/circle_image_view.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/circleImage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:shapeAppearanceOverlay="@style/circleImageView"
|
||||||
|
app:srcCompat="@drawable/background_splash" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/circleText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:gravity="center"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:typeface="normal" />
|
||||||
|
</RelativeLayout>
|
@ -5,7 +5,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<ImageView
|
<bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
|
||||||
android:id="@+id/itemImage"
|
android:id="@+id/itemImage"
|
||||||
android:layout_width="46dp"
|
android:layout_width="46dp"
|
||||||
android:layout_height="46dp"
|
android:layout_height="46dp"
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintVertical_bias="0.0" />
|
app:layout_constraintVertical_bias="0.0" />
|
||||||
|
|
||||||
<ImageView
|
<bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
|
||||||
android:id="@+id/itemImage"
|
android:id="@+id/itemImage"
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
|
@ -32,4 +32,10 @@
|
|||||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||||
<item name="android:windowIsTranslucent">true</item>
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="circleImageView" parent="">
|
||||||
|
<item name="cornerFamily">rounded</item>
|
||||||
|
<item name="cornerSize">50%</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -300,9 +300,10 @@ class RepositoryTest {
|
|||||||
every { appSettingsService.isItemCachingEnabled() } returns true
|
every { appSettingsService.isItemCachingEnabled() } returns true
|
||||||
|
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
repository.setSourceFilter(SelfossModel.Source(
|
repository.setSourceFilter(SelfossModel.SourceDetail(
|
||||||
1,
|
1,
|
||||||
"Test",
|
"Test",
|
||||||
|
null,
|
||||||
listOf("tags"),
|
listOf("tags"),
|
||||||
SPOUT,
|
SPOUT,
|
||||||
"",
|
"",
|
||||||
@ -609,30 +610,32 @@ class RepositoryTest {
|
|||||||
fun get_sources() {
|
fun get_sources() {
|
||||||
val (sources, sourcesDB) = prepareSources()
|
val (sources, sourcesDB) = prepareSources()
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
var testSources: List<SelfossModel.Source>?
|
var testSources: List<SelfossModel.Source>
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSources()
|
testSources = repository.getSourcesDetails()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertSame(sources, testSources)
|
assertEquals(sources, testSources)
|
||||||
assertNotEquals(sourcesDB.map { it.toView() }, testSources)
|
assertNotEquals(sourcesDB.map { it.toView() }, testSources)
|
||||||
coVerify(exactly = 1) { api.sources() }
|
coVerify(exactly = 1) { api.sourcesDetailed() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareSources(): Pair<ArrayList<SelfossModel.Source>, List<SOURCE>> {
|
private fun prepareSources(): Pair<ArrayList<SelfossModel.SourceDetail>, List<SOURCE>> {
|
||||||
val sources = arrayListOf(
|
val sources = arrayListOf(
|
||||||
SelfossModel.Source(
|
SelfossModel.SourceDetail(
|
||||||
1,
|
1,
|
||||||
"First source",
|
"First source",
|
||||||
|
null,
|
||||||
listOf("Test", "second"),
|
listOf("Test", "second"),
|
||||||
SPOUT,
|
SPOUT,
|
||||||
"",
|
"",
|
||||||
IMAGE_URL_2,
|
IMAGE_URL_2,
|
||||||
SelfossModel.SourceParams("url")
|
SelfossModel.SourceParams("url")
|
||||||
),
|
),
|
||||||
SelfossModel.Source(
|
SelfossModel.SourceDetail(
|
||||||
2,
|
2,
|
||||||
"Second source",
|
"Second source",
|
||||||
|
null,
|
||||||
listOf("second"),
|
listOf("second"),
|
||||||
SPOUT,
|
SPOUT,
|
||||||
"",
|
"",
|
||||||
@ -661,7 +664,7 @@ class RepositoryTest {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
coEvery { api.sources() } returns StatusAndData(success = true, data = sources)
|
coEvery { api.sourcesDetailed() } returns StatusAndData(success = true, data = sources)
|
||||||
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
|
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
|
||||||
return Pair(sources, sourcesDB)
|
return Pair(sources, sourcesDB)
|
||||||
}
|
}
|
||||||
@ -675,13 +678,13 @@ class RepositoryTest {
|
|||||||
initializeRepository()
|
initializeRepository()
|
||||||
var testSources: List<SelfossModel.Source>?
|
var testSources: List<SelfossModel.Source>?
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSources()
|
testSources = repository.getSourcesDetails()
|
||||||
// Sources will be fetched from the database on the second call, thus testSources != sources
|
// Sources will be fetched from the database on the second call, thus testSources != sources
|
||||||
testSources = repository.getSources()
|
testSources = repository.getSourcesDetails()
|
||||||
}
|
}
|
||||||
|
|
||||||
coVerify(exactly = 1) { api.sources() }
|
coVerify(exactly = 1) { api.sourcesDetailed() }
|
||||||
assertNotSame(sources, testSources)
|
assertNotEquals(sources, testSources)
|
||||||
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
||||||
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
||||||
}
|
}
|
||||||
@ -693,13 +696,13 @@ class RepositoryTest {
|
|||||||
every { appSettingsService.isUpdateSourcesEnabled() } returns true
|
every { appSettingsService.isUpdateSourcesEnabled() } returns true
|
||||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
var testSources: List<SelfossModel.Source>?
|
var testSources: List<SelfossModel.Source>
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSources()
|
testSources = repository.getSourcesDetails()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertSame(sources, testSources)
|
assertEquals(sources, testSources)
|
||||||
coVerify(exactly = 1) { api.sources() }
|
coVerify(exactly = 1) { api.sourcesDetailed() }
|
||||||
verify(exactly = 0) { db.sourcesQueries }
|
verify(exactly = 0) { db.sourcesQueries }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -710,13 +713,13 @@ class RepositoryTest {
|
|||||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||||
initializeRepository()
|
initializeRepository()
|
||||||
var testSources: List<SelfossModel.Source>?
|
var testSources: List<SelfossModel.Source>
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSources()
|
testSources = repository.getSourcesDetails()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertSame(sources, testSources)
|
assertEquals(sources, testSources)
|
||||||
coVerify(exactly = 1) { api.sources() }
|
coVerify(exactly = 1) { api.sourcesDetailed() }
|
||||||
verify(atLeast = 1) { db.sourcesQueries }
|
verify(atLeast = 1) { db.sourcesQueries }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -724,13 +727,13 @@ class RepositoryTest {
|
|||||||
fun get_sources_without_connection() {
|
fun get_sources_without_connection() {
|
||||||
val (_, sourcesDB) = prepareSources()
|
val (_, sourcesDB) = prepareSources()
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
var testSources: List<SelfossModel.Source>?
|
var testSources: List<SelfossModel.Source>
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSources()
|
testSources = repository.getSourcesDetails()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
||||||
coVerify(exactly = 0) { api.sources() }
|
coVerify(exactly = 0) { api.sourcesDetailed() }
|
||||||
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -741,13 +744,13 @@ class RepositoryTest {
|
|||||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||||
every { appSettingsService.isUpdateSourcesEnabled() } returns true
|
every { appSettingsService.isUpdateSourcesEnabled() } returns true
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
var testSources: List<SelfossModel.Source>?
|
var testSources: List<SelfossModel.Source>
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSources()
|
testSources = repository.getSourcesDetails()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(emptyList<SelfossModel.Source>(), testSources)
|
assertEquals(emptyList<SelfossModel.Source>(), testSources)
|
||||||
coVerify(exactly = 0) { api.sources() }
|
coVerify(exactly = 0) { api.sourcesDetailed() }
|
||||||
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() }
|
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -758,13 +761,13 @@ class RepositoryTest {
|
|||||||
every { appSettingsService.isItemCachingEnabled() } returns true
|
every { appSettingsService.isItemCachingEnabled() } returns true
|
||||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
var testSources: List<SelfossModel.Source>?
|
var testSources: List<SelfossModel.Source>
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSources()
|
testSources = repository.getSourcesDetails()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
||||||
coVerify(exactly = 0) { api.sources() }
|
coVerify(exactly = 0) { api.sourcesDetailed() }
|
||||||
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,13 +778,13 @@ class RepositoryTest {
|
|||||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||||
initializeRepository(MutableStateFlow(false))
|
initializeRepository(MutableStateFlow(false))
|
||||||
var testSources: List<SelfossModel.Source>?
|
var testSources: List<SelfossModel.Source>
|
||||||
runBlocking {
|
runBlocking {
|
||||||
testSources = repository.getSources()
|
testSources = repository.getSourcesDetails()
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
assertEquals(sourcesDB.map { it.toView() }, testSources)
|
||||||
coVerify(exactly = 0) { api.sources() }
|
coVerify(exactly = 0) { api.sourcesDetailed() }
|
||||||
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1102,9 +1105,10 @@ class RepositoryTest {
|
|||||||
private fun prepareSearch() {
|
private fun prepareSearch() {
|
||||||
repository.setTagFilter(SelfossModel.Tag("Tag", "read", 0))
|
repository.setTagFilter(SelfossModel.Tag("Tag", "read", 0))
|
||||||
repository.setSourceFilter(
|
repository.setSourceFilter(
|
||||||
SelfossModel.Source(
|
SelfossModel.SourceDetail(
|
||||||
1,
|
1,
|
||||||
"First source",
|
"First source",
|
||||||
|
5,
|
||||||
listOf("Test", "second"),
|
listOf("Test", "second"),
|
||||||
SPOUT,
|
SPOUT,
|
||||||
"",
|
"",
|
||||||
|
@ -6,9 +6,12 @@ class MercuryModel {
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class ParsedContent(
|
class ParsedContent(
|
||||||
val title: String?,
|
val title: String? = null,
|
||||||
val content: String?,
|
val content: String? = null,
|
||||||
val lead_image_url: String?, // NOSONAR
|
val lead_image_url: String? = null, // NOSONAR
|
||||||
val url: String
|
val url: String? = null,
|
||||||
|
val error: Boolean? = null,
|
||||||
|
val message: String? = null,
|
||||||
|
val failed: Boolean? = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,8 @@ class SelfossModel {
|
|||||||
@Serializable
|
@Serializable
|
||||||
class Stats(
|
class Stats(
|
||||||
val total: Int,
|
val total: Int,
|
||||||
val unread: Int?,
|
val unread: Int? = null,
|
||||||
val starred: Int?
|
val starred: Int? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -36,9 +36,9 @@ class SelfossModel {
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class ApiInformation(
|
data class ApiInformation(
|
||||||
val version: String?,
|
val version: String? = null,
|
||||||
val apiversion: String?,
|
val apiversion: String? = null,
|
||||||
val configuration: ApiConfiguration?
|
val configuration: ApiConfiguration? = null
|
||||||
) {
|
) {
|
||||||
fun getApiMajorVersion(): Int {
|
fun getApiMajorVersion(): Int {
|
||||||
var versionNumber = 0
|
var versionNumber = 0
|
||||||
@ -54,26 +54,45 @@ class SelfossModel {
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class ApiConfiguration(
|
data class ApiConfiguration(
|
||||||
@Serializable(with = BooleanSerializer::class)
|
@Serializable(with = BooleanSerializer::class)
|
||||||
val publicMode: Boolean?,
|
val publicMode: Boolean? = null,
|
||||||
@Serializable(with = BooleanSerializer::class)
|
@Serializable(with = BooleanSerializer::class)
|
||||||
val authEnabled: Boolean?
|
val authEnabled: Boolean? = null
|
||||||
) {
|
) {
|
||||||
fun isAuthEnabled() = authEnabled ?: true
|
fun isAuthEnabled() = authEnabled ?: true
|
||||||
|
|
||||||
fun isPublicModeEnabled() = publicMode ?: false
|
fun isPublicModeEnabled() = publicMode ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Source {
|
||||||
|
val id: Int
|
||||||
|
var title: String
|
||||||
|
var unread: Int?
|
||||||
|
var error: String?
|
||||||
|
var icon: String?
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Source(
|
data class SourceStats(
|
||||||
val id: Int,
|
override val id: Int,
|
||||||
val title: String,
|
override var title: String,
|
||||||
|
override var unread: Int? = null,
|
||||||
|
override var error: String? = null,
|
||||||
|
override var icon: String? = null
|
||||||
|
) : Source
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SourceDetail(
|
||||||
|
override val id: Int,
|
||||||
|
override var title: String,
|
||||||
|
override var unread: Int? = null,
|
||||||
@Serializable(with = TagsListSerializer::class)
|
@Serializable(with = TagsListSerializer::class)
|
||||||
val tags: List<String>,
|
var tags: List<String>? = null,
|
||||||
val spout: String,
|
var spout: String? = null,
|
||||||
val error: String,
|
override var error: String? = null,
|
||||||
val icon: String?,
|
override var icon: String? = null,
|
||||||
val params: SourceParams?
|
var params: SourceParams? = null
|
||||||
)
|
) : Source
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SourceParams(
|
data class SourceParams(
|
||||||
val url: String? = null
|
val url: String? = null
|
||||||
@ -88,13 +107,13 @@ class SelfossModel {
|
|||||||
var unread: Boolean,
|
var unread: Boolean,
|
||||||
@Serializable(with = BooleanSerializer::class)
|
@Serializable(with = BooleanSerializer::class)
|
||||||
var starred: Boolean,
|
var starred: Boolean,
|
||||||
val thumbnail: String?,
|
val thumbnail: String? = null,
|
||||||
val icon: String?,
|
val icon: String? = null,
|
||||||
val link: String,
|
val link: String,
|
||||||
val sourcetitle: String,
|
val sourcetitle: String,
|
||||||
@Serializable(with = TagsListSerializer::class)
|
@Serializable(with = TagsListSerializer::class)
|
||||||
val tags: List<String>,
|
val tags: List<String>,
|
||||||
val author: String?
|
val author: String? = null
|
||||||
) {
|
) {
|
||||||
fun getLinkDecoded(): String {
|
fun getLinkDecoded(): String {
|
||||||
var stringUrl: String
|
var stringUrl: String
|
||||||
|
@ -44,11 +44,11 @@ class Repository(
|
|||||||
private val _badgeStarred = MutableStateFlow(0)
|
private val _badgeStarred = MutableStateFlow(0)
|
||||||
val badgeStarred = _badgeStarred.asStateFlow()
|
val badgeStarred = _badgeStarred.asStateFlow()
|
||||||
|
|
||||||
private var fetchedSources = false
|
|
||||||
private var fetchedTags = false
|
private var fetchedTags = false
|
||||||
|
private var fetchedSources = false
|
||||||
|
|
||||||
private var _readerItems = ArrayList<SelfossModel.Item>()
|
private var _readerItems = ArrayList<SelfossModel.Item>()
|
||||||
private var _selectedSource: SelfossModel.Source? = null
|
private var _selectedSource: SelfossModel.SourceDetail? = null
|
||||||
|
|
||||||
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
|
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
|
||||||
var fetchedItems: StatusAndData<List<SelfossModel.Item>> = StatusAndData.error()
|
var fetchedItems: StatusAndData<List<SelfossModel.Item>> = StatusAndData.error()
|
||||||
@ -180,23 +180,46 @@ class Repository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSources(): ArrayList<SelfossModel.Source> {
|
suspend fun getSourcesDetailsOrStats(): ArrayList<SelfossModel.Source> {
|
||||||
|
var sources = ArrayList<SelfossModel.Source>()
|
||||||
val isDatabaseEnabled =
|
val isDatabaseEnabled =
|
||||||
appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled()
|
appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled()
|
||||||
return if (isNetworkAvailable() && !fetchedSources) {
|
val shouldFetch = if (!appSettingsService.isUpdateSourcesEnabled()) !fetchedSources else true
|
||||||
val apiSources = api.sources()
|
if (shouldFetch && isNetworkAvailable()) {
|
||||||
if (apiSources.success && apiSources.data != null && isDatabaseEnabled) {
|
if (appSettingsService.getPublicAccess()) {
|
||||||
resetDBSourcesWithData(apiSources.data)
|
val apiSources = api.sourcesStats()
|
||||||
if (!appSettingsService.isUpdateSourcesEnabled()) {
|
if (apiSources.success && apiSources.data != null) {
|
||||||
fetchedSources = true
|
fetchedSources = true
|
||||||
|
sources = apiSources.data as ArrayList<SelfossModel.Source>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sources = getSourcesDetails() as ArrayList<SelfossModel.Source>
|
||||||
|
}
|
||||||
|
} else if (isDatabaseEnabled) {
|
||||||
|
sources = getDBSources().map { it.toView() } as ArrayList<SelfossModel.Source>
|
||||||
|
}
|
||||||
|
|
||||||
|
return sources
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getSourcesDetails(): ArrayList<SelfossModel.SourceDetail> {
|
||||||
|
var sources = ArrayList<SelfossModel.SourceDetail>()
|
||||||
|
val isDatabaseEnabled =
|
||||||
|
appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled()
|
||||||
|
val shouldFetch = if (!appSettingsService.isUpdateSourcesEnabled()) !fetchedSources else true
|
||||||
|
if (shouldFetch && isNetworkAvailable()) {
|
||||||
|
val apiSources = api.sourcesDetailed()
|
||||||
|
if (apiSources.success && apiSources.data != null) {
|
||||||
|
fetchedSources = true
|
||||||
|
sources = apiSources.data
|
||||||
|
if (isDatabaseEnabled) {
|
||||||
|
resetDBSourcesWithData(sources)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apiSources.data ?: ArrayList()
|
|
||||||
} else if (isDatabaseEnabled) {
|
} else if (isDatabaseEnabled) {
|
||||||
ArrayList(getDBSources().map { it.toView() })
|
sources = getDBSources().map { it.toView() } as ArrayList<SelfossModel.SourceDetail>
|
||||||
} else {
|
|
||||||
ArrayList()
|
|
||||||
}
|
}
|
||||||
|
return sources
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun markAsRead(item: SelfossModel.Item): Boolean {
|
suspend fun markAsRead(item: SelfossModel.Item): Boolean {
|
||||||
@ -482,7 +505,7 @@ class Repository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resetDBSourcesWithData(sources: List<SelfossModel.Source>) {
|
private fun resetDBSourcesWithData(sources: List<SelfossModel.SourceDetail>) {
|
||||||
db.sourcesQueries.deleteAllSources()
|
db.sourcesQueries.deleteAllSources()
|
||||||
|
|
||||||
db.sourcesQueries.transaction {
|
db.sourcesQueries.transaction {
|
||||||
@ -592,7 +615,7 @@ class Repository(
|
|||||||
ReaderForSelfossDB.Schema.migrate(driverFactory.createDriver(), 0, 1)
|
ReaderForSelfossDB.Schema.migrate(driverFactory.createDriver(), 0, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSelectedSource(source: SelfossModel.Source) {
|
fun setSelectedSource(source: SelfossModel.SourceDetail) {
|
||||||
_selectedSource = source
|
_selectedSource = source
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +623,7 @@ class Repository(
|
|||||||
_selectedSource = null
|
_selectedSource = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSelectedSource(): SelfossModel.Source? {
|
fun getSelectedSource(): SelfossModel.SourceDetail? {
|
||||||
return _selectedSource
|
return _selectedSource
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,6 +7,7 @@ import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
|||||||
import io.github.aakira.napier.Napier
|
import io.github.aakira.napier.Napier
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.plugins.*
|
import io.ktor.client.plugins.*
|
||||||
|
import io.ktor.client.plugins.auth.providers.*
|
||||||
import io.ktor.client.plugins.cache.*
|
import io.ktor.client.plugins.cache.*
|
||||||
import io.ktor.client.plugins.contentnegotiation.*
|
import io.ktor.client.plugins.contentnegotiation.*
|
||||||
import io.ktor.client.plugins.cookies.*
|
import io.ktor.client.plugins.cookies.*
|
||||||
@ -15,6 +16,9 @@ import io.ktor.client.request.*
|
|||||||
import io.ktor.client.statement.*
|
import io.ktor.client.statement.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.serialization.kotlinx.json.*
|
import io.ktor.serialization.kotlinx.json.*
|
||||||
|
import io.ktor.util.*
|
||||||
|
import io.ktor.utils.io.charsets.*
|
||||||
|
import io.ktor.utils.io.core.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -32,6 +36,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
prettyPrint = true
|
prettyPrint = true
|
||||||
isLenient = true
|
isLenient = true
|
||||||
ignoreUnknownKeys = true
|
ignoreUnknownKeys = true
|
||||||
|
explicitNulls = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
install(Logging) {
|
install(Logging) {
|
||||||
@ -63,6 +68,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
expectSuccess = false
|
expectSuccess = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +80,13 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
client = createHttpClient()
|
client = createHttpClient()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun constructBasicAuthValue(credentials: BasicAuthCredentials): String {
|
||||||
|
val authString = "${credentials.username}:${credentials.password}"
|
||||||
|
val authBuf = authString.toByteArray(Charsets.UTF_8).encodeBase64()
|
||||||
|
|
||||||
|
return "Basic $authBuf"
|
||||||
|
}
|
||||||
|
|
||||||
// Api version was introduces after the POST login, so when there is a version, it should be available
|
// Api version was introduces after the POST login, so when there is a version, it should be available
|
||||||
private fun shouldHavePostLogin() = appSettingsService.getApiVersion() != -1
|
private fun shouldHavePostLogin() = appSettingsService.getApiVersion() != -1
|
||||||
private fun hasLoginInfo() =
|
private fun hasLoginInfo() =
|
||||||
@ -96,11 +109,23 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
private suspend fun getLogin() = maybeResponse(client.tryToGet(url("/login")) {
|
private suspend fun getLogin() = maybeResponse(client.tryToGet(url("/login")) {
|
||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
private suspend fun postLogin() = maybeResponse(client.tryToPost(url("/login")) {
|
private suspend fun postLogin() = maybeResponse(client.tryToPost(url("/login")) {
|
||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
private fun shouldHaveNewLogout() =
|
private fun shouldHaveNewLogout() =
|
||||||
@ -114,9 +139,23 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun maybeLogoutIfAvailable() =
|
private suspend fun maybeLogoutIfAvailable() =
|
||||||
responseOrSuccessIf404(client.tryToGet(url("/logout")))
|
responseOrSuccessIf404(client.tryToGet(url("/logout")) {
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
private suspend fun doLogout() = maybeResponse(client.tryToDelete(url("/api/session/current")))
|
private suspend fun doLogout() = maybeResponse(client.tryToDelete(url("/api/session/current")) {
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
suspend fun getItems(
|
suspend fun getItems(
|
||||||
type: String,
|
type: String,
|
||||||
@ -139,6 +178,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("updatedsince", updatedSince)
|
parameter("updatedsince", updatedSince)
|
||||||
parameter("items", items ?: appSettingsService.getItemsNumber())
|
parameter("items", items ?: appSettingsService.getItemsNumber())
|
||||||
parameter("offset", offset)
|
parameter("offset", offset)
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun getItemsWithoutCatch(): StatusAndData<List<SelfossModel.Item>> =
|
suspend fun getItemsWithoutCatch(): StatusAndData<List<SelfossModel.Item>> =
|
||||||
@ -149,6 +194,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
}
|
}
|
||||||
parameter("type", "all")
|
parameter("type", "all")
|
||||||
parameter("items", 1)
|
parameter("items", 1)
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun stats(): StatusAndData<SelfossModel.Stats> =
|
suspend fun stats(): StatusAndData<SelfossModel.Stats> =
|
||||||
@ -157,6 +208,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun tags(): StatusAndData<List<SelfossModel.Tag>> =
|
suspend fun tags(): StatusAndData<List<SelfossModel.Tag>> =
|
||||||
@ -165,6 +222,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun update(): StatusAndData<String> =
|
suspend fun update(): StatusAndData<String> =
|
||||||
@ -173,6 +236,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun spouts(): StatusAndData<Map<String, SelfossModel.Spout>> =
|
suspend fun spouts(): StatusAndData<Map<String, SelfossModel.Spout>> =
|
||||||
@ -181,18 +250,51 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun sources(): StatusAndData<ArrayList<SelfossModel.Source>> =
|
suspend fun sourcesStats(): StatusAndData<ArrayList<SelfossModel.SourceStats>> =
|
||||||
|
bodyOrFailure(client.tryToGet(url("/sources/stats")) {
|
||||||
|
if (!shouldHavePostLogin()) {
|
||||||
|
parameter("username", appSettingsService.getUserName())
|
||||||
|
parameter("password", appSettingsService.getPassword())
|
||||||
|
}
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
suspend fun sourcesDetailed(): StatusAndData<ArrayList<SelfossModel.SourceDetail>> =
|
||||||
bodyOrFailure(client.tryToGet(url("/sources/list")) {
|
bodyOrFailure(client.tryToGet(url("/sources/list")) {
|
||||||
if (!shouldHavePostLogin()) {
|
if (!shouldHavePostLogin()) {
|
||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun apiInformation(): StatusAndData<SelfossModel.ApiInformation> =
|
suspend fun apiInformation(): StatusAndData<SelfossModel.ApiInformation> =
|
||||||
bodyOrFailure(client.tryToGet(url("/api/about")))
|
bodyOrFailure(client.tryToGet(url("/api/about")) {
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
suspend fun markAsRead(id: String): SuccessResponse =
|
suspend fun markAsRead(id: String): SuccessResponse =
|
||||||
maybeResponse(client.tryToPost(url("/mark/$id")) {
|
maybeResponse(client.tryToPost(url("/mark/$id")) {
|
||||||
@ -200,6 +302,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun unmarkAsRead(id: String): SuccessResponse =
|
suspend fun unmarkAsRead(id: String): SuccessResponse =
|
||||||
@ -208,6 +316,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun starr(id: String): SuccessResponse =
|
suspend fun starr(id: String): SuccessResponse =
|
||||||
@ -216,6 +330,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun unstarr(id: String): SuccessResponse =
|
suspend fun unstarr(id: String): SuccessResponse =
|
||||||
@ -224,6 +344,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
suspend fun markAllAsRead(ids: List<String>): SuccessResponse =
|
suspend fun markAllAsRead(ids: List<String>): SuccessResponse =
|
||||||
@ -235,6 +361,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
append("password", appSettingsService.getPassword())
|
append("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
ids.map { append("ids[]", it) }
|
ids.map { append("ids[]", it) }
|
||||||
|
},
|
||||||
|
block = {
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -270,6 +404,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
append("url", url)
|
append("url", url)
|
||||||
append("spout", spout)
|
append("spout", spout)
|
||||||
append(tagsParamName, tags)
|
append(tagsParamName, tags)
|
||||||
|
},
|
||||||
|
block = {
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -307,6 +449,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
append("url", url)
|
append("url", url)
|
||||||
append("spout", spout)
|
append("spout", spout)
|
||||||
append(tagsParamName, tags)
|
append(tagsParamName, tags)
|
||||||
|
},
|
||||||
|
block = {
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -316,5 +466,18 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
|
if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
|
||||||
|
headers {
|
||||||
|
append(
|
||||||
|
HttpHeaders.Authorization,
|
||||||
|
constructBasicAuthValue(
|
||||||
|
BasicAuthCredentials(
|
||||||
|
username = appSettingsService.getBasicUserName(),
|
||||||
|
password = appSettingsService.getBasicPassword()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
@ -10,7 +10,9 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
private var _publicAccess: Boolean? = null
|
private var _publicAccess: Boolean? = null
|
||||||
private var _baseUrl: String = ""
|
private var _baseUrl: String = ""
|
||||||
private var _userName: String = ""
|
private var _userName: String = ""
|
||||||
|
private var _basicUserName: String = ""
|
||||||
private var _password: String = ""
|
private var _password: String = ""
|
||||||
|
private var _basicPassword: String = ""
|
||||||
|
|
||||||
// User settings related
|
// User settings related
|
||||||
private var _itemsCaching: Boolean? = null
|
private var _itemsCaching: Boolean? = null
|
||||||
@ -96,6 +98,20 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
return _password
|
return _password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getBasicUserName(): String {
|
||||||
|
if (_basicUserName.isEmpty()) {
|
||||||
|
refreshBasicUsername()
|
||||||
|
}
|
||||||
|
return _basicUserName
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBasicPassword(): String {
|
||||||
|
if (_basicPassword.isEmpty()) {
|
||||||
|
refreshBasicPassword()
|
||||||
|
}
|
||||||
|
return _basicPassword
|
||||||
|
}
|
||||||
|
|
||||||
fun getItemsNumber(): Int {
|
fun getItemsNumber(): Int {
|
||||||
if (_itemsNumber == null) {
|
if (_itemsNumber == null) {
|
||||||
refreshItemsNumber()
|
refreshItemsNumber()
|
||||||
@ -149,6 +165,14 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
_password = settings.getString(PASSWORD, "")
|
_password = settings.getString(PASSWORD, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun refreshBasicUsername() {
|
||||||
|
_basicUserName = settings.getString(BASIC_LOGIN, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshBasicPassword() {
|
||||||
|
_basicPassword = settings.getString(BASIC_PASSWORD, "")
|
||||||
|
}
|
||||||
|
|
||||||
private fun refreshArticleViewerEnabled() {
|
private fun refreshArticleViewerEnabled() {
|
||||||
_articleViewer = settings.getBoolean(PREFER_ARTICLE_VIEWER, true)
|
_articleViewer = settings.getBoolean(PREFER_ARTICLE_VIEWER, true)
|
||||||
}
|
}
|
||||||
@ -354,6 +378,8 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
fun refreshApiSettings() {
|
fun refreshApiSettings() {
|
||||||
refreshPassword()
|
refreshPassword()
|
||||||
refreshUsername()
|
refreshUsername()
|
||||||
|
refreshBasicUsername()
|
||||||
|
refreshBasicPassword()
|
||||||
refreshBaseUrl()
|
refreshBaseUrl()
|
||||||
refreshApiVersion()
|
refreshApiVersion()
|
||||||
refreshPublicAccess()
|
refreshPublicAccess()
|
||||||
@ -387,7 +413,17 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
login: String,
|
login: String,
|
||||||
password: String
|
password: String
|
||||||
) {
|
) {
|
||||||
settings.putString(BASE_URL, url)
|
val regex = """\/\/(\D+):(\D+)@""".toRegex()
|
||||||
|
val matchResult = regex.find(url)
|
||||||
|
if (matchResult != null) {
|
||||||
|
val (basicLogin, basicPassword) = matchResult.destructured
|
||||||
|
settings.putString(BASIC_LOGIN, basicLogin)
|
||||||
|
settings.putString(BASIC_PASSWORD, basicPassword)
|
||||||
|
val urlWithoutBasicAuth = url.replace(regex, "//")
|
||||||
|
settings.putString(BASE_URL, urlWithoutBasicAuth)
|
||||||
|
} else {
|
||||||
|
settings.putString(BASE_URL, url)
|
||||||
|
}
|
||||||
settings.putString(LOGIN, login)
|
settings.putString(LOGIN, login)
|
||||||
settings.putString(PASSWORD, password)
|
settings.putString(PASSWORD, password)
|
||||||
refreshApiSettings()
|
refreshApiSettings()
|
||||||
@ -397,6 +433,8 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
settings.remove(BASE_URL)
|
settings.remove(BASE_URL)
|
||||||
settings.remove(LOGIN)
|
settings.remove(LOGIN)
|
||||||
settings.remove(PASSWORD)
|
settings.remove(PASSWORD)
|
||||||
|
settings.remove(BASIC_LOGIN)
|
||||||
|
settings.remove(BASIC_PASSWORD)
|
||||||
refreshApiSettings()
|
refreshApiSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,6 +478,10 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
|
|
||||||
const val PASSWORD = "password"
|
const val PASSWORD = "password"
|
||||||
|
|
||||||
|
const val BASIC_LOGIN = "basic_login"
|
||||||
|
|
||||||
|
const val BASIC_PASSWORD = "basic_password"
|
||||||
|
|
||||||
const val PREFER_ARTICLE_VIEWER = "prefer_article_viewer"
|
const val PREFER_ARTICLE_VIEWER = "prefer_article_viewer"
|
||||||
|
|
||||||
const val CARD_VIEW_ACTIVE = "card_view_active"
|
const val CARD_VIEW_ACTIVE = "card_view_active"
|
||||||
@ -470,7 +512,6 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
|
|
||||||
const val PERIODIC_REFRESH_MINUTES = "periodic_refresh_minutes"
|
const val PERIODIC_REFRESH_MINUTES = "periodic_refresh_minutes"
|
||||||
|
|
||||||
|
|
||||||
const val INFINITE_LOADING = "infinite_loading"
|
const val INFINITE_LOADING = "infinite_loading"
|
||||||
|
|
||||||
const val ITEMS_CACHING = "items_caching"
|
const val ITEMS_CACHING = "items_caching"
|
||||||
|
@ -12,24 +12,25 @@ fun TAG.toView(): SelfossModel.Tag =
|
|||||||
this.unread.toInt()
|
this.unread.toInt()
|
||||||
)
|
)
|
||||||
|
|
||||||
fun SOURCE.toView(): SelfossModel.Source =
|
fun SOURCE.toView(): SelfossModel.SourceDetail =
|
||||||
SelfossModel.Source(
|
SelfossModel.SourceDetail(
|
||||||
this.id.toInt(),
|
this.id.toInt(),
|
||||||
this.title,
|
this.title,
|
||||||
this.tags.split(","),
|
null,
|
||||||
|
this.tags?.split(","),
|
||||||
this.spout,
|
this.spout,
|
||||||
this.error,
|
this.error,
|
||||||
this.icon,
|
this.icon,
|
||||||
if (this.url != null) SelfossModel.SourceParams(this.url) else null
|
if (this.url != null) SelfossModel.SourceParams(this.url) else null
|
||||||
)
|
)
|
||||||
|
|
||||||
fun SelfossModel.Source.toEntity(): SOURCE =
|
fun SelfossModel.SourceDetail.toEntity(): SOURCE =
|
||||||
SOURCE(
|
SOURCE(
|
||||||
this.id.toString(),
|
this.id.toString(),
|
||||||
this.title.getHtmlDecoded(),
|
this.title.getHtmlDecoded(),
|
||||||
this.tags.joinToString(","),
|
this.tags?.joinToString(",").orEmpty(),
|
||||||
this.spout,
|
this.spout.orEmpty(),
|
||||||
this.error,
|
this.error.orEmpty(),
|
||||||
this.icon.orEmpty(),
|
this.icon.orEmpty(),
|
||||||
this.params?.url
|
this.params?.url
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user