Compare commits
23 Commits
109050d0cf
...
v122092671
Author | SHA1 | Date | |
---|---|---|---|
74ef4da15b | |||
bd96c67788 | |||
da71de6806 | |||
0264da8ccc | |||
270d959ee0 | |||
6d11dfb80c | |||
4184bbb900 | |||
4c12c9d570 | |||
f4db02521d | |||
01763556b1 | |||
e2411c00d8 | |||
0473a5f7bc | |||
d0d6a4378c | |||
1dfa3c9f07 | |||
815f00e764 | |||
bdc77ab8ef | |||
6bd06cb458 | |||
e9e8bee6c9 | |||
ff6038dbd4 | |||
8146cff011 | |||
fc4c48dd12 | |||
94f1ec943c | |||
0278540fb2 |
104
.drone.yml
104
.drone.yml
@ -1,21 +1,117 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: android
|
||||
name: test
|
||||
|
||||
steps:
|
||||
- name: code-analysis
|
||||
- name: AnylyseBuildTest
|
||||
image: mingc/android-build-box:latest
|
||||
failure: ignore
|
||||
commands:
|
||||
- ls -la
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Analysing..."
|
||||
- ./gradlew sonarqube -Dsonar.projectKey=RFS2 -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\""
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Building..."
|
||||
- ./gradlew :androidApp:build -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Testing..."
|
||||
- echo "---------------------------------------------------------"
|
||||
environment:
|
||||
SONAR_HOST_URL:
|
||||
from_secret: sonarScannerHostUrl
|
||||
SONAR_LOGIN:
|
||||
from_secret: sonarScannerLogin
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: Publish
|
||||
|
||||
steps:
|
||||
- name: createTag
|
||||
image: ubuntu:latest
|
||||
commands:
|
||||
- apt-get update && apt-get install -y git
|
||||
- ./build.sh --publish --from-ci
|
||||
- git remote add pushing https://$GITEA_USR:$GITEA_PASS@gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform.git
|
||||
- git push pushing --tags
|
||||
environment:
|
||||
GITEA_USR:
|
||||
from_secret: giteaUsr
|
||||
GITEA_PASS:
|
||||
from_secret: giteaPass
|
||||
|
||||
- name: scpFiles
|
||||
image: appleboy/drone-scp
|
||||
settings:
|
||||
host: amine-louveau.fr
|
||||
username: ubuntu
|
||||
key:
|
||||
from_secret: privateKey
|
||||
port: 22
|
||||
target: /home/ubuntu/
|
||||
source: version.txt
|
||||
|
||||
- name: deploy
|
||||
image: appleboy/drone-ssh
|
||||
settings:
|
||||
host: amine-louveau.fr
|
||||
user: ubuntu
|
||||
key:
|
||||
from_secret: privateKey
|
||||
command_timeout: 2m
|
||||
script:
|
||||
- cd /home/ubuntu
|
||||
- sudo rm -rf /var/www/amine/version.txt
|
||||
- sudo chown www-data:www-data ./version.txt
|
||||
- sudo mv version.txt /var/www/amine/
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- promote
|
||||
target:
|
||||
- production
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: Release
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: mingc/android-build-box:latest
|
||||
commands:
|
||||
- ./gradlew :androidApp:build -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
|
||||
- echo "Generate APK"
|
||||
- ./gradlew :androidApp:assembleGithubConfigRelease -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Get Key"
|
||||
- wget https://amine-louveau.fr/key
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Zipalign"
|
||||
- $ANDROID_HOME/build-tools/31.0.0/zipalign -f -v 4 androidApp/build/outputs/apk/githubConfig/release/androidApp-githubConfig-release-unsigned.apk androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Sign"
|
||||
- $ANDROID_HOME/build-tools/31.0.0/apksigner sign -v --out signed.apk --ks ./key --ks-key-alias $YOUR_KEY_ALIAS --ks-pass pass:$YOUR_KEYSTORE_PASSWORD --v1-signing-enabled true --v2-signing-enabled true androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Verify"
|
||||
- $ANDROID_HOME/build-tools/31.0.0/apksigner verify signed.apk
|
||||
environment:
|
||||
YOUR_KEYSTORE_PASSWORD:
|
||||
from_secret: keyPass
|
||||
YOUR_KEY_ALIAS:
|
||||
from_secret: keyAlias
|
||||
|
||||
- name: gitea_release
|
||||
image: plugins/gitea-release
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: giteaAPI
|
||||
base_url: https://gitea.amine-louveau.fr
|
||||
files: signed.apk
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,3 +1,14 @@
|
||||
# V2/Multiplatform rewrite
|
||||
|
||||
**v1**
|
||||
|
||||
- The app has the same functionalities as before.
|
||||
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
# Old version changes
|
||||
|
||||
**1.7.x**
|
||||
|
||||
- Hiding tags with 0 articles
|
||||
|
@ -1,4 +1,4 @@
|
||||
# ReaderForSelfoss-multiplatform
|
||||
# ReaderForSelfoss-multiplatform [](https://build.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform)
|
||||
|
||||
[](https://crowdin.com/project/readerforselfoss)
|
||||
|
||||
|
@ -93,7 +93,7 @@ android {
|
||||
}
|
||||
flavorDimensions.add("build")
|
||||
productFlavors {
|
||||
create("github") {
|
||||
create("githubConfig") {
|
||||
versionNameSuffix = "-github"
|
||||
dimension = "build"
|
||||
}
|
||||
@ -101,6 +101,7 @@ android {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
namespace = "bou.amine.apps.readerforselfossv2.android"
|
||||
|
||||
}
|
||||
|
||||
|
31
androidApp/proguard-rules.pro
vendored
31
androidApp/proguard-rules.pro
vendored
@ -55,11 +55,38 @@
|
||||
public *;
|
||||
}
|
||||
|
||||
-dontwarn com.anupcowkur.reservoir.**
|
||||
|
||||
-dontwarn javax.annotation.**
|
||||
|
||||
-keep class android.support.v7.widget.SearchView { *; }
|
||||
|
||||
# maybe remove later ?
|
||||
-keep class * extends androidx.fragment.app.Fragment
|
||||
|
||||
|
||||
# Keep `Companion` object fields of serializable classes.
|
||||
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
|
||||
-if @kotlinx.serialization.Serializable class **
|
||||
-keepclassmembers class <1> {
|
||||
static <1>$Companion Companion;
|
||||
}
|
||||
|
||||
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
|
||||
-if @kotlinx.serialization.Serializable class ** {
|
||||
static **$* *;
|
||||
}
|
||||
-keepclassmembers class <2>$<3> {
|
||||
kotlinx.serialization.KSerializer serializer(...);
|
||||
}
|
||||
|
||||
# Keep `INSTANCE.serializer()` of serializable objects.
|
||||
-if @kotlinx.serialization.Serializable class ** {
|
||||
public static ** INSTANCE;
|
||||
}
|
||||
-keepclassmembers class <1> {
|
||||
public static <1> INSTANCE;
|
||||
kotlinx.serialization.KSerializer serializer(...);
|
||||
}
|
||||
|
||||
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
|
||||
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# NOTE: This is copy/pasted in jenkins
|
||||
|
||||
rm -f version.txt
|
||||
printf "versionName=$1-github\nversionCode=$1" >> version.txt
|
||||
|
||||
# You'll need to change server as your server and define a VERSION_PATH.
|
||||
scp version.txt server:$VERSION_PATH
|
||||
|
||||
rm version.txt
|
@ -1,18 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="bou.amine.apps.readerforselfossv2.android">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
tools:replace="android:allowBackup"
|
||||
android:name=".MyApp"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/NoBar">
|
||||
android:theme="@style/NoBar"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:theme="@style/SplashTheme"
|
||||
|
@ -9,7 +9,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityAddSourceBinding
|
||||
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
||||
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
|
||||
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
|
||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||
@ -84,7 +84,7 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
|
||||
super.onResume()
|
||||
|
||||
val baseUrl = appSettingsService.getBaseUrl()
|
||||
if (baseUrl.isEmpty() || !baseUrl.isBaseUrlValid(this@AddSourceActivity)) {
|
||||
if (baseUrl.isEmpty() || baseUrl.isBaseUrlInvalid(this@AddSourceActivity)) {
|
||||
mustLoginToAddSource()
|
||||
} else {
|
||||
handleSpoutsSpinner(binding.spoutsSpinner, binding.progress, binding.formContainer)
|
||||
|
@ -11,6 +11,7 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@ -49,14 +50,12 @@ import com.mikepenz.materialdrawer.holder.ColorHolder
|
||||
import com.mikepenz.materialdrawer.holder.StringHolder
|
||||
import com.mikepenz.materialdrawer.model.DividerDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.interfaces.*
|
||||
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
|
||||
import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
||||
import com.mikepenz.materialdrawer.util.addStickyFooterItem
|
||||
import com.mikepenz.materialdrawer.util.updateBadge
|
||||
import com.mikepenz.materialdrawer.widget.AccountHeaderView
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@ -64,12 +63,10 @@ import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.closestDI
|
||||
import org.kodein.di.instance
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
|
||||
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAware {
|
||||
|
||||
private val SETTINGS_ACTIVITY: Int = 101111
|
||||
private val DRAWER_ID_TAGS = 100101L
|
||||
private val DRAWER_ID_HIDDEN_TAGS = 101100L
|
||||
private val DRAWER_ID_SOURCES = 100110L
|
||||
@ -96,6 +93,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
|
||||
private lateinit var tagsBadge: Map<Long, Int>
|
||||
|
||||
private val settingsLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
appSettingsService.refreshUserSettings()
|
||||
}
|
||||
|
||||
override val di by closestDI()
|
||||
private val repository : Repository by instance()
|
||||
private val appSettingsService : AppSettingsService by instance()
|
||||
@ -137,7 +138,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
customTabActivityHelper = CustomTabActivityHelper()
|
||||
|
||||
handleBottomBar()
|
||||
handleDrawer()
|
||||
initDrawer()
|
||||
|
||||
handleSwipeRefreshLayout()
|
||||
|
||||
@ -147,7 +148,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
repository.tryToCacheItemsAndGetNewOnes()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun handleSwipeRefreshLayout() {
|
||||
@ -323,17 +323,17 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
scoop.update(Toppings.PRIMARY_DARK.value, appColors.colorPrimaryDark)
|
||||
}
|
||||
|
||||
private fun handleDrawer() {
|
||||
private fun initDrawer() {
|
||||
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
|
||||
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
|
||||
Glide.with(this@HomeActivity)
|
||||
.asBitmap()
|
||||
.load(uri)
|
||||
.apply(RequestOptions()
|
||||
.placeholder(R.mipmap.ic_launcher)
|
||||
.fallback(R.mipmap.ic_launcher)
|
||||
.fitCenter())
|
||||
.into(imageView)
|
||||
.asBitmap()
|
||||
.load(uri)
|
||||
.apply(RequestOptions()
|
||||
.placeholder(R.mipmap.ic_launcher)
|
||||
.fallback(R.mipmap.ic_launcher)
|
||||
.fitCenter())
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
override fun cancel(imageView: ImageView) {
|
||||
@ -362,285 +362,31 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
|
||||
binding.drawerContainer.addDrawerListener(drawerListener)
|
||||
|
||||
binding.mainDrawer.addStickyFooterItem(
|
||||
PrimaryDrawerItem().apply {
|
||||
nameRes = R.string.drawer_report_bug
|
||||
iconRes = R.drawable.ic_bug_report_black_24dp
|
||||
isIconTinted = true
|
||||
onDrawerItemClickListener = { _, _, _ ->
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
|
||||
startActivity(browserIntent)
|
||||
false
|
||||
}
|
||||
})
|
||||
|
||||
binding.mainDrawer.addStickyFooterItem(
|
||||
PrimaryDrawerItem().apply {
|
||||
nameRes = R.string.title_activity_settings
|
||||
iconRes = R.drawable.ic_settings_black_24dp
|
||||
isIconTinted = true
|
||||
onDrawerItemClickListener = { _, _, _ ->
|
||||
startActivityForResult(Intent(this@HomeActivity, SettingsActivity::class.java), SETTINGS_ACTIVITY)
|
||||
false
|
||||
}
|
||||
})
|
||||
|
||||
if (appSettingsService.isDisplayAccountHeaderEnabled()) {
|
||||
AccountHeaderView(this).apply {
|
||||
attachToSliderView(binding.mainDrawer)
|
||||
addProfiles(
|
||||
ProfileDrawerItem().apply {
|
||||
nameText = appSettingsService.getBaseUrl()
|
||||
setBackgroundResource(R.drawable.bg)
|
||||
iconRes = R.mipmap.ic_launcher
|
||||
selectionListEnabledForSingleProfile = false
|
||||
}
|
||||
)
|
||||
}
|
||||
// Sticky items
|
||||
addStickyPrimaryItem(R.string.drawer_report_bug, R.drawable.ic_bug_report_black_24dp) { _, _, _ ->
|
||||
val browserIntent =
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
|
||||
startActivity(browserIntent)
|
||||
false
|
||||
}
|
||||
addStickyPrimaryItem(R.string.title_activity_settings, R.drawable.ic_settings_black_24dp) { _, _, _ ->
|
||||
settingsLauncher.launch(Intent(this, SettingsActivity::class.java))
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor this.
|
||||
// TODO: use updateSources
|
||||
private fun addStickyPrimaryItem(name: Int, icon: Int, clickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)?) {
|
||||
binding.mainDrawer.addStickyFooterItem(
|
||||
PrimaryDrawerItem().apply {
|
||||
nameRes = name
|
||||
iconRes = icon
|
||||
isIconTinted = true
|
||||
onDrawerItemClickListener = clickListener
|
||||
})
|
||||
}
|
||||
|
||||
private fun handleDrawerItems() {
|
||||
tagsBadge = emptyMap()
|
||||
fun handleDrawerData(maybeDrawerData: DrawerData?, loadedFromCache: Boolean = false) {
|
||||
fun createDrawerItem(
|
||||
it: SelfossModel.Tag
|
||||
) {
|
||||
val gd = GradientDrawable()
|
||||
val gdColor = try {
|
||||
Color.parseColor(it.color)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
appColors.colorPrimary
|
||||
}
|
||||
|
||||
gd.setColor(gdColor)
|
||||
gd.shape = GradientDrawable.RECTANGLE
|
||||
gd.setSize(30, 30)
|
||||
gd.cornerRadius = 30F
|
||||
|
||||
val drawerItem = PrimaryDrawerItem()
|
||||
.apply {
|
||||
nameText = it.tag.getHtmlDecoded()
|
||||
identifier = it.tag.longHash()
|
||||
iconDrawable = gd
|
||||
badgeStyle = BadgeStyle().apply {
|
||||
textColor = ColorHolder.fromColor(Color.WHITE)
|
||||
color = ColorHolder.fromColor(appColors.colorAccent)
|
||||
}
|
||||
onDrawerItemClickListener = { _, _, _ ->
|
||||
repository.tagFilter = it
|
||||
repository.sourceFilter = null
|
||||
getElementsAccordingToTab()
|
||||
fetchOnEmptyList()
|
||||
false
|
||||
}
|
||||
}
|
||||
if (it.unread > 0) {
|
||||
drawerItem.badgeText = it.unread.toString()
|
||||
}
|
||||
|
||||
binding.mainDrawer.itemAdapter.add(drawerItem)
|
||||
}
|
||||
|
||||
fun handleTags(maybeTags: List<SelfossModel.Tag>?) {
|
||||
if (maybeTags == null) {
|
||||
if (loadedFromCache) {
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
SecondaryDrawerItem()
|
||||
.apply { nameRes = R.string.drawer_error_loading_tags; isSelectable = false }
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val filteredTags = maybeTags
|
||||
.filterNot { appSettingsService.getHiddenTags().contains(it.tag) }
|
||||
.sortedBy { it.unread == 0 }
|
||||
tagsBadge = filteredTags.map {
|
||||
createDrawerItem(it)
|
||||
|
||||
(it.tag.longHash() to it.unread)
|
||||
}.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleHiddenTags(maybeTags: List<SelfossModel.Tag>?) {
|
||||
if (maybeTags == null) {
|
||||
if (loadedFromCache) {
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
SecondaryDrawerItem().apply {
|
||||
nameRes = R.string.drawer_error_loading_tags
|
||||
isSelectable = false
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val filteredHiddenTags: List<SelfossModel.Tag> =
|
||||
maybeTags.filter { appSettingsService.getHiddenTags().contains(it.tag) }
|
||||
tagsBadge = filteredHiddenTags.map {
|
||||
createDrawerItem(it)
|
||||
|
||||
(it.tag.longHash() to it.unread)
|
||||
}.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSources(maybeSources: List<SelfossModel.Source>?) {
|
||||
if (maybeSources == null) {
|
||||
if (loadedFromCache) {
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
SecondaryDrawerItem().apply {
|
||||
nameRes = R.string.drawer_error_loading_sources
|
||||
isSelectable = false
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
for (source in maybeSources) {
|
||||
val item = PrimaryDrawerItem().apply {
|
||||
nameText = source.title.getHtmlDecoded()
|
||||
identifier = source.id.toLong()
|
||||
iconUrl = source.getIcon(repository.baseUrl)
|
||||
onDrawerItemClickListener = { _,_,_ ->
|
||||
repository.sourceFilter = source
|
||||
repository.tagFilter = null
|
||||
getElementsAccordingToTab()
|
||||
fetchOnEmptyList()
|
||||
false
|
||||
}
|
||||
}
|
||||
binding.mainDrawer.itemAdapter.add(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.mainDrawer.itemAdapter.clear()
|
||||
if (maybeDrawerData != null) {
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
SecondaryDrawerItem().apply {
|
||||
nameRes = R.string.drawer_item_filters
|
||||
isSelectable = false
|
||||
identifier = DRAWER_ID_FILTERS
|
||||
badgeRes = R.string.drawer_action_clear
|
||||
onDrawerItemClickListener = { _,_,_ ->
|
||||
repository.sourceFilter = null
|
||||
repository.tagFilter = null
|
||||
binding.mainDrawer.setSelectionAtPosition(-1)
|
||||
getElementsAccordingToTab()
|
||||
fetchOnEmptyList()
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
if (appSettingsService.getHiddenTags().isNotEmpty()) {
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
DividerDrawerItem(),
|
||||
SecondaryDrawerItem().apply {
|
||||
nameRes = R.string.drawer_item_hidden_tags
|
||||
identifier = DRAWER_ID_HIDDEN_TAGS
|
||||
isSelectable = false
|
||||
}
|
||||
)
|
||||
handleHiddenTags(maybeDrawerData.tags)
|
||||
}
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
DividerDrawerItem(),
|
||||
SecondaryDrawerItem().apply {
|
||||
nameRes = R.string.drawer_item_tags
|
||||
identifier = DRAWER_ID_TAGS
|
||||
isSelectable = false
|
||||
}
|
||||
)
|
||||
handleTags(maybeDrawerData.tags)
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
DividerDrawerItem(),
|
||||
SecondaryDrawerItem().apply {
|
||||
nameRes = R.string.drawer_item_sources
|
||||
identifier = DRAWER_ID_SOURCES
|
||||
isSelectable = false
|
||||
badgeRes = R.string.drawer_action_edit
|
||||
onDrawerItemClickListener = { v,_,_ ->
|
||||
startActivity(Intent(v!!.context, SourcesActivity::class.java))
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
handleSources(maybeDrawerData.sources)
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
DividerDrawerItem(),
|
||||
PrimaryDrawerItem().apply {
|
||||
nameRes = R.string.action_about
|
||||
isSelectable = false
|
||||
iconRes = R.drawable.ic_info_outline_white_24dp
|
||||
isIconTinted = true
|
||||
onDrawerItemClickListener = { _,_,_ ->
|
||||
LibsBuilder()
|
||||
.withAboutIconShown(true)
|
||||
.withAboutVersionShown(true)
|
||||
.start(this@HomeActivity)
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (!loadedFromCache) {
|
||||
if (maybeDrawerData.tags != null) {
|
||||
thread {
|
||||
repository.resetDBTagsWithData(maybeDrawerData.tags)
|
||||
}
|
||||
}
|
||||
if (maybeDrawerData.sources != null) {
|
||||
thread {
|
||||
repository.resetDBSourcesWithData(maybeDrawerData.sources)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!loadedFromCache) {
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
PrimaryDrawerItem().apply {
|
||||
nameRes = R.string.no_tags_loaded
|
||||
identifier = DRAWER_ID_TAGS
|
||||
isSelectable = false
|
||||
},
|
||||
PrimaryDrawerItem().apply {
|
||||
nameRes = R.string.no_sources_loaded
|
||||
identifier = DRAWER_ID_SOURCES
|
||||
isSelectable = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun drawerApiCalls(maybeDrawerData: DrawerData?) {
|
||||
var tags: List<SelfossModel.Tag>? = null
|
||||
var sources: List<SelfossModel.Source>?
|
||||
|
||||
fun sourcesApiCall() {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val response = repository.getSources()
|
||||
if (response != null) {
|
||||
sources = response
|
||||
val apiDrawerData = DrawerData(tags, sources)
|
||||
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
|
||||
handleDrawerData(apiDrawerData)
|
||||
}
|
||||
} else {
|
||||
val apiDrawerData = DrawerData(tags, null)
|
||||
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
|
||||
handleDrawerData(apiDrawerData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
tags = repository.getTags()
|
||||
sourcesApiCall()
|
||||
}
|
||||
}
|
||||
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
PrimaryDrawerItem().apply {
|
||||
nameRes = R.string.drawer_loading
|
||||
@ -648,16 +394,189 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
}
|
||||
)
|
||||
|
||||
thread {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val drawerData = DrawerData(repository.getDBTags().map { it.toView() },
|
||||
repository.getDBSources().map { it.toView() })
|
||||
runOnUiThread {
|
||||
handleDrawerData(drawerData, loadedFromCache = true)
|
||||
drawerApiCalls(drawerData)
|
||||
// Only refresh if there is no data in the DB, or if the `UpdateSources` setting is enabled
|
||||
if (drawerData.sources?.isEmpty() == true || appSettingsService.isUpdateSourcesEnabled()) {
|
||||
drawerApiCalls(drawerData)
|
||||
} else {
|
||||
handleDrawerData(drawerData, loadedFromCache = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawerApiCalls(drawerData: DrawerData) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val apiDrawerData = DrawerData(repository.getTags(), repository.getSources())
|
||||
handleDrawerData(if (drawerData != apiDrawerData) apiDrawerData else drawerData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDrawerData(drawerData: DrawerData, loadedFromCache: Boolean = false) {
|
||||
binding.mainDrawer.itemAdapter.clear()
|
||||
|
||||
// Filters title with clear action
|
||||
secondaryItem(withDivider = false, R.string.drawer_item_filters, DRAWER_ID_FILTERS, R.string.drawer_action_clear) { _,_,_ ->
|
||||
repository.sourceFilter = null
|
||||
repository.tagFilter = null
|
||||
binding.mainDrawer.setSelectionAtPosition(-1)
|
||||
getElementsAccordingToTab()
|
||||
fetchOnEmptyList()
|
||||
false
|
||||
}
|
||||
|
||||
// Hidden tags
|
||||
if (drawerData.tags != null && drawerData.tags.isNotEmpty() && appSettingsService.getHiddenTags().isNotEmpty()) {
|
||||
secondaryItem(
|
||||
withDivider = true,
|
||||
R.string.drawer_item_hidden_tags,
|
||||
DRAWER_ID_HIDDEN_TAGS
|
||||
)
|
||||
handleHiddenTags(drawerData.tags)
|
||||
}
|
||||
|
||||
// Tags
|
||||
secondaryItem(withDivider = true, R.string.drawer_item_tags, DRAWER_ID_TAGS)
|
||||
if (drawerData.tags == null && !loadedFromCache) {
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
SecondaryDrawerItem()
|
||||
.apply { nameRes = R.string.drawer_error_loading_tags; isSelectable = false }
|
||||
)
|
||||
} else {
|
||||
handleTags(drawerData.tags!!)
|
||||
}
|
||||
|
||||
// Sources
|
||||
secondaryItem(withDivider = true, R.string.drawer_item_sources, DRAWER_ID_SOURCES, R.string.drawer_action_edit) { v, _, _ ->
|
||||
startActivity(Intent(v!!.context, SourcesActivity::class.java))
|
||||
false
|
||||
}
|
||||
if (drawerData.sources == null && !loadedFromCache) {
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
SecondaryDrawerItem().apply {
|
||||
nameRes = R.string.drawer_error_loading_tags
|
||||
isSelectable = false
|
||||
}
|
||||
)
|
||||
} else {
|
||||
handleSources(drawerData.sources!!)
|
||||
}
|
||||
|
||||
// About action
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
DividerDrawerItem(),
|
||||
PrimaryDrawerItem().apply {
|
||||
nameRes = R.string.action_about
|
||||
isSelectable = false
|
||||
iconRes = R.drawable.ic_info_outline_white_24dp
|
||||
isIconTinted = true
|
||||
onDrawerItemClickListener = { _,_,_ ->
|
||||
LibsBuilder()
|
||||
.withAboutIconShown(true)
|
||||
.withAboutVersionShown(true)
|
||||
.start(this@HomeActivity)
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun secondaryItem(withDivider: Boolean, name: Int, id: Long, badgeId: Int? = null, clickListener: ((v: View?, item: IDrawerItem<*>, position: Int) -> Boolean)? = null) {
|
||||
if (withDivider) {
|
||||
binding.mainDrawer.itemAdapter.add(DividerDrawerItem())
|
||||
}
|
||||
|
||||
binding.mainDrawer.itemAdapter.add(
|
||||
SecondaryDrawerItem().apply {
|
||||
nameRes = name
|
||||
identifier = id
|
||||
isSelectable = false
|
||||
if (badgeId != null) {
|
||||
badgeRes = badgeId
|
||||
}
|
||||
onDrawerItemClickListener = clickListener
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun createDrawerItem(it: SelfossModel.Tag) {
|
||||
val gd = GradientDrawable()
|
||||
val gdColor = try {
|
||||
Color.parseColor(it.color)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
appColors.colorPrimary
|
||||
}
|
||||
gd.setColor(gdColor)
|
||||
gd.shape = GradientDrawable.RECTANGLE
|
||||
gd.setSize(30, 30)
|
||||
gd.cornerRadius = 30F
|
||||
|
||||
val drawerItem = PrimaryDrawerItem()
|
||||
.apply {
|
||||
nameText = it.tag.getHtmlDecoded()
|
||||
identifier = it.tag.longHash()
|
||||
iconDrawable = gd
|
||||
badgeStyle = BadgeStyle().apply {
|
||||
textColor = ColorHolder.fromColor(Color.WHITE)
|
||||
color = ColorHolder.fromColor(appColors.colorAccent)
|
||||
}
|
||||
onDrawerItemClickListener = { _, _, _ ->
|
||||
repository.tagFilter = it
|
||||
repository.sourceFilter = null
|
||||
getElementsAccordingToTab()
|
||||
fetchOnEmptyList()
|
||||
false
|
||||
}
|
||||
}
|
||||
if (it.unread > 0) {
|
||||
drawerItem.badgeText = it.unread.toString()
|
||||
}
|
||||
|
||||
binding.mainDrawer.itemAdapter.add(drawerItem)
|
||||
}
|
||||
|
||||
private fun handleTags(tags: List<SelfossModel.Tag>) {
|
||||
val filteredTags = tags
|
||||
.filterNot { appSettingsService.getHiddenTags().contains(it.tag) }
|
||||
.sortedBy { it.tag }
|
||||
createTagItems(filteredTags)
|
||||
}
|
||||
|
||||
private fun handleHiddenTags(tags: List<SelfossModel.Tag>) {
|
||||
val filteredHiddenTags: List<SelfossModel.Tag> =
|
||||
tags.filter { appSettingsService.getHiddenTags().contains(it.tag) }
|
||||
createTagItems(filteredHiddenTags)
|
||||
}
|
||||
|
||||
private fun createTagItems(tags: List<SelfossModel.Tag>) {
|
||||
tagsBadge = tags.associate {
|
||||
createDrawerItem(it)
|
||||
|
||||
(it.tag.longHash() to it.unread)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSources(sources: List<SelfossModel.Source>) {
|
||||
for (source in sources) {
|
||||
val item = PrimaryDrawerItem().apply {
|
||||
nameText = source.title.getHtmlDecoded()
|
||||
identifier = source.id.toLong()
|
||||
iconUrl = source.getIcon(repository.baseUrl)
|
||||
onDrawerItemClickListener = { _,_,_ ->
|
||||
repository.sourceFilter = source
|
||||
repository.tagFilter = null
|
||||
getElementsAccordingToTab()
|
||||
fetchOnEmptyList()
|
||||
false
|
||||
}
|
||||
}
|
||||
binding.mainDrawer.itemAdapter.add(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reloadLayoutManager() {
|
||||
val currentManager = binding.recyclerView.layoutManager
|
||||
val layoutManager: RecyclerView.LayoutManager
|
||||
@ -1030,12 +949,5 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
WorkManager.getInstance(baseContext).enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == SETTINGS_ACTIVITY) {
|
||||
appSettingsService.refreshUserSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding
|
||||
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
|
||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
@ -115,14 +115,14 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
binding.passwordView.error = null
|
||||
|
||||
// Store values at the time of the login attempt.
|
||||
val url = binding.urlView.text.toString()
|
||||
val login = binding.loginView.text.toString()
|
||||
val password = binding.passwordView.text.toString()
|
||||
val url = binding.urlView.text.toString().trim()
|
||||
val login = binding.loginView.text.toString().trim()
|
||||
val password = binding.passwordView.text.toString().trim()
|
||||
|
||||
var cancel = false
|
||||
var focusView: View? = null
|
||||
|
||||
if (!url.isBaseUrlValid(this@LoginActivity)) {
|
||||
if (url.isBaseUrlInvalid(this@LoginActivity)) {
|
||||
binding.urlView.error = getString(R.string.login_url_problem)
|
||||
focusView = binding.urlView
|
||||
cancel = true
|
||||
|
@ -268,8 +268,8 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
|
||||
private fun getContentFromMercury(customTabsIntent: CustomTabsIntent) {
|
||||
if (repository.isNetworkAvailable()) {
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
val parser = MercuryApi()
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
val parser = MercuryApi()
|
||||
|
||||
parser.parseUrl(url).enqueue(
|
||||
object : Callback<ParsedContent> {
|
||||
|
@ -69,11 +69,13 @@ class SettingsActivity : AppCompatActivity(),
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
if (supportFragmentManager.popBackStackImmediate()) {
|
||||
return if (supportFragmentManager.popBackStackImmediate()) {
|
||||
supportActionBar?.title = getText(R.string.title_activity_settings)
|
||||
return true
|
||||
false
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
true
|
||||
}
|
||||
return super.onSupportNavigateUp()
|
||||
}
|
||||
|
||||
override fun onPreferenceStartFragment(
|
||||
@ -216,11 +218,4 @@ class SettingsActivity : AppCompatActivity(),
|
||||
setPreferencesFromResource(R.xml.pref_experimental, rootKey)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> super.onBackPressed()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package bou.amine.apps.readerforselfossv2.android.utils
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
import java.security.cert.CertificateException
|
||||
import java.security.cert.X509Certificate
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.TrustManager
|
||||
import javax.net.ssl.X509TrustManager
|
||||
|
||||
fun getUnsafeHttpClient(): OkHttpClient.Builder =
|
||||
try {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
|
||||
override fun getAcceptedIssuers(): Array<X509Certificate> =
|
||||
arrayOf()
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkClientTrusted(
|
||||
chain: Array<java.security.cert.X509Certificate>,
|
||||
authType: String
|
||||
) {
|
||||
}
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkServerTrusted(
|
||||
chain: Array<java.security.cert.X509Certificate>,
|
||||
authType: String
|
||||
) {
|
||||
}
|
||||
})
|
||||
|
||||
// Install the all-trusting trust manager
|
||||
val sslContext = SSLContext.getInstance("SSL")
|
||||
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
|
||||
|
||||
val sslSocketFactory = sslContext.socketFactory
|
||||
|
||||
OkHttpClient.Builder()
|
||||
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
|
||||
.hostnameVerifier { _, _ -> true }
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(e)
|
||||
}
|
@ -163,7 +163,7 @@ private fun openInBrowser(linkDecoded: String, app: Activity) {
|
||||
fun String.isUrlValid(): Boolean =
|
||||
this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
|
||||
|
||||
fun String.isBaseUrlValid(ctx: Context): Boolean {
|
||||
fun String.isBaseUrlInvalid(ctx: Context): Boolean {
|
||||
val baseUrl = this.toHttpUrlOrNull()
|
||||
var existsAndEndsWithSlash = false
|
||||
if (baseUrl != null) {
|
||||
@ -171,7 +171,7 @@ fun String.isBaseUrlValid(ctx: Context): Boolean {
|
||||
existsAndEndsWithSlash = "" == pathSegments[pathSegments.size - 1]
|
||||
}
|
||||
|
||||
return Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash
|
||||
return !(Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash)
|
||||
}
|
||||
|
||||
fun Context.openInBrowserAsNewTask(i: SelfossModel.Item) {
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 406 B |
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">L\'alçada de les targetes serà fixa</string>
|
||||
<string name="source_code">Codi font</string>
|
||||
<string name="drawer_error_loading_tags">S\'ha produït un error en carregar les etiquetes</string>
|
||||
<string name="drawer_error_loading_sources">S\'ha produït un error en carregar les fonts</string>
|
||||
<string name="drawer_item_filters">Filtres</string>
|
||||
<string name="drawer_action_clear">Esborra</string>
|
||||
<string name="drawer_item_tags">Etiquetes</string>
|
||||
<string name="drawer_item_sources">Fonts</string>
|
||||
<string name="drawer_action_edit">Edita</string>
|
||||
<string name="no_tags_loaded">No s\'ha carregat cap etiqueta</string>
|
||||
<string name="no_sources_loaded">No s\'ha carregat cap font</string>
|
||||
<string name="drawer_loading">S\'està carregant…</string>
|
||||
<string name="menu_home_search">Cerca</string>
|
||||
<string name="can_delete_source">No es pot suprimir la font</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">API de Selfoss</string>
|
||||
<string name="pref_api_items_number_title">Nombre d\'elements carregats</string>
|
||||
<string name="pref_hidden_tags">Etiquetes ocultes</string>
|
||||
<string name="display_header_drawer_summary">Mostra una capçalera amb la instància URL de Selfoss al panell lateral.</string>
|
||||
<string name="display_header_drawer_title">Capçalera de menú</string>
|
||||
<string name="pref_general_infinite_loading_title">Carrega articles en desplaçar</string>
|
||||
<string name="translation">Traducció</string>
|
||||
<string name="cant_open_invalid_url">L\'element URL no és vàlid. Estic intentant solucionar aquest problema perquè l\'aplicació no falli.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Kartenhöhe ist fix</string>
|
||||
<string name="source_code">Quellcode</string>
|
||||
<string name="drawer_error_loading_tags">Fehler beim Laden der Tags…</string>
|
||||
<string name="drawer_error_loading_sources">Fehler beim Laden der Quellen…</string>
|
||||
<string name="drawer_item_filters">Filter</string>
|
||||
<string name="drawer_action_clear">leeren</string>
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Quellen</string>
|
||||
<string name="drawer_action_edit">bearbeiten</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">Keine Quellen geladen</string>
|
||||
<string name="drawer_loading">Lade…</string>
|
||||
<string name="menu_home_search">Suche</string>
|
||||
<string name="can_delete_source">Can\'t delete the source…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">selfoss API</string>
|
||||
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
||||
<string name="display_header_drawer_title">Account header</string>
|
||||
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
|
||||
<string name="translation">Übersetzung</string>
|
||||
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Se fijará la altura de la tarjeta</string>
|
||||
<string name="source_code">Código fuente</string>
|
||||
<string name="drawer_error_loading_tags">Error al cargar etiquetas…</string>
|
||||
<string name="drawer_error_loading_sources">Error al cargar fuentes…</string>
|
||||
<string name="drawer_item_filters">Filtros</string>
|
||||
<string name="drawer_action_clear">limpiar</string>
|
||||
<string name="drawer_item_tags">Etiquetas</string>
|
||||
<string name="drawer_item_sources">Fuentes</string>
|
||||
<string name="drawer_action_edit">editar</string>
|
||||
<string name="no_tags_loaded">No hay etiquetas cargadas</string>
|
||||
<string name="no_sources_loaded">No hay fuentes cargadas</string>
|
||||
<string name="drawer_loading">Cargando…</string>
|
||||
<string name="menu_home_search">Buscar</string>
|
||||
<string name="can_delete_source">No se puede eliminar la fuente…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">Api de Selfoss</string>
|
||||
<string name="pref_api_items_number_title">Número de artículos cargados</string>
|
||||
<string name="pref_hidden_tags">Etiquetas ocultas</string>
|
||||
<string name="display_header_drawer_summary">Mostrar una cabecera con la url de instancia de selfoss en el cajón lateral.</string>
|
||||
<string name="display_header_drawer_title">Cabecera de cuenta</string>
|
||||
<string name="pref_general_infinite_loading_title">Cargar más artículos en desplazamiento</string>
|
||||
<string name="translation">Traducción</string>
|
||||
<string name="cant_open_invalid_url">La url del elemento no es válida. Estoy buscando resolver este problema para que la aplicación no colapse.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
<string name="source_code">Source code</string>
|
||||
<string name="drawer_error_loading_tags">Error loading tags…</string>
|
||||
<string name="drawer_error_loading_sources">Error loading sources…</string>
|
||||
<string name="drawer_item_filters">Filters</string>
|
||||
<string name="drawer_action_clear">clear</string>
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
<string name="menu_home_search">Search</string>
|
||||
<string name="can_delete_source">Can\'t delete the source…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
||||
<string name="display_header_drawer_title">Account header</string>
|
||||
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
|
||||
<string name="translation">Translation</string>
|
||||
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">La taille de la carte sera fixe</string>
|
||||
<string name="source_code">Code source</string>
|
||||
<string name="drawer_error_loading_tags">Erreur lors du chargement des tags…</string>
|
||||
<string name="drawer_error_loading_sources">Erreur lors du chargement des sources…</string>
|
||||
<string name="drawer_item_filters">Filtres</string>
|
||||
<string name="drawer_action_clear">raz</string>
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">éditer</string>
|
||||
<string name="no_tags_loaded">Pas de tags chargés</string>
|
||||
<string name="no_sources_loaded">Pas de sources chargés</string>
|
||||
<string name="drawer_loading">Chargement …</string>
|
||||
<string name="menu_home_search">Rechercher</string>
|
||||
<string name="can_delete_source">Impossible de supprimer la source…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">Api Selfoss</string>
|
||||
<string name="pref_api_items_number_title">Nombre d\'articles chargés</string>
|
||||
<string name="pref_hidden_tags">Tags Cachés</string>
|
||||
<string name="display_header_drawer_summary">Afficher une entête avec l\'url de votre instance de Selfoss en haut du drawer lateral.</string>
|
||||
<string name="display_header_drawer_title">Entête de compte</string>
|
||||
<string name="pref_general_infinite_loading_title">Charger plus d\'articles au scroll</string>
|
||||
<string name="translation">Traduction</string>
|
||||
<string name="cant_open_invalid_url">L’url de l’élément n’est pas valide. En attendant la résolution du problème, le lien ne s\'ouvrira pas.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">A altura das tarxetas será fixa</string>
|
||||
<string name="source_code">Código fonte</string>
|
||||
<string name="drawer_error_loading_tags">Produciuse un erro ao cargar as etiquetas…</string>
|
||||
<string name="drawer_error_loading_sources">Produciuse un erro ao cargar as fontes…</string>
|
||||
<string name="drawer_item_filters">Filtros</string>
|
||||
<string name="drawer_action_clear">limpar</string>
|
||||
<string name="drawer_item_tags">Etiquetas</string>
|
||||
<string name="drawer_item_sources">Fontes</string>
|
||||
<string name="drawer_action_edit">editar</string>
|
||||
<string name="no_tags_loaded">Non se cargou ningunha etiqueta</string>
|
||||
<string name="no_sources_loaded">Non se cargou ningunha fonte</string>
|
||||
<string name="drawer_loading">Cargando…</string>
|
||||
<string name="menu_home_search">Procurar</string>
|
||||
<string name="can_delete_source">Non se puido eliminar a fonte…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">API de Selfoss</string>
|
||||
<string name="pref_api_items_number_title">Número de elementos cargados</string>
|
||||
<string name="pref_hidden_tags">Etiquetas ocultas</string>
|
||||
<string name="display_header_drawer_summary">Amosar unha cabeceira coa URL da instancia de Selfoss no panel lateral.</string>
|
||||
<string name="display_header_drawer_title">Cabeceira da conta</string>
|
||||
<string name="pref_general_infinite_loading_title">Cargar máis artigos ao desprazarse</string>
|
||||
<string name="translation">Traducción</string>
|
||||
<string name="cant_open_invalid_url">A URL do elemento non é válida. Estou tratando de solucionar isto pra que a aplicación non falle.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Ukuran kartu akan tetap</string>
|
||||
<string name="source_code">Kode sumber</string>
|
||||
<string name="drawer_error_loading_tags">Kesalahan saat memuat tag…</string>
|
||||
<string name="drawer_error_loading_sources">Kesalahan saat memuat sumber…</string>
|
||||
<string name="drawer_item_filters">Filter</string>
|
||||
<string name="drawer_action_clear">kosongkan</string>
|
||||
<string name="drawer_item_tags">Tag</string>
|
||||
<string name="drawer_item_sources">Sumber</string>
|
||||
<string name="drawer_action_edit">suntung</string>
|
||||
<string name="no_tags_loaded">Tidak ada tag yang dimuat</string>
|
||||
<string name="no_sources_loaded">Tak ada sumber yang dimuat</string>
|
||||
<string name="drawer_loading">Memuat …</string>
|
||||
<string name="menu_home_search">Cari</string>
|
||||
<string name="can_delete_source">Tidak dapat menghapus sumber…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Item nomor dimuat</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="display_header_drawer_summary">Kop dengan alamat link Selfoss ditampilkan di laci lateral.</string>
|
||||
<string name="display_header_drawer_title">Kop akun</string>
|
||||
<string name="pref_general_infinite_loading_title">Muat lebih banyak artikel saat membalik halaman</string>
|
||||
<string name="translation">Terjemahan</string>
|
||||
<string name="cant_open_invalid_url">Alamat tautan proyek tidak valid. Saya mencoba memecahkan masalah ini untuk menghindari aplikasi berhenti.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
<string name="source_code">Codice sorgente</string>
|
||||
<string name="drawer_error_loading_tags">Errore nel caricamento dei tag…</string>
|
||||
<string name="drawer_error_loading_sources">Errore nel caricamento delle fonti…</string>
|
||||
<string name="drawer_item_filters">Filtri</string>
|
||||
<string name="drawer_action_clear">cancella</string>
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Fonti</string>
|
||||
<string name="drawer_action_edit">modifica</string>
|
||||
<string name="no_tags_loaded">Nessun tag caricato</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Caricamento…</string>
|
||||
<string name="menu_home_search">Cerca</string>
|
||||
<string name="can_delete_source">Non è possibile eliminare la fonte…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">Api di Selfoss</string>
|
||||
<string name="pref_api_items_number_title">Numero di elementi caricati</string>
|
||||
<string name="pref_hidden_tags">Tag nascosti</string>
|
||||
<string name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
||||
<string name="display_header_drawer_title">Account header</string>
|
||||
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
|
||||
<string name="translation">Traduzioni</string>
|
||||
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
<string name="source_code">Source code</string>
|
||||
<string name="drawer_error_loading_tags">Error loading tags…</string>
|
||||
<string name="drawer_error_loading_sources">Error loading sources…</string>
|
||||
<string name="drawer_item_filters">Filters</string>
|
||||
<string name="drawer_action_clear">clear</string>
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
<string name="menu_home_search">Search</string>
|
||||
<string name="can_delete_source">Can\'t delete the source…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
||||
<string name="display_header_drawer_title">Account header</string>
|
||||
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
|
||||
<string name="translation">Translation</string>
|
||||
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Vaste hoogte</string>
|
||||
<string name="source_code">Broncode</string>
|
||||
<string name="drawer_error_loading_tags">Fout bij het laden van tags…</string>
|
||||
<string name="drawer_error_loading_sources">Fout bij laden van bronnen…</string>
|
||||
<string name="drawer_item_filters">Filters</string>
|
||||
<string name="drawer_action_clear">wissen</string>
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Bronnen</string>
|
||||
<string name="drawer_action_edit">bewerken</string>
|
||||
<string name="no_tags_loaded">Geen tags geladen</string>
|
||||
<string name="no_sources_loaded">Geen bronnen geladen</string>
|
||||
<string name="drawer_loading">Bezig met laden …</string>
|
||||
<string name="menu_home_search">Zoeken</string>
|
||||
<string name="can_delete_source">Kan de bron niet verwijderen…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Geladen items nummer</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="display_header_drawer_summary">Laat een koptekst weergeven met de url van de selfoss instantie in de zijlade.</string>
|
||||
<string name="display_header_drawer_title">Account titel</string>
|
||||
<string name="pref_general_infinite_loading_title">Laad meer artikelen door te bladeren</string>
|
||||
<string name="translation">Vertaling</string>
|
||||
<string name="cant_open_invalid_url">De URL is ongeldig. Ik probeer dit probleem op te lossen, zodat de toepassing niet wordt afgesloten.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Cards com altura de tamanho fixo</string>
|
||||
<string name="source_code">Código fonte</string>
|
||||
<string name="drawer_error_loading_tags">Erro ao carregar as tags…</string>
|
||||
<string name="drawer_error_loading_sources">Erro ao carregar as fontes…</string>
|
||||
<string name="drawer_item_filters">Filtros</string>
|
||||
<string name="drawer_action_clear">limpar</string>
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Fontes</string>
|
||||
<string name="drawer_action_edit">editar</string>
|
||||
<string name="no_tags_loaded">Nenhuma tag carregada</string>
|
||||
<string name="no_sources_loaded">Nenhuma fonte carregada</string>
|
||||
<string name="drawer_loading">Carregando …</string>
|
||||
<string name="menu_home_search">Procurar</string>
|
||||
<string name="can_delete_source">Não foi possível apagar a fonte…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Quantidade de itens carregados</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="display_header_drawer_summary">Exibir um cabeçalho com o URL da instância do Selfoss na barra lateral.</string>
|
||||
<string name="display_header_drawer_title">Cabeçalho da conta</string>
|
||||
<string name="pref_general_infinite_loading_title">Carregar mais artigos ao realizar o scroll</string>
|
||||
<string name="translation">Traduções</string>
|
||||
<string name="cant_open_invalid_url">A url está inválida. Estou tentando resolver esse problema para que o aplicativo não encerre.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Altura do cartão será corrigida</string>
|
||||
<string name="source_code">Código fonte</string>
|
||||
<string name="drawer_error_loading_tags">Erro ao carregar etiquetas…</string>
|
||||
<string name="drawer_error_loading_sources">Erro ao carregar fontes…</string>
|
||||
<string name="drawer_item_filters">Filtros</string>
|
||||
<string name="drawer_action_clear">limpar</string>
|
||||
<string name="drawer_item_tags">Etiquetas</string>
|
||||
<string name="drawer_item_sources">Fontes</string>
|
||||
<string name="drawer_action_edit">editar</string>
|
||||
<string name="no_tags_loaded">Não tags carregado</string>
|
||||
<string name="no_sources_loaded">Não há fontes carregadas</string>
|
||||
<string name="drawer_loading">A carregar…</string>
|
||||
<string name="menu_home_search">Buscar</string>
|
||||
<string name="can_delete_source">Não é possível excluir a fonte…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">Api de Selfoss</string>
|
||||
<string name="pref_api_items_number_title">Número de itens carregados</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="display_header_drawer_summary">Exibir um cabeçalho com o url de instância de selfoss na gaveta lateral.</string>
|
||||
<string name="display_header_drawer_title">Cabeçalho de conta</string>
|
||||
<string name="pref_general_infinite_loading_title">Carregar mais artigos no pergaminho</string>
|
||||
<string name="translation">Tradução</string>
|
||||
<string name="cant_open_invalid_url">A url do item é inválido. Eu estou olhando para resolver esta questão, para que o app não vai falhar.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
<string name="source_code">Source code</string>
|
||||
<string name="drawer_error_loading_tags">Error loading tags…</string>
|
||||
<string name="drawer_error_loading_sources">Error loading sources…</string>
|
||||
<string name="drawer_item_filters">Filters</string>
|
||||
<string name="drawer_action_clear">clear</string>
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
<string name="menu_home_search">Search</string>
|
||||
<string name="can_delete_source">Can\'t delete the source…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
||||
<string name="display_header_drawer_title">Account header</string>
|
||||
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
|
||||
<string name="translation">Translation</string>
|
||||
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Kart yüksekliği sabit olacak</string>
|
||||
<string name="source_code">Kaynak kodu</string>
|
||||
<string name="drawer_error_loading_tags">Etiketler yükleme hatası…</string>
|
||||
<string name="drawer_error_loading_sources">Kaynaklar yüklenirken hata oluştu…</string>
|
||||
<string name="drawer_item_filters">Filtreler</string>
|
||||
<string name="drawer_action_clear">temizle</string>
|
||||
<string name="drawer_item_tags">Etiketler</string>
|
||||
<string name="drawer_item_sources">Kaynaklar</string>
|
||||
<string name="drawer_action_edit">düzenle</string>
|
||||
<string name="no_tags_loaded">Yüklenen görüntü yok</string>
|
||||
<string name="no_sources_loaded">Yüklenen kaynak yok</string>
|
||||
<string name="drawer_loading">Yükleniyor…</string>
|
||||
<string name="menu_home_search">Ara</string>
|
||||
<string name="can_delete_source">Kaynak silinemiyor…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">Selfoss Uygulaması</string>
|
||||
<string name="pref_api_items_number_title">Yüklenen öğe numarası</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="display_header_drawer_summary">Selfoss örneği url\'li bir üstbilgi, yan çekmece üzerine gösterin.</string>
|
||||
<string name="display_header_drawer_title">Hesap başlığı</string>
|
||||
<string name="pref_general_infinite_loading_title">Kaydırma üzerine daha fazla makale yükleyin</string>
|
||||
<string name="translation">Çeviri</string>
|
||||
<string name="cant_open_invalid_url">Öğe url geçersiz. Uygulama çökmeyeceği için bu sorunu çözmeye çalışıyorum.</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">卡片高度将被固定</string>
|
||||
<string name="source_code">源代码</string>
|
||||
<string name="drawer_error_loading_tags">加载标记时出错..。</string>
|
||||
<string name="drawer_error_loading_sources">加载源时出错..。</string>
|
||||
<string name="drawer_item_filters">搜索条件</string>
|
||||
<string name="drawer_action_clear">清空</string>
|
||||
<string name="drawer_item_tags">标签</string>
|
||||
<string name="drawer_item_sources">来源</string>
|
||||
<string name="drawer_action_edit">编辑</string>
|
||||
<string name="no_tags_loaded">未加载标签</string>
|
||||
<string name="no_sources_loaded">未加载数据源</string>
|
||||
<string name="drawer_loading">正在载入…</string>
|
||||
<string name="menu_home_search">搜索</string>
|
||||
<string name="can_delete_source">无法删除数据源…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">塞尔福斯 Api</string>
|
||||
<string name="pref_api_items_number_title">已加载项目编号</string>
|
||||
<string name="pref_hidden_tags">隐藏标签</string>
|
||||
<string name="display_header_drawer_summary">在侧边栏中显示带有 Selfoss 链接地址的页眉。</string>
|
||||
<string name="display_header_drawer_title">帐户页眉</string>
|
||||
<string name="pref_general_infinite_loading_title">翻页时载入更多文章</string>
|
||||
<string name="translation">翻译</string>
|
||||
<string name="cant_open_invalid_url">项目链接地址无效。我正在设法解决这个问题,以避免应用程序崩溃。</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">卡片高度将被固定</string>
|
||||
<string name="source_code">源代码</string>
|
||||
<string name="drawer_error_loading_tags">加载标记时出错..。</string>
|
||||
<string name="drawer_error_loading_sources">加载源时出错..。</string>
|
||||
<string name="drawer_item_filters">搜索条件</string>
|
||||
<string name="drawer_action_clear">清空</string>
|
||||
<string name="drawer_item_tags">标签</string>
|
||||
<string name="drawer_item_sources">来源</string>
|
||||
<string name="drawer_action_edit">编辑</string>
|
||||
<string name="no_tags_loaded">未加载标签</string>
|
||||
<string name="no_sources_loaded">未加载数据源</string>
|
||||
<string name="drawer_loading">正在载入…</string>
|
||||
<string name="menu_home_search">搜索</string>
|
||||
<string name="can_delete_source">无法删除数据源…</string>
|
||||
@ -84,8 +81,6 @@
|
||||
<string name="pref_selfoss_category">塞尔福斯 Api</string>
|
||||
<string name="pref_api_items_number_title">已加载项目编号</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="display_header_drawer_summary">在侧边栏中显示带有 Selfoss 链接地址的页眉。</string>
|
||||
<string name="display_header_drawer_title">帐户页眉</string>
|
||||
<string name="pref_general_infinite_loading_title">翻页时载入更多文章</string>
|
||||
<string name="translation">翻译</string>
|
||||
<string name="cant_open_invalid_url">项目链接地址无效。我正在设法解决这个问题,以避免应用程序崩溃。</string>
|
||||
|
@ -66,14 +66,11 @@
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
<string name="source_code">Source code</string>
|
||||
<string name="drawer_error_loading_tags">Error loading tags…</string>
|
||||
<string name="drawer_error_loading_sources">Error loading sources…</string>
|
||||
<string name="drawer_item_filters">Filters</string>
|
||||
<string name="drawer_action_clear">clear</string>
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
<string name="menu_home_search">Search</string>
|
||||
<string name="can_delete_source">Can\'t delete the source…</string>
|
||||
@ -84,9 +81,6 @@
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string
|
||||
name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
||||
<string name="display_header_drawer_title">Account header</string>
|
||||
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
|
||||
<string name="translation">Translation</string>
|
||||
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
|
||||
|
17
androidApp/src/main/res/xml/data_extraction_rules.xml
Normal file
17
androidApp/src/main/res/xml/data_extraction_rules.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<exclude domain="root" />
|
||||
<exclude domain="file" />
|
||||
<exclude domain="database" />
|
||||
<exclude domain="sharedpref" />
|
||||
<exclude domain="external" />
|
||||
</cloud-backup>
|
||||
<device-transfer>
|
||||
<exclude domain="root" />
|
||||
<exclude domain="file" />
|
||||
<exclude domain="database" />
|
||||
<exclude domain="sharedpref" />
|
||||
<exclude domain="external" />
|
||||
</device-transfer>
|
||||
</data-extraction-rules>
|
@ -60,12 +60,6 @@
|
||||
android:title="@string/pref_general_category_displaying">
|
||||
|
||||
</PreferenceCategory>
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="account_header_displaying"
|
||||
android:summary="@string/display_header_drawer_summary"
|
||||
android:title="@string/display_header_drawer_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="card_view_active"
|
||||
|
@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
# You can pass --force as first parameter to force push and tag creation.
|
||||
|
||||
echo "Creating tag $@"
|
||||
|
||||
TAG="v$@"
|
||||
git tag ${TAG}
|
||||
|
||||
echo "Pushing tag"
|
||||
|
||||
git push origin ${TAG}
|
@ -6,7 +6,7 @@ buildscript {
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
|
||||
classpath("com.android.tools.build:gradle:7.2.2")
|
||||
classpath("com.android.tools.build:gradle:7.3.0")
|
||||
|
||||
// sonarquve
|
||||
classpath("org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513")
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
git fetch --tags -p
|
||||
|
||||
BASE_VERSION="1.7"
|
||||
BASE_VERSION="1"
|
||||
LAST_TAG=$(git tag -l | sort -V | tail -1)
|
||||
|
||||
INITIAL_VERSION="${BASE_VERSION//./}$(date '+%y%m%j')"
|
||||
@ -21,11 +21,11 @@ VERSION="${INITIAL_VERSION}${TODAYS_VERSION}"
|
||||
|
||||
PARAMS_EXCEPT_PUBLISH=$(echo $1 | sed 's/\-\-publish//')
|
||||
|
||||
./version.sh ${VERSION} ${PARAMS_EXCEPT_PUBLISH}
|
||||
./version.sh ${VERSION} ${PARAMS_EXCEPT_PUBLISH} $@
|
||||
|
||||
if [[ "$@" == *'--publish'* ]]
|
||||
then
|
||||
./publish-version.sh ${VERSION}
|
||||
./publish-version.sh ${VERSION} $@
|
||||
else
|
||||
echo "Did not publish. If you wanted to do so, call the script with \"--publish\" or \"--publish-local\"."
|
||||
fi
|
@ -1,3 +1 @@
|
||||
A new RSS reader for <a href="http://selfoss.aditu.de/">selfoss</a>.
|
||||
|
||||
It connects to your selfoss instance (works only with selfoss, and can't work without it), and you'll be able to read and manage all your RSS feeds.
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
#Wed Feb 09 17:05:19 CET 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
15
publish-version.sh
Executable file
15
publish-version.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# NOTE: This is copy/pasted in jenkins
|
||||
|
||||
rm -f version.txt
|
||||
printf "versionName=$1-github\nversionCode=$1" >> version.txt
|
||||
|
||||
if [[ "$@" == *'--from-ci'* ]]
|
||||
then
|
||||
echo "File created. HANDLE IN CI"
|
||||
else
|
||||
# You'll need to change server as your server and define a VERSION_PATH.
|
||||
scp version.txt server:$VERSION_PATH
|
||||
rm version.txt
|
||||
fi
|
@ -112,6 +112,7 @@ android {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
namespace = "bou.amine.apps.readerforselfossv2"
|
||||
}
|
||||
|
||||
sqldelight {
|
||||
|
@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="bou.amine.apps.readerforselfossv2" />
|
||||
<manifest />
|
@ -2,7 +2,14 @@ package bou.amine.apps.readerforselfossv2.model
|
||||
|
||||
import bou.amine.apps.readerforselfossv2.utils.DateUtils
|
||||
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.json.*
|
||||
|
||||
class SelfossModel {
|
||||
|
||||
@ -50,6 +57,7 @@ class SelfossModel {
|
||||
data class Source(
|
||||
val id: Int,
|
||||
val title: String,
|
||||
@Serializable(with = TagsListSerializer::class)
|
||||
val tags: List<String>,
|
||||
val spout: String,
|
||||
val error: String,
|
||||
@ -62,12 +70,15 @@ class SelfossModel {
|
||||
val datetime: String,
|
||||
val title: String,
|
||||
val content: String,
|
||||
@Serializable(with = BooleanSerializer::class)
|
||||
var unread: Boolean,
|
||||
@Serializable(with = BooleanSerializer::class)
|
||||
var starred: Boolean,
|
||||
val thumbnail: String?,
|
||||
val icon: String?,
|
||||
val link: String,
|
||||
val sourcetitle: String,
|
||||
@Serializable(with = TagsListSerializer::class)
|
||||
val tags: List<String>
|
||||
) {
|
||||
// TODO: maybe find a better way to handle these kind of urls
|
||||
@ -105,4 +116,48 @@ class SelfossModel {
|
||||
return this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this seems to be super slow.
|
||||
object TagsListSerializer : KSerializer<List<String>> {
|
||||
override fun deserialize(decoder: Decoder): List<String> {
|
||||
return when(val json = ((decoder as JsonDecoder).decodeJsonElement())) {
|
||||
is JsonArray -> json.toList().map { it.toString() }
|
||||
else -> json.toString().split(",")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: List<String>) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
object BooleanSerializer : KSerializer<Boolean> {
|
||||
override fun deserialize(decoder: Decoder): Boolean {
|
||||
val json = ((decoder as JsonDecoder).decodeJsonElement()).jsonPrimitive
|
||||
return json.booleanOrNull ?: json.int == 1
|
||||
}
|
||||
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = PrimitiveSerialDescriptor("b", PrimitiveKind.BOOLEAN)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
class StatusAndData<T>(val success: Boolean, val data: T? = null) {
|
||||
companion object {
|
||||
fun <T> succes(d: T): StatusAndData<T> {
|
||||
return StatusAndData(true, d)
|
||||
}
|
||||
|
||||
fun <T> error(): StatusAndData<T> {
|
||||
return StatusAndData(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
|
||||
// TODO: Use the updatedSince parameter
|
||||
var fetchedItems: List<SelfossModel.Item>? = null
|
||||
var fetchedItems: SelfossModel.StatusAndData<List<SelfossModel.Item>> = SelfossModel.StatusAndData.error()
|
||||
if (isNetworkAvailable()) {
|
||||
fetchedItems = api.getItems(
|
||||
displayedItems.type,
|
||||
@ -59,23 +59,25 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
)
|
||||
} else {
|
||||
if (appSettingsService.isItemCachingEnabled()) {
|
||||
fetchedItems = getDBItems().filter {
|
||||
displayedItems == ItemType.ALL ||
|
||||
(it.unread && displayedItems == ItemType.UNREAD) ||
|
||||
(it.starred && displayedItems == ItemType.STARRED)
|
||||
}.map { it.toView() }
|
||||
fetchedItems = SelfossModel.StatusAndData.succes(
|
||||
getDBItems().filter {
|
||||
displayedItems == ItemType.ALL ||
|
||||
(it.unread && displayedItems == ItemType.UNREAD) ||
|
||||
(it.starred && displayedItems == ItemType.STARRED)
|
||||
}.map { it.toView() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (fetchedItems != null) {
|
||||
items = ArrayList(fetchedItems)
|
||||
if (fetchedItems.success && fetchedItems.data != null) {
|
||||
items = ArrayList(fetchedItems.data!!)
|
||||
sortItems()
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
suspend fun getOlderItems(): ArrayList<SelfossModel.Item> {
|
||||
var fetchedItems: List<SelfossModel.Item>? = null
|
||||
var fetchedItems: SelfossModel.StatusAndData<List<SelfossModel.Item>> = SelfossModel.StatusAndData.error()
|
||||
if (isNetworkAvailable()) {
|
||||
val offset = items.size
|
||||
fetchedItems = api.getItems(
|
||||
@ -88,16 +90,16 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
)
|
||||
} // When using the db cache, we load everything the first time, so there should be nothing more to load.
|
||||
|
||||
if (fetchedItems != null) {
|
||||
items.addAll(fetchedItems)
|
||||
if (fetchedItems.success && fetchedItems.data != null) {
|
||||
items.addAll(fetchedItems.data!!)
|
||||
sortItems()
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
private suspend fun getMaxItemsForBackground(itemType: ItemType): List<SelfossModel.Item>? {
|
||||
private suspend fun getMaxItemsForBackground(itemType: ItemType): List<SelfossModel.Item> {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.getItems(
|
||||
val items = api.getItems(
|
||||
itemType.type,
|
||||
0,
|
||||
tagFilter?.tag,
|
||||
@ -106,6 +108,11 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
null,
|
||||
200
|
||||
)
|
||||
return if (items.success && items.data != null) {
|
||||
items.data
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
@ -119,10 +126,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
var success = false
|
||||
if (isNetworkAvailable()) {
|
||||
val response = api.stats()
|
||||
if (response != null) {
|
||||
badgeUnread = response.unread
|
||||
badgeAll = response.total
|
||||
badgeStarred = response.starred
|
||||
if (response.success && response.data != null) {
|
||||
badgeUnread = response.data.unread
|
||||
badgeAll = response.data.total
|
||||
badgeStarred = response.data.starred
|
||||
success = true
|
||||
}
|
||||
} else {
|
||||
@ -137,7 +144,11 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
suspend fun getTags(): List<SelfossModel.Tag>? {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.tags()
|
||||
val apiTags = api.tags()
|
||||
if (apiTags.success && apiTags.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
|
||||
resetDBTagsWithData(apiTags.data)
|
||||
}
|
||||
apiTags.data
|
||||
} else {
|
||||
getDBTags().map { it.toView() }
|
||||
}
|
||||
@ -145,16 +156,24 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
suspend fun getSpouts(): Map<String, SelfossModel.Spout>? {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.spouts()
|
||||
val spouts = api.spouts()
|
||||
return if (spouts.success && spouts.data != null) {
|
||||
spouts.data
|
||||
} else {
|
||||
emptyMap() // TODO: do something here
|
||||
}
|
||||
} else {
|
||||
throw NetworkUnavailableException()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getSources(): ArrayList<SelfossModel.Source>? {
|
||||
|
||||
return if (isNetworkAvailable()) {
|
||||
api.sources()
|
||||
val apiSources = api.sources()
|
||||
if (apiSources.success && apiSources.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
|
||||
resetDBSourcesWithData(apiSources.data)
|
||||
}
|
||||
apiSources.data
|
||||
} else {
|
||||
ArrayList(getDBSources().map { it.toView() })
|
||||
}
|
||||
@ -171,7 +190,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
private suspend fun markAsReadById(id: Int): Boolean {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.markAsRead(id.toString())?.isSuccess == true
|
||||
api.markAsRead(id.toString())?.isSuccess
|
||||
} else {
|
||||
insertDBAction(id.toString(), read = true)
|
||||
true
|
||||
@ -190,7 +209,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
private suspend fun unmarkAsReadById(id: Int): Boolean {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.unmarkAsRead(id.toString())?.isSuccess == true
|
||||
api.unmarkAsRead(id.toString())?.isSuccess
|
||||
} else {
|
||||
insertDBAction(id.toString(), unread = true)
|
||||
true
|
||||
@ -208,7 +227,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
private suspend fun starrById(id: Int): Boolean {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.starr(id.toString())?.isSuccess == true
|
||||
api.starr(id.toString())?.isSuccess
|
||||
} else {
|
||||
insertDBAction(id.toString(), starred = true)
|
||||
true
|
||||
@ -226,7 +245,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
private suspend fun unstarrById(id: Int): Boolean {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.unstarr(id.toString())?.isSuccess == true
|
||||
api.unstarr(id.toString())?.isSuccess
|
||||
} else {
|
||||
insertDBAction(id.toString(), starred = true)
|
||||
true
|
||||
@ -236,7 +255,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean {
|
||||
var success = false
|
||||
|
||||
if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess == true) {
|
||||
if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess) {
|
||||
success = true
|
||||
for (item in items) {
|
||||
markAsReadLocally(item)
|
||||
@ -325,7 +344,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
suspend fun updateRemote(): Boolean {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.update()?.equals("finished") ?: false
|
||||
api.update()?.equals("finished")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@ -358,8 +377,8 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
if (isNetworkAvailable()) {
|
||||
val fetchedVersion = api.version()
|
||||
if (fetchedVersion != null && fetchedVersion.getApiMajorVersion() != apiMajorVersion) {
|
||||
appSettingsService.updateApiVersion(fetchedVersion.getApiMajorVersion())
|
||||
if (fetchedVersion.success && fetchedVersion.data != null && fetchedVersion.data.getApiMajorVersion() != apiMajorVersion) {
|
||||
appSettingsService.updateApiVersion(fetchedVersion.data.getApiMajorVersion())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -376,7 +395,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
fun getDBSources(): List<SOURCE> = db.sourcesQueries.sources().executeAsList()
|
||||
|
||||
fun resetDBTagsWithData(tagEntities: List<SelfossModel.Tag>) {
|
||||
private fun resetDBTagsWithData(tagEntities: List<SelfossModel.Tag>) {
|
||||
db.tagsQueries.deleteAllTags()
|
||||
|
||||
db.tagsQueries.transaction {
|
||||
@ -386,7 +405,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
}
|
||||
}
|
||||
|
||||
fun resetDBSourcesWithData(sources: List<SelfossModel.Source>) {
|
||||
private fun resetDBSourcesWithData(sources: List<SelfossModel.Source>) {
|
||||
db.sourcesQueries.deleteAllSources()
|
||||
|
||||
db.sourcesQueries.transaction {
|
||||
@ -418,7 +437,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
val newItems = getMaxItemsForBackground(ItemType.UNREAD)
|
||||
val allItems = getMaxItemsForBackground(ItemType.ALL)
|
||||
val starredItems = getMaxItemsForBackground(ItemType.STARRED)
|
||||
insertDBItems(newItems.orEmpty() + allItems.orEmpty() + starredItems.orEmpty())
|
||||
insertDBItems(newItems + allItems + starredItems)
|
||||
return newItems
|
||||
} catch (e: Throwable) {
|
||||
// We do nothing
|
||||
|
@ -10,6 +10,7 @@ import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.request.forms.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.serialization.json.Json
|
||||
@ -34,7 +35,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
appSettingsService.logApiCalls(message)
|
||||
}
|
||||
}
|
||||
level = LogLevel.ALL
|
||||
level = LogLevel.INFO
|
||||
}
|
||||
install(HttpTimeout) {
|
||||
requestTimeoutMillis = appSettingsService.getApiTimeout()
|
||||
@ -65,11 +66,11 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
client = createHttpClient()
|
||||
}
|
||||
|
||||
suspend fun login(): SelfossModel.SuccessResponse? =
|
||||
client.get(url("/login")) {
|
||||
suspend fun login(): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.get(url("/login")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun getItems(
|
||||
type: String,
|
||||
@ -79,8 +80,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
search: String?,
|
||||
updatedSince: String?,
|
||||
items: Int? = null
|
||||
): List<SelfossModel.Item>? =
|
||||
client.get(url("/items")) {
|
||||
): SelfossModel.StatusAndData<List<SelfossModel.Item>> =
|
||||
bodyOrFailure(client.get(url("/items")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
parameter("type", type)
|
||||
@ -90,74 +91,74 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
parameter("updatedsince", updatedSince)
|
||||
parameter("items", items ?: appSettingsService.getItemsNumber())
|
||||
parameter("offset", offset)
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun stats(): SelfossModel.Stats? =
|
||||
client.get(url("/stats")) {
|
||||
suspend fun stats(): SelfossModel.StatusAndData<SelfossModel.Stats> =
|
||||
bodyOrFailure(client.get(url("/stats")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun tags(): List<SelfossModel.Tag>? =
|
||||
client.get(url("/tags")) {
|
||||
suspend fun tags(): SelfossModel.StatusAndData<List<SelfossModel.Tag>> =
|
||||
bodyOrFailure(client.get(url("/tags")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun update(): String? =
|
||||
client.get(url("/update")) {
|
||||
suspend fun update(): SelfossModel.StatusAndData<String> =
|
||||
bodyOrFailure(client.get(url("/update")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun spouts(): Map<String, SelfossModel.Spout>? =
|
||||
client.get(url("/sources/spouts")) {
|
||||
suspend fun spouts(): SelfossModel.StatusAndData<Map<String, SelfossModel.Spout>> =
|
||||
bodyOrFailure(client.get(url("/sources/spouts")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun sources(): ArrayList<SelfossModel.Source>? =
|
||||
client.get(url("/sources/list")) {
|
||||
suspend fun sources(): SelfossModel.StatusAndData<ArrayList<SelfossModel.Source>> =
|
||||
bodyOrFailure(client.get(url("/sources/list")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun version(): SelfossModel.ApiVersion? =
|
||||
client.get(url("/api/about")).body()
|
||||
suspend fun version(): SelfossModel.StatusAndData<SelfossModel.ApiVersion> =
|
||||
bodyOrFailure(client.get(url("/api/about")))
|
||||
|
||||
suspend fun markAsRead(id: String): SelfossModel.SuccessResponse? =
|
||||
client.post(url("/mark/$id")) {
|
||||
suspend fun markAsRead(id: String): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.post(url("/mark/$id")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse? =
|
||||
client.post(url("/unmark/$id")) {
|
||||
suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.post(url("/unmark/$id")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun starr(id: String): SelfossModel.SuccessResponse? =
|
||||
client.post(url("/starr/$id")) {
|
||||
suspend fun starr(id: String): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.post(url("/starr/$id")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun unstarr(id: String): SelfossModel.SuccessResponse? =
|
||||
client.post(url("/unstarr/$id")) {
|
||||
suspend fun unstarr(id: String): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.post(url("/unstarr/$id")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun markAllAsRead(ids: List<String>): SelfossModel.SuccessResponse? =
|
||||
client.submitForm(
|
||||
suspend fun markAllAsRead(ids: List<String>): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.submitForm(
|
||||
url = url("/mark"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", appSettingsService.getUserName())
|
||||
append("password", appSettingsService.getPassword())
|
||||
ids.map { append("ids[]", it) }
|
||||
}
|
||||
).body()
|
||||
))
|
||||
|
||||
suspend fun createSourceForVersion(
|
||||
title: String,
|
||||
@ -166,12 +167,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
tags: String,
|
||||
filter: String,
|
||||
version: Int
|
||||
): SelfossModel.SuccessResponse? =
|
||||
if (version > 1) {
|
||||
createSource2(title, url, spout, tags, filter)
|
||||
} else {
|
||||
createSource(title, url, spout, tags, filter)
|
||||
}
|
||||
): SelfossModel.SuccessResponse =
|
||||
maybeResponse(
|
||||
if (version > 1) {
|
||||
createSource2(title, url, spout, tags, filter)
|
||||
} else {
|
||||
createSource(title, url, spout, tags, filter)
|
||||
}
|
||||
)
|
||||
|
||||
suspend fun createSource(
|
||||
title: String,
|
||||
@ -179,7 +182,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
spout: String,
|
||||
tags: String,
|
||||
filter: String
|
||||
): SelfossModel.SuccessResponse? =
|
||||
): HttpResponse =
|
||||
client.submitForm(
|
||||
url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"),
|
||||
formParameters = Parameters.build {
|
||||
@ -189,7 +192,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
append("tags", tags)
|
||||
append("filter", filter)
|
||||
}
|
||||
).body()
|
||||
)
|
||||
|
||||
suspend fun createSource2(
|
||||
title: String,
|
||||
@ -197,7 +200,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
spout: String,
|
||||
tags: String,
|
||||
filter: String
|
||||
): SelfossModel.SuccessResponse? =
|
||||
): HttpResponse =
|
||||
client.submitForm(
|
||||
url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"),
|
||||
formParameters = Parameters.build {
|
||||
@ -207,11 +210,27 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
append("tags[]", tags)
|
||||
append("filter", filter)
|
||||
}
|
||||
).body()
|
||||
)
|
||||
|
||||
suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse? =
|
||||
client.delete(url("/source/$id")) {
|
||||
suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.delete(url("/source/$id")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun maybeResponse(r: HttpResponse): SelfossModel.SuccessResponse {
|
||||
return if (r.status.isSuccess()) {
|
||||
r.body()
|
||||
} else {
|
||||
SelfossModel.SuccessResponse(false)
|
||||
}
|
||||
}
|
||||
|
||||
suspend inline fun <reified T> bodyOrFailure(r: HttpResponse): SelfossModel.StatusAndData<T> {
|
||||
return if (r.status.isSuccess()) {
|
||||
SelfossModel.StatusAndData.succes(r.body())
|
||||
} else {
|
||||
SelfossModel.StatusAndData.error()
|
||||
}
|
||||
}
|
||||
}
|
@ -25,7 +25,6 @@ class AppSettingsService {
|
||||
private var _periodicRefresh: Boolean? = null
|
||||
private var _refreshWhenChargingOnly: Boolean? = null
|
||||
private var _infiniteLoading: Boolean? = null
|
||||
private var _displayAccountHeader: Boolean? = null
|
||||
private var _notifyNewItems: Boolean? = null
|
||||
private var _itemsNumber: Int? = null
|
||||
private var _apiTimeout: Long? = null
|
||||
@ -34,9 +33,9 @@ class AppSettingsService {
|
||||
private var _markOnScroll: Boolean? = null
|
||||
private var _activeAlignment: Int? = null
|
||||
|
||||
private var _fontSize: Int? = null // settings.getString("reader_font_size", "16").toInt()
|
||||
private var _staticBar: Boolean? = null // settings.getBoolean("reader_static_bar", false)
|
||||
private var _font: String = "" // settings.getString("reader_font", "")
|
||||
private var _fontSize: Int? = null
|
||||
private var _staticBar: Boolean? = null
|
||||
private var _font: String = ""
|
||||
|
||||
|
||||
init {
|
||||
@ -247,17 +246,6 @@ class AppSettingsService {
|
||||
return _infiniteLoading == true
|
||||
}
|
||||
|
||||
private fun refreshDisplayAccountHeaderEnabled() {
|
||||
_displayAccountHeader = settings.getBoolean("account_header_displaying", false)
|
||||
}
|
||||
|
||||
fun isDisplayAccountHeaderEnabled(): Boolean {
|
||||
if (_displayAccountHeader != null) {
|
||||
refreshDisplayAccountHeaderEnabled()
|
||||
}
|
||||
return _displayAccountHeader == true
|
||||
}
|
||||
|
||||
private fun refreshItemCachingEnabled() {
|
||||
_itemsCaching = settings.getBoolean("items_caching", false)
|
||||
}
|
||||
@ -364,7 +352,6 @@ class AppSettingsService {
|
||||
refreshRefreshMinutes()
|
||||
refreshHiddenTags()
|
||||
refreshInfiniteLoadingEnabled()
|
||||
refreshDisplayAccountHeaderEnabled()
|
||||
refreshItemCachingEnabled()
|
||||
refreshNotifyNewItemsEnabled()
|
||||
refreshMarkOnScrollEnabled()
|
||||
|
14
version.sh
Executable file
14
version.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Creating tag $1"
|
||||
|
||||
TAG="v$1"
|
||||
git tag -a ${TAG} -m ${TAG}
|
||||
|
||||
if [[ "$@" == *'--from-ci'* ]]
|
||||
then
|
||||
echo "Tag created. HANDLE IN CI"
|
||||
else
|
||||
echo "Pushing tag"
|
||||
git push origin ${TAG}
|
||||
fi
|
Reference in New Issue
Block a user