diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt
index 6f4537b..ab4d547 100644
--- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt
+++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt
@@ -115,10 +115,16 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
}
}
+ val swipeDirs = if (appSettingsService.getPublicAccess()) {
+ 0
+ } else {
+ ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
+ }
+
val simpleItemTouchCallback =
object : ItemTouchHelper.SimpleCallback(
0,
- ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
+ swipeDirs
) {
override fun getSwipeDirs(
recyclerView: RecyclerView,
@@ -511,6 +517,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.home_menu, menu)
+ if (appSettingsService.getPublicAccess()) {
+ menu.removeItem(R.id.readAll)
+ menu.removeItem(R.id.action_sources)
+ }
val searchItem = menu.findItem(R.id.action_search)
val searchView = searchItem.getActionView() as SearchView
diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt
index a47a1af..2a48ea7 100644
--- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt
+++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/LoginActivity.kt
@@ -128,7 +128,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
private fun goToMain() {
CoroutineScope(Dispatchers.Main).launch {
- repository.updateApiVersion()
+ repository.updateApiInformation()
ACRA.errorReporter.putCustomData("SELFOSS_API_VERSION", appSettingsService.getApiVersion().toString())
}
val intent = Intent(this, HomeActivity::class.java)
diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt
index 70ec25d..8ca576d 100644
--- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt
+++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt
@@ -84,7 +84,7 @@ class ReaderActivity : AppCompatActivity(), DIAware {
}
private fun readItem(item: SelfossModel.Item) {
- if (appSettingsService.isMarkOnScrollEnabled()) {
+ if (appSettingsService.isMarkOnScrollEnabled() && !appSettingsService.getPublicAccess()) {
CoroutineScope(Dispatchers.IO).launch {
repository.markAsRead(item)
}
@@ -137,28 +137,34 @@ class ReaderActivity : AppCompatActivity(), DIAware {
inflater.inflate(R.menu.reader_menu, menu)
toolbarMenu = menu
- if (allItems.isNotEmpty() && allItems[currentItem].starred) {
- canRemoveFromFavorite()
- } else {
- canFavorite()
- }
alignmentMenu()
- binding.pager.registerOnPageChangeCallback(
- object : ViewPager2.OnPageChangeCallback() {
-
- override fun onPageSelected(position: Int) {
- super.onPageSelected(position)
-
- if (allItems[position].starred) {
- canRemoveFromFavorite()
- } else {
- canFavorite()
- }
- readItem(allItems[position])
- }
+ if (appSettingsService.getPublicAccess()) {
+ menu.removeItem(R.id.star)
+ } else {
+ if (allItems.isNotEmpty() && allItems[currentItem].starred) {
+ canRemoveFromFavorite()
+ } else {
+ canFavorite()
}
- )
+
+
+ binding.pager.registerOnPageChangeCallback(
+ object : ViewPager2.OnPageChangeCallback() {
+
+ override fun onPageSelected(position: Int) {
+ super.onPageSelected(position)
+
+ if (allItems[position].starred) {
+ canRemoveFromFavorite()
+ } else {
+ canFavorite()
+ }
+ readItem(allItems[position])
+ }
+ }
+ )
+ }
return true
}
@@ -177,7 +183,7 @@ class ReaderActivity : AppCompatActivity(), DIAware {
when (item.itemId) {
android.R.id.home -> {
- onBackPressed()
+ onBackPressedDispatcher.onBackPressed()
return true
}
R.id.star -> {
diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt
index d9b5f55..f21bd77 100644
--- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt
+++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt
@@ -56,6 +56,10 @@ class ItemCardAdapter(
val itm = items[position]
binding.favButton.isSelected = itm.starred
+ if (appSettingsService.getPublicAccess()) {
+ binding.favButton.visibility = View.GONE
+ }
+
binding.title.text = itm.title.getHtmlDecoded()
binding.title.setOnTouchListener(LinkOnTouchListener())
diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt
index 3fb842c..4dd0221 100644
--- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt
+++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt
@@ -195,6 +195,9 @@ class ArticleFragment : Fragment(), DIAware {
private fun handleFloatingToolbar(): FloatingToolbar {
val floatingToolbar: FloatingToolbar = binding.floatingToolbar
+ if (appSettingsService.getPublicAccess()) {
+ floatingToolbar.setMenu(R.menu.reader_toolbar_no_read)
+ }
floatingToolbar.attachFab(fab)
floatingToolbar.background = ColorDrawable(resources.getColor(R.color.colorAccent))
diff --git a/androidApp/src/main/res/layout/card_item.xml b/androidApp/src/main/res/layout/card_item.xml
index 023be37..42e2b6e 100644
--- a/androidApp/src/main/res/layout/card_item.xml
+++ b/androidApp/src/main/res/layout/card_item.xml
@@ -47,7 +47,6 @@
android:id="@+id/sourceImage"
android:layout_width="40dp"
android:layout_height="40dp"
- android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintLeft_toLeftOf="parent"
@@ -85,41 +84,32 @@
app:layout_constraintTop_toBottomOf="@+id/title"
tools:text="Google Actualité Il y a 5h" />
-
+ app:srcCompat="@drawable/ic_open_in_browser_black_24dp"
+ app:tint="?android:attr/textColorPrimary" />
+ app:srcCompat="@drawable/ic_menu_heart_60dp"
+ app:tint="@color/ic_menu_heart_color" />
-
+
+
+
diff --git a/androidApp/src/main/res/layout/fragment_article.xml b/androidApp/src/main/res/layout/fragment_article.xml
index 8e6db28..664015d 100644
--- a/androidApp/src/main/res/layout/fragment_article.xml
+++ b/androidApp/src/main/res/layout/fragment_article.xml
@@ -83,7 +83,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
- android:layout_gravity="end|bottom|right">
+ android:layout_gravity="end|bottom|end">
+
+
+
+
+
+
\ No newline at end of file
diff --git a/androidApp/src/test/kotlin/RepositoryTest.kt b/androidApp/src/test/kotlin/RepositoryTest.kt
index 38245b2..ec307b9 100644
--- a/androidApp/src/test/kotlin/RepositoryTest.kt
+++ b/androidApp/src/test/kotlin/RepositoryTest.kt
@@ -20,6 +20,8 @@ import org.junit.Test
private const val BASE_URL = "https://test.com/selfoss/"
+private const val USERNAME = "username"
+
private const val SPOUT = "spouts\\rss\\fulltextrss"
private const val IMAGE_URL = "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
@@ -49,7 +51,7 @@ class RepositoryTest {
repository = Repository(api, appSettingsService, isConnectionAvailable, db)
runBlocking {
- repository.updateApiVersion()
+ repository.updateApiInformation()
}
}
@@ -58,12 +60,13 @@ class RepositoryTest {
clearAllMocks()
every { appSettingsService.getApiVersion() } returns 4
every { appSettingsService.getBaseUrl() } returns BASE_URL
+ every { appSettingsService.getUserName() } returns USERNAME
every { appSettingsService.isItemCachingEnabled() } returns false
every { appSettingsService.isUpdateSourcesEnabled() } returns false
- coEvery { api.version() } returns StatusAndData(
+ coEvery { api.apiInformation() } returns StatusAndData(
success = true,
- data = SelfossModel.ApiVersion("2.19-ba1e8e3", "4.0.0")
+ data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(false, true))
)
coEvery { api.stats() } returns StatusAndData(
success = true,
@@ -81,7 +84,7 @@ class RepositoryTest {
fun instantiate_repository() {
initializeRepository()
- coVerify(exactly = 1) { api.version() }
+ coVerify(exactly = 1) { api.apiInformation() }
}
@Test
@@ -90,7 +93,7 @@ class RepositoryTest {
initializeRepository(MutableStateFlow(false))
- coVerify(exactly = 0) { api.version() }
+ coVerify(exactly = 0) { api.apiInformation() }
coVerify(exactly = 0) { api.stats() }
}
@@ -110,10 +113,70 @@ class RepositoryTest {
verify(exactly = 1) { appSettingsService.updateApiVersion(4) }
}
+ @Test
+ fun get_public_access() {
+ every { appSettingsService.updatePublicAccess(any()) } returns Unit
+ coEvery { api.apiInformation() } returns StatusAndData(
+ success = true,
+ data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(true, true))
+ )
+ every { appSettingsService.getUserName() } returns ""
+
+ initializeRepository()
+
+ coVerify(exactly = 1) { api.apiInformation() }
+ coVerify(exactly = 1) { appSettingsService.updatePublicAccess(true) }
+ }
+
+ @Test
+ fun get_public_access_username_not_empty() {
+ every { appSettingsService.updatePublicAccess(any()) } returns Unit
+ coEvery { api.apiInformation() } returns StatusAndData(
+ success = true,
+ data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(true, true))
+ )
+ every { appSettingsService.getUserName() } returns "username"
+
+ initializeRepository()
+
+ coVerify(exactly = 1) { api.apiInformation() }
+ coVerify(exactly = 0) { appSettingsService.updatePublicAccess(true) }
+ }
+
+ @Test
+ fun get_public_access_no_auth() {
+ every { appSettingsService.updatePublicAccess(any()) } returns Unit
+ coEvery { api.apiInformation() } returns StatusAndData(
+ success = true,
+ data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(true, false))
+ )
+ every { appSettingsService.getUserName() } returns ""
+
+ initializeRepository()
+
+ coVerify(exactly = 1) { api.apiInformation() }
+ coVerify(exactly = 0) { appSettingsService.updatePublicAccess(true) }
+ }
+
+ @Test
+ fun get_public_access_disabled() {
+ every { appSettingsService.updatePublicAccess(any()) } returns Unit
+ coEvery { api.apiInformation() } returns StatusAndData(
+ success = true,
+ data = SelfossModel.ApiInformation("2.19-ba1e8e3", "4.0.0", SelfossModel.ApiConfiguration(false, true))
+ )
+ every { appSettingsService.getUserName() } returns ""
+
+ initializeRepository()
+
+ coVerify(exactly = 1) { api.apiInformation() }
+ coVerify(exactly = 0) { appSettingsService.updatePublicAccess(true) }
+ }
+
@Test
fun get_api_1_date_with_api_4_version_stored() {
every { appSettingsService.getApiVersion() } returns 4
- coEvery { api.version() } returns StatusAndData(success = false, null)
+ coEvery { api.apiInformation() } returns StatusAndData(success = false, null)
val itemParameters = FakeItemParameters()
itemParameters.datetime = "2021-04-23 11:45:32"
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt
index e5eec2a..8fdf5e8 100644
--- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt
+++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt
@@ -35,17 +35,32 @@ class SelfossModel {
)
@Serializable
- data class ApiVersion(
+ data class ApiInformation(
val version: String?,
- val apiversion: String?
+ val apiversion: String?,
+ val configuration: ApiConfiguration?
) {
- fun getApiMajorVersion() : Int {
+ fun getApiMajorVersion(): Int {
var versionNumber = 0
if (apiversion != null) {
versionNumber = apiversion.substringBefore(".").toInt()
}
return versionNumber
}
+
+ fun getApiConfiguration() = configuration ?: ApiConfiguration(null, null)
+ }
+
+ @Serializable
+ data class ApiConfiguration(
+ @Serializable(with = BooleanSerializer::class)
+ val publicMode: Boolean?,
+ @Serializable(with = BooleanSerializer::class)
+ val authEnabled: Boolean?
+ ) {
+ fun isAuthEnabled() = authEnabled ?: true
+
+ fun isPublicModeEnabled() = publicMode ?: false
}
@Serializable
diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt
index fb03172..1bcf35e 100644
--- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt
+++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt
@@ -439,13 +439,23 @@ class Repository(
api.refreshLoginInformation()
}
- suspend fun updateApiVersion() {
+ suspend fun updateApiInformation() {
val apiMajorVersion = appSettingsService.getApiVersion()
if (isNetworkAvailable()) {
- val fetchedVersion = api.version()
- if (fetchedVersion.success && fetchedVersion.data != null && fetchedVersion.data.getApiMajorVersion() != apiMajorVersion) {
- appSettingsService.updateApiVersion(fetchedVersion.data.getApiMajorVersion())
+ val fetchedInformation = api.apiInformation()
+ if (fetchedInformation.success && fetchedInformation.data != null) {
+ if (fetchedInformation.data.getApiMajorVersion() != apiMajorVersion) {
+ appSettingsService.updateApiVersion(fetchedInformation.data.getApiMajorVersion())
+ }
+ // Check if we're accessing the instance in public mode
+ // This happens when auth and public mode are enabled but
+ // no credentials are provided to login
+ if (appSettingsService.getUserName().isEmpty()
+ && fetchedInformation.data.getApiConfiguration().isAuthEnabled()
+ && fetchedInformation.data.getApiConfiguration().isPublicModeEnabled()) {
+ appSettingsService.updatePublicAccess(true)
+ }
}
}
}
diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt
index c41e157..4d10b3b 100644
--- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt
+++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt
@@ -191,7 +191,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
}
})
- suspend fun version(): StatusAndData =
+ suspend fun apiInformation(): StatusAndData =
bodyOrFailure(client.tryToGet(url("/api/about")))
suspend fun markAsRead(id: String): SuccessResponse =
diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/AppSettingsService.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/AppSettingsService.kt
index d5b622d..35716a9 100644
--- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/AppSettingsService.kt
+++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/AppSettingsService.kt
@@ -7,6 +7,7 @@ class AppSettingsService {
// Api related
private var _apiVersion: Int = -1
+ private var _publicAccess: Boolean? = null
private var _baseUrl: String = ""
private var _userName: String = ""
private var _password: String = ""
@@ -48,10 +49,32 @@ class AppSettingsService {
return _apiVersion
}
+
+ fun updateApiVersion(apiMajorVersion: Int) {
+ settings.putInt(API_VERSION_MAJOR, apiMajorVersion)
+ refreshApiVersion()
+ }
+
private fun refreshApiVersion() {
_apiVersion = settings.getInt(API_VERSION_MAJOR, -1)
}
+ fun getPublicAccess(): Boolean {
+ if (_publicAccess == null) {
+ refreshPublicAccess()
+ }
+ return _publicAccess!!
+ }
+
+ fun updatePublicAccess(publicAccess: Boolean) {
+ settings.putBoolean(API_PUBLIC_ACCESS, publicAccess)
+ refreshPublicAccess()
+ }
+
+ private fun refreshPublicAccess() {
+ _publicAccess = settings.getBoolean(API_PUBLIC_ACCESS, false)
+ }
+
fun getBaseUrl(): String {
if (_baseUrl.isEmpty()) {
refreshBaseUrl()
@@ -333,6 +356,7 @@ class AppSettingsService {
refreshUsername()
refreshBaseUrl()
refreshApiVersion()
+ refreshPublicAccess()
}
fun refreshUserSettings() {
@@ -376,11 +400,6 @@ class AppSettingsService {
refreshApiSettings()
}
- fun updateApiVersion(apiMajorVersion: Int) {
- settings.putInt(API_VERSION_MAJOR, apiMajorVersion)
- refreshApiVersion()
- }
-
fun clearAll() {
settings.clear()
refreshApiSettings()
@@ -409,6 +428,8 @@ class AppSettingsService {
const val API_VERSION_MAJOR = "apiVersionMajor"
+ const val API_PUBLIC_ACCESS = "apiPublicAccess"
+
const val API_ITEMS_NUMBER = "prefer_api_items_number"
const val API_TIMEOUT = "api_timeout"