Compare commits
	
		
			25 Commits
		
	
	
		
			v123051211
			...
			4482234e1a
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4482234e1a | |||
| b5de30f561 | |||
| 70ad5f322c | |||
| d167092c83 | |||
| c4f4bafe85 | |||
| ed06b22a77 | |||
| 
						 | 
					172362b533 | ||
| 
						 | 
					ad72cb6f56 | ||
| 
						 | 
					9057ee0052 | ||
| 
						 | 
					50d0b44315 | ||
| 
						 | 
					21b08ed384 | ||
| 
						 | 
					993c4d2ee9 | ||
| 
						 | 
					57a9d51027 | ||
| 
						 | 
					673f0edb8b | ||
| 
						 | 
					7f96798f13 | ||
| 
						 | 
					6e5704a45b | ||
| 
						 | 
					495591159f | ||
| 
						 | 
					718fe7c5ee | ||
| 
						 | 
					ecd23213f9 | ||
| 
						 | 
					e6baed8cb4 | ||
| 
						 | 
					c87abec0b9 | ||
| 
						 | 
					0aba41d8bf | ||
| 
						 | 
					2a2d1047b4 | ||
| 
						 | 
					66ef1ccf32 | ||
| 
						 | 
					677ede5bc7 | 
							
								
								
									
										55
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,3 +1,58 @@
 | 
			
		||||
**v123061811**
 | 
			
		||||
 | 
			
		||||
- feat: Added confirmation dialog for disconnect item menu.
 | 
			
		||||
- Changelog for v123061651 [CI SKIP]
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
**v123061651**
 | 
			
		||||
 | 
			
		||||
- i18n: Translation update.
 | 
			
		||||
- i18n: Translation update.
 | 
			
		||||
- i18n: Translation update.
 | 
			
		||||
- fix: avoid trying to open invalid image urls.
 | 
			
		||||
- Changelog for v123051471 [CI SKIP]
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
**v123051471**
 | 
			
		||||
 | 
			
		||||
- fix: images could be null.
 | 
			
		||||
- fix: Check if color is not empty before parsing it.
 | 
			
		||||
- chore: Removed unused log.
 | 
			
		||||
- Changelog for v123051331 [CI SKIP]
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
**v123051331**
 | 
			
		||||
 | 
			
		||||
- fix: illegal input.
 | 
			
		||||
- Changelog for v123051321 [CI SKIP]
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
**v123051321**
 | 
			
		||||
 | 
			
		||||
- debug: Debug null context.
 | 
			
		||||
- Changelog for v123051301 [CI SKIP]
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
**v123051301**
 | 
			
		||||
 | 
			
		||||
- feat: Basic auth from url. Fixes #142 (#143)
 | 
			
		||||
- debug: Debug index out of bound exception.
 | 
			
		||||
- Changelog for v123051211 [CI SKIP]
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
**v123051211**
 | 
			
		||||
 | 
			
		||||
- fix: Sometimes url isn't even defined.
 | 
			
		||||
- Changelog for v123041021 [CI SKIP]
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
**v123041021**
 | 
			
		||||
 | 
			
		||||
- fix: 'Enable Core Library Desugaring to support older Android versions' (#138) from davidoskky/ReaderForSelfoss-multiplatform:desugaring into master
 | 
			
		||||
 
 | 
			
		||||
@@ -599,12 +599,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            R.id.action_disconnect -> {
 | 
			
		||||
                runBlocking {
 | 
			
		||||
                    repository.logout()
 | 
			
		||||
                needsConfirmation(R.string.confirm_disconnect_title, R.string.confirm_disconnect_description) {
 | 
			
		||||
                    runBlocking {
 | 
			
		||||
                        repository.logout()
 | 
			
		||||
                    }
 | 
			
		||||
                    val intent = Intent(this, LoginActivity::class.java)
 | 
			
		||||
                    this.startActivity(intent)
 | 
			
		||||
                    finish()
 | 
			
		||||
                }
 | 
			
		||||
                val intent = Intent(this, LoginActivity::class.java)
 | 
			
		||||
                this.startActivity(intent)
 | 
			
		||||
                finish()
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            R.id.action_settings -> {
 | 
			
		||||
 
 | 
			
		||||
@@ -139,9 +139,12 @@ class LoginActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
 | 
			
		||||
        showProgress(true)
 | 
			
		||||
 | 
			
		||||
        appSettingsService.updateSelfSigned(binding.selfSigned.isChecked)
 | 
			
		||||
 | 
			
		||||
        repository.refreshLoginInformation(url, login, password)
 | 
			
		||||
 | 
			
		||||
        CoroutineScope(Dispatchers.Main).launch {
 | 
			
		||||
            repository.updateApiInformation()
 | 
			
		||||
            val result = repository.login()
 | 
			
		||||
            if (result) {
 | 
			
		||||
                val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance()
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,11 @@ class ReaderActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
            finish()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        readItem(allItems[currentItem])
 | 
			
		||||
        try {
 | 
			
		||||
            readItem(allItems[currentItem])
 | 
			
		||||
        } catch (e: IndexOutOfBoundsException) {
 | 
			
		||||
            finish()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        binding.pager.adapter = ScreenSlidePagerAdapter(this)
 | 
			
		||||
        binding.pager.setCurrentItem(currentItem, false)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ import bou.amine.apps.readerforselfossv2.android.model.toModel
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.model.toParcelable
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.utils.isUrlValid
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.utils.shareLink
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.model.MercuryModel
 | 
			
		||||
@@ -314,7 +315,7 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
        binding.webcontent.webViewClient = object : WebViewClient() {
 | 
			
		||||
            @Deprecated("Deprecated in Java")
 | 
			
		||||
            override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
 | 
			
		||||
                return if (context != null && binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
 | 
			
		||||
                return if (context != null && url.isUrlValid() && binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
 | 
			
		||||
                    } catch (e: ActivityNotFoundException) {
 | 
			
		||||
@@ -530,7 +531,12 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
 | 
			
		||||
    private fun openInBrowserAfterFailing() {
 | 
			
		||||
        binding.progressBar.visibility = View.GONE
 | 
			
		||||
        requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item)
 | 
			
		||||
        if (context != null) {
 | 
			
		||||
            requireContext().openInBrowserAsNewTask(this@ArticleFragment.item)
 | 
			
		||||
        } else {
 | 
			
		||||
            Exception("openInBrowserAfterFailing context is null").sendSilentlyWithAcraWithName("openInBrowserAfterFailing > $context")
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
@@ -548,8 +554,8 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun performClick(): Boolean {
 | 
			
		||||
        if (binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
 | 
			
		||||
            binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
 | 
			
		||||
        if (allImages != null && (binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
 | 
			
		||||
            binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE)
 | 
			
		||||
        ) {
 | 
			
		||||
 | 
			
		||||
            val position: Int = allImages.indexOf(binding.webcontent.hitTestResult.extra)
 | 
			
		||||
 
 | 
			
		||||
@@ -149,21 +149,23 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
 | 
			
		||||
            c.ellipsize = TextUtils.TruncateAt.END
 | 
			
		||||
            c.text = tag.tag
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                val gd = GradientDrawable()
 | 
			
		||||
                val gdColor = try {
 | 
			
		||||
                    Color.parseColor(tag.color)
 | 
			
		||||
                } catch (e: IllegalArgumentException) {
 | 
			
		||||
                    e.sendSilentlyWithAcraWithName("color issue " + tag.color)
 | 
			
		||||
                    resources.getColor(R.color.colorPrimary)
 | 
			
		||||
            if (tag.color.isNotEmpty()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    val gd = GradientDrawable()
 | 
			
		||||
                    val gdColor = try {
 | 
			
		||||
                        Color.parseColor(tag.color)
 | 
			
		||||
                    } catch (e: IllegalArgumentException) {
 | 
			
		||||
                        e.sendSilentlyWithAcraWithName("color issue " + tag.color)
 | 
			
		||||
                        resources.getColor(R.color.colorPrimary)
 | 
			
		||||
                    }
 | 
			
		||||
                    gd.setColor(gdColor)
 | 
			
		||||
                    gd.shape = GradientDrawable.RECTANGLE
 | 
			
		||||
                    gd.setSize(30, 30)
 | 
			
		||||
                    gd.cornerRadius = 30F
 | 
			
		||||
                    c.chipIcon = gd
 | 
			
		||||
                } catch (e: Exception) {
 | 
			
		||||
                    e.sendSilentlyWithAcraWithName("tags > GradientDrawable")
 | 
			
		||||
                }
 | 
			
		||||
                gd.setColor(gdColor)
 | 
			
		||||
                gd.shape = GradientDrawable.RECTANGLE
 | 
			
		||||
                gd.setSize(30, 30)
 | 
			
		||||
                gd.cornerRadius = 30F
 | 
			
		||||
                c.chipIcon = gd
 | 
			
		||||
            } catch (e: Exception) {
 | 
			
		||||
                e.sendSilentlyWithAcraWithName("tags > GradientDrawable")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            c.setOnCloseIconClickListener {
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,13 @@
 | 
			
		||||
                android:maxLines="1"
 | 
			
		||||
                android:minHeight="48dp" />
 | 
			
		||||
 | 
			
		||||
            <com.google.android.material.switchmaterial.SwitchMaterial
 | 
			
		||||
                android:id="@+id/selfSigned"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:text="@string/disable_ssl"
 | 
			
		||||
                android:textAlignment="viewStart" />
 | 
			
		||||
 | 
			
		||||
            <com.google.android.material.switchmaterial.SwitchMaterial
 | 
			
		||||
                android:id="@+id/withLogin"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -33,8 +33,8 @@
 | 
			
		||||
    <string name="addStringNoUrl">"Accede pra engadir fontes."</string>
 | 
			
		||||
    <string name="cant_get_sources">"Non se pode obter a lista de fontes."</string>
 | 
			
		||||
    <string name="cant_create_source">"Non se pode crear unha fonte."</string>
 | 
			
		||||
    <string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
 | 
			
		||||
    <string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string>
 | 
			
		||||
    <string name="cant_get_spouts_no_network">"Non se pode obter a lista de spouts por mor dun erro de rede."</string>
 | 
			
		||||
    <string name="cant_get_spouts">"Non se pode obter a lista de spoits. Pode que haxa algún problema coa api."</string>
 | 
			
		||||
    <string name="form_not_complete">"O formulario non está completo"</string>
 | 
			
		||||
    <string name="pref_header_links">"Ligazóns"</string>
 | 
			
		||||
    <string name="issue_tracker_link">"Rastrexador de Incidencias"</string>
 | 
			
		||||
@@ -116,16 +116,19 @@
 | 
			
		||||
    <string name="reader_static_bar_on">A barra inferior mostrarase sempre</string>
 | 
			
		||||
    <string name="reader_static_bar_off">A barra inferior pode mostrarse a través do botón flotante</string>
 | 
			
		||||
    <string name="remove_source">Eliminar fonte</string>
 | 
			
		||||
    <string name="pref_theme_title">Light/Dark mode</string>
 | 
			
		||||
    <string name="mode_dark">Dark mode</string>
 | 
			
		||||
    <string name="mode_system">Follow the system setting</string>
 | 
			
		||||
    <string name="mode_light">Light mode</string>
 | 
			
		||||
    <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
 | 
			
		||||
    <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
 | 
			
		||||
    <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
 | 
			
		||||
    <string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
 | 
			
		||||
    <string name="menu_home_filter">Filters</string>
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="pref_theme_title">Modo Claro/Escuro</string>
 | 
			
		||||
    <string name="mode_dark">Modo escuro</string>
 | 
			
		||||
    <string name="mode_system">Seguir axustes do sistema</string>
 | 
			
		||||
    <string name="mode_light">Modo claro</string>
 | 
			
		||||
    <string name="gdpr_dialog_title">A aplicación non comparte ningún dato persoal seu.</string>
 | 
			
		||||
    <string name="gdpr_dialog_message"><![CDATA[O envío de informes de erros está habilitado. Pode deshabilitarse dende a páxina de axustes. Ten en conta que os informes de erros son esenciais para o desenvolvemento da aplicación.]]></string>
 | 
			
		||||
    <string name="crash_toast_text">Ocurriu un erro. Enviando os detalles o desenvolvedor.</string>
 | 
			
		||||
    <string name="pref_switch_disable_acra">"Deshabilitar o reporte automático de erros. "</string>
 | 
			
		||||
    <string name="menu_home_filter">Filtros</string>
 | 
			
		||||
    <string name="application_selfoss_only">Esta aplicación só funciona cunha instancia de Selfoss, e con ningún outro filtro RSS.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -90,7 +90,7 @@
 | 
			
		||||
    <string name="pref_switch_items_caching">Save items for offline use</string>
 | 
			
		||||
    <string name="pref_switch_update_sources">Check for new sources and tags</string>
 | 
			
		||||
    <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
 | 
			
		||||
    <string name="network_connectivity_lost">"Network connection lost"</string>
 | 
			
		||||
    <string name="network_connectivity_lost">"Koneksi jaringan hilang"</string>
 | 
			
		||||
    <string name="network_connectivity_retrieved">"Network connection is now available"</string>
 | 
			
		||||
    <string name="pref_switch_periodic_refresh">Sync articles</string>
 | 
			
		||||
    <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources xmlns:tools="http://schemas.android.com/tools">
 | 
			
		||||
<resources>
 | 
			
		||||
    <string name="app_name">"Lettore RSS per Selfoss"</string>
 | 
			
		||||
    <string name="title_activity_login">"Accedi"</string>
 | 
			
		||||
    <string name="prompt_password">"Password"</string>
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
 | 
			
		||||
    <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
 | 
			
		||||
    <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
 | 
			
		||||
    <string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
 | 
			
		||||
    <string name="menu_home_filter">Filters</string>
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
</resources>
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -125,7 +125,10 @@
 | 
			
		||||
    <string name="crash_toast_text">发生崩溃。请将细节发送给开发人员。</string>
 | 
			
		||||
    <string name="pref_switch_disable_acra">"禁用自动错误报告 "</string>
 | 
			
		||||
    <string name="menu_home_filter">筛选器</string>
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="application_selfoss_only">此应用只适用于 Selfoss 实例,不适用于其他 RSS 。</string>
 | 
			
		||||
    <string name="menu_home_sources">源</string>
 | 
			
		||||
    <string name="update_source">更新源</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -128,4 +128,7 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
    <string name="disable_ssl">Disable SSL</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
    <string name="error_invalid_password">"Password not long enough"</string>
 | 
			
		||||
    <string name="error_field_required">"Field required"</string>
 | 
			
		||||
    <string name="prompt_url">"Url"</string>
 | 
			
		||||
    <string name="disable_ssl">"Disable SSL"</string>
 | 
			
		||||
    <string name="withLoginSwitch">"Login required ?"</string>
 | 
			
		||||
    <string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
 | 
			
		||||
    <string name="prompt_login">"Username"</string>
 | 
			
		||||
@@ -131,4 +132,6 @@
 | 
			
		||||
    <string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
 | 
			
		||||
    <string name="menu_home_sources">Sources</string>
 | 
			
		||||
    <string name="update_source">Update source</string>
 | 
			
		||||
    <string name="confirm_disconnect_title">Disconnect ?</string>
 | 
			
		||||
    <string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -29,13 +29,17 @@ kotlin {
 | 
			
		||||
    sourceSets {
 | 
			
		||||
        val commonMain by getting {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                implementation("io.ktor:ktor-client-core:2.1.1")
 | 
			
		||||
                implementation("io.ktor:ktor-client-content-negotiation:2.1.1")
 | 
			
		||||
                implementation("io.ktor:ktor-serialization-kotlinx-json:2.1.1")
 | 
			
		||||
                implementation("io.ktor:ktor-client-logging:2.1.1")
 | 
			
		||||
                val ktorVersion = "2.3.2"
 | 
			
		||||
 | 
			
		||||
                implementation("io.ktor:ktor-client-core:$ktorVersion")
 | 
			
		||||
                implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
 | 
			
		||||
                implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
 | 
			
		||||
                implementation("io.ktor:ktor-client-logging:$ktorVersion")
 | 
			
		||||
                implementation("io.ktor:ktor-client-auth:$ktorVersion")
 | 
			
		||||
                implementation("io.ktor:ktor-client-cio:$ktorVersion")
 | 
			
		||||
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
 | 
			
		||||
                implementation("io.ktor:ktor-client-auth:2.1.1")
 | 
			
		||||
                implementation("org.jsoup:jsoup:1.14.3")
 | 
			
		||||
 | 
			
		||||
                implementation("org.jsoup:jsoup:1.15.4")
 | 
			
		||||
 | 
			
		||||
                //Dependency Injection
 | 
			
		||||
                implementation("org.kodein.di:kodein-di:7.12.0")
 | 
			
		||||
@@ -58,7 +62,8 @@ kotlin {
 | 
			
		||||
        }
 | 
			
		||||
        val androidMain by getting {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                implementation("io.ktor:ktor-client-okhttp:2.1.1")
 | 
			
		||||
                implementation("com.squareup.okhttp3:okhttp:4.10.0")
 | 
			
		||||
                implementation("io.ktor:ktor-client-okhttp:2.2.4")
 | 
			
		||||
                implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
 | 
			
		||||
 | 
			
		||||
                // Sql
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,17 @@
 | 
			
		||||
package bou.amine.apps.readerforselfossv2.rest
 | 
			
		||||
 | 
			
		||||
import io.ktor.client.engine.cio.CIOEngineConfig
 | 
			
		||||
import java.security.cert.X509Certificate
 | 
			
		||||
import javax.net.ssl.X509TrustManager
 | 
			
		||||
 | 
			
		||||
class NaiveTrustManager : X509TrustManager {
 | 
			
		||||
    override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
 | 
			
		||||
 | 
			
		||||
    override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
 | 
			
		||||
 | 
			
		||||
    override fun getAcceptedIssuers(): Array<out X509Certificate> = arrayOf()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) {
 | 
			
		||||
    config.https.trustManager = NaiveTrustManager()
 | 
			
		||||
}
 | 
			
		||||
@@ -6,12 +6,12 @@ class MercuryModel {
 | 
			
		||||
 | 
			
		||||
    @Serializable
 | 
			
		||||
    class ParsedContent(
 | 
			
		||||
        val title: String?,
 | 
			
		||||
        val content: String?,
 | 
			
		||||
        val lead_image_url: String?, // NOSONAR
 | 
			
		||||
        val url: String?,
 | 
			
		||||
        val error: Boolean?,
 | 
			
		||||
        val message: String?,
 | 
			
		||||
        val failed: Boolean?
 | 
			
		||||
        val title: String? = null,
 | 
			
		||||
        val content: String? = null,
 | 
			
		||||
        val lead_image_url: String? = null, // NOSONAR
 | 
			
		||||
        val url: String? = null,
 | 
			
		||||
        val error: Boolean? = null,
 | 
			
		||||
        val message: String? = null,
 | 
			
		||||
        val failed: Boolean? = null
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,8 @@ class SelfossModel {
 | 
			
		||||
    @Serializable
 | 
			
		||||
    class Stats(
 | 
			
		||||
        val total: Int,
 | 
			
		||||
        val unread: Int?,
 | 
			
		||||
        val starred: Int?
 | 
			
		||||
        val unread: Int? = null,
 | 
			
		||||
        val starred: Int? = null
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @Serializable
 | 
			
		||||
@@ -36,9 +36,9 @@ class SelfossModel {
 | 
			
		||||
 | 
			
		||||
    @Serializable
 | 
			
		||||
    data class ApiInformation(
 | 
			
		||||
        val version: String?,
 | 
			
		||||
        val apiversion: String?,
 | 
			
		||||
        val configuration: ApiConfiguration?
 | 
			
		||||
        val version: String? = null,
 | 
			
		||||
        val apiversion: String? = null,
 | 
			
		||||
        val configuration: ApiConfiguration? = null
 | 
			
		||||
    ) {
 | 
			
		||||
        fun getApiMajorVersion(): Int {
 | 
			
		||||
            var versionNumber = 0
 | 
			
		||||
@@ -54,9 +54,9 @@ class SelfossModel {
 | 
			
		||||
    @Serializable
 | 
			
		||||
    data class ApiConfiguration(
 | 
			
		||||
        @Serializable(with = BooleanSerializer::class)
 | 
			
		||||
        val publicMode: Boolean?,
 | 
			
		||||
        val publicMode: Boolean? = null,
 | 
			
		||||
        @Serializable(with = BooleanSerializer::class)
 | 
			
		||||
        val authEnabled: Boolean?
 | 
			
		||||
        val authEnabled: Boolean? = null
 | 
			
		||||
    ) {
 | 
			
		||||
        fun isAuthEnabled() = authEnabled ?: true
 | 
			
		||||
 | 
			
		||||
@@ -75,7 +75,7 @@ class SelfossModel {
 | 
			
		||||
    data class SourceStats(
 | 
			
		||||
        override val id: Int,
 | 
			
		||||
        override var title: String,
 | 
			
		||||
        override var unread: Int?,
 | 
			
		||||
        override var unread: Int? = null,
 | 
			
		||||
        override var error: String? = null,
 | 
			
		||||
        override var icon: String? = null
 | 
			
		||||
        ) : Source
 | 
			
		||||
@@ -86,11 +86,11 @@ class SelfossModel {
 | 
			
		||||
        override var title: String,
 | 
			
		||||
        override var unread: Int? = null,
 | 
			
		||||
        @Serializable(with = TagsListSerializer::class)
 | 
			
		||||
        var tags: List<String>?,
 | 
			
		||||
        var spout: String?,
 | 
			
		||||
        override var error: String?,
 | 
			
		||||
        override var icon: String?,
 | 
			
		||||
        var params: SourceParams?
 | 
			
		||||
        var tags: List<String>? = null,
 | 
			
		||||
        var spout: String? = null,
 | 
			
		||||
        override var error: String? = null,
 | 
			
		||||
        override var icon: String? = null,
 | 
			
		||||
        var params: SourceParams? = null
 | 
			
		||||
    ) : Source
 | 
			
		||||
 | 
			
		||||
    @Serializable
 | 
			
		||||
@@ -107,13 +107,13 @@ class SelfossModel {
 | 
			
		||||
        var unread: Boolean,
 | 
			
		||||
        @Serializable(with = BooleanSerializer::class)
 | 
			
		||||
        var starred: Boolean,
 | 
			
		||||
        val thumbnail: String?,
 | 
			
		||||
        val icon: String?,
 | 
			
		||||
        val thumbnail: String? = null,
 | 
			
		||||
        val icon: String? = null,
 | 
			
		||||
        val link: String,
 | 
			
		||||
        val sourcetitle: String,
 | 
			
		||||
        @Serializable(with = TagsListSerializer::class)
 | 
			
		||||
        val tags: List<String>,
 | 
			
		||||
        val author: String?
 | 
			
		||||
        val author: String? = null
 | 
			
		||||
    ) {
 | 
			
		||||
        fun getLinkDecoded(): String {
 | 
			
		||||
            var stringUrl: String
 | 
			
		||||
 
 | 
			
		||||
@@ -5,78 +5,105 @@ import bou.amine.apps.readerforselfossv2.model.StatusAndData
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.model.SuccessResponse
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
 | 
			
		||||
import io.github.aakira.napier.Napier
 | 
			
		||||
import io.ktor.client.*
 | 
			
		||||
import io.ktor.client.plugins.*
 | 
			
		||||
import io.ktor.client.plugins.cache.*
 | 
			
		||||
import io.ktor.client.plugins.contentnegotiation.*
 | 
			
		||||
import io.ktor.client.plugins.cookies.*
 | 
			
		||||
import io.ktor.client.plugins.logging.*
 | 
			
		||||
import io.ktor.client.request.*
 | 
			
		||||
import io.ktor.client.statement.*
 | 
			
		||||
import io.ktor.http.*
 | 
			
		||||
import io.ktor.serialization.kotlinx.json.*
 | 
			
		||||
import io.ktor.client.HttpClient
 | 
			
		||||
import io.ktor.client.engine.cio.CIO
 | 
			
		||||
import io.ktor.client.engine.cio.CIOEngineConfig
 | 
			
		||||
import io.ktor.client.plugins.HttpRequestRetry
 | 
			
		||||
import io.ktor.client.plugins.HttpTimeout
 | 
			
		||||
import io.ktor.client.plugins.auth.providers.BasicAuthCredentials
 | 
			
		||||
import io.ktor.client.plugins.cache.HttpCache
 | 
			
		||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
 | 
			
		||||
import io.ktor.client.plugins.cookies.HttpCookies
 | 
			
		||||
import io.ktor.client.plugins.logging.LogLevel
 | 
			
		||||
import io.ktor.client.plugins.logging.Logger
 | 
			
		||||
import io.ktor.client.plugins.logging.Logging
 | 
			
		||||
import io.ktor.client.request.get
 | 
			
		||||
import io.ktor.client.request.headers
 | 
			
		||||
import io.ktor.client.request.parameter
 | 
			
		||||
import io.ktor.client.statement.HttpResponse
 | 
			
		||||
import io.ktor.http.HttpHeaders
 | 
			
		||||
import io.ktor.http.HttpStatusCode
 | 
			
		||||
import io.ktor.http.Parameters
 | 
			
		||||
import io.ktor.serialization.kotlinx.json.json
 | 
			
		||||
import io.ktor.util.encodeBase64
 | 
			
		||||
import io.ktor.utils.io.charsets.Charsets
 | 
			
		||||
import io.ktor.utils.io.core.toByteArray
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
 | 
			
		||||
expect fun setupInsecureHTTPEngine(config: CIOEngineConfig)
 | 
			
		||||
 | 
			
		||||
fun createHttpClient(
 | 
			
		||||
    appSettingsService: AppSettingsService,
 | 
			
		||||
    api: SelfossApi
 | 
			
		||||
) =
 | 
			
		||||
    HttpClient(CIO) {
 | 
			
		||||
        if (appSettingsService.getSelfSigned()) {
 | 
			
		||||
            engine {
 | 
			
		||||
                setupInsecureHTTPEngine(this)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        install(ContentNegotiation) {
 | 
			
		||||
            install(HttpCache)
 | 
			
		||||
            json(Json {
 | 
			
		||||
                prettyPrint = true
 | 
			
		||||
                isLenient = true
 | 
			
		||||
                ignoreUnknownKeys = true
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
        install(Logging) {
 | 
			
		||||
            logger = object : Logger {
 | 
			
		||||
                override fun log(message: String) {
 | 
			
		||||
                    Napier.d(message, tag = "LogApiCalls")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            level = LogLevel.INFO
 | 
			
		||||
        }
 | 
			
		||||
        install(HttpTimeout) {
 | 
			
		||||
            requestTimeoutMillis = appSettingsService.getApiTimeout()
 | 
			
		||||
        }
 | 
			
		||||
        install(HttpCookies)
 | 
			
		||||
        install(HttpRequestRetry) {
 | 
			
		||||
            maxRetries = 2
 | 
			
		||||
            retryIf { _, response ->
 | 
			
		||||
                response.status == HttpStatusCode.Forbidden && api.shouldHavePostLogin() && api.hasLoginInfo()
 | 
			
		||||
            }
 | 
			
		||||
            modifyRequest {
 | 
			
		||||
                Napier.i("Will modify", tag = "HttpSend")
 | 
			
		||||
                CoroutineScope(Dispatchers.Main).launch {
 | 
			
		||||
                    Napier.i("Will login", tag = "HttpSend")
 | 
			
		||||
                    api.login()
 | 
			
		||||
                    Napier.i("Did login", tag = "HttpSend")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        expectSuccess = false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
 | 
			
		||||
    var client = createHttpClient()
 | 
			
		||||
 | 
			
		||||
    private fun createHttpClient(): HttpClient {
 | 
			
		||||
        val client = HttpClient {
 | 
			
		||||
            install(ContentNegotiation) {
 | 
			
		||||
                install(HttpCache)
 | 
			
		||||
                json(Json {
 | 
			
		||||
                    prettyPrint = true
 | 
			
		||||
                    isLenient = true
 | 
			
		||||
                    ignoreUnknownKeys = true
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
            install(Logging) {
 | 
			
		||||
                logger = object : Logger {
 | 
			
		||||
                    override fun log(message: String) {
 | 
			
		||||
                        Napier.d(message, tag = "LogApiCalls")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                level = LogLevel.INFO
 | 
			
		||||
            }
 | 
			
		||||
            install(HttpTimeout) {
 | 
			
		||||
                requestTimeoutMillis = appSettingsService.getApiTimeout()
 | 
			
		||||
            }
 | 
			
		||||
            install(HttpCookies)
 | 
			
		||||
            install(HttpRequestRetry) {
 | 
			
		||||
                maxRetries = 2
 | 
			
		||||
                retryIf { _, response ->
 | 
			
		||||
                    response.status == HttpStatusCode.Forbidden && shouldHavePostLogin() && hasLoginInfo()
 | 
			
		||||
                }
 | 
			
		||||
                modifyRequest {
 | 
			
		||||
                    Napier.i("Will modify", tag = "HttpSend")
 | 
			
		||||
                    CoroutineScope(Dispatchers.Main).launch {
 | 
			
		||||
                        Napier.i("Will login", tag = "HttpSend")
 | 
			
		||||
                        this@SelfossApi.login()
 | 
			
		||||
                        Napier.i("Did login", tag = "HttpSend")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            expectSuccess = false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return client
 | 
			
		||||
    }
 | 
			
		||||
    var client = createHttpClient(appSettingsService, this)
 | 
			
		||||
 | 
			
		||||
    fun url(path: String) =
 | 
			
		||||
        "${appSettingsService.getBaseUrl()}$path"
 | 
			
		||||
 | 
			
		||||
    fun refreshLoginInformation() {
 | 
			
		||||
        appSettingsService.refreshApiSettings()
 | 
			
		||||
        client = createHttpClient()
 | 
			
		||||
        client = createHttpClient(appSettingsService, this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun constructBasicAuthValue(credentials: BasicAuthCredentials): String {
 | 
			
		||||
        val authString = "${credentials.username}:${credentials.password}"
 | 
			
		||||
        val authBuf = authString.toByteArray(Charsets.UTF_8).encodeBase64()
 | 
			
		||||
 | 
			
		||||
        return "Basic $authBuf"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Api version was introduces after the POST login, so when there is a version, it should be available
 | 
			
		||||
    private fun shouldHavePostLogin() = appSettingsService.getApiVersion() != -1
 | 
			
		||||
    private fun hasLoginInfo() =
 | 
			
		||||
    fun shouldHavePostLogin() = appSettingsService.getApiVersion() != -1
 | 
			
		||||
    fun hasLoginInfo() =
 | 
			
		||||
        appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword()
 | 
			
		||||
            .isNotEmpty()
 | 
			
		||||
 | 
			
		||||
@@ -96,11 +123,23 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
    private suspend fun getLogin() = maybeResponse(client.tryToGet(url("/login")) {
 | 
			
		||||
        parameter("username", appSettingsService.getUserName())
 | 
			
		||||
        parameter("password", appSettingsService.getPassword())
 | 
			
		||||
        if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
            headers {
 | 
			
		||||
                append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    private suspend fun postLogin() = maybeResponse(client.tryToPost(url("/login")) {
 | 
			
		||||
        parameter("username", appSettingsService.getUserName())
 | 
			
		||||
        parameter("password", appSettingsService.getPassword())
 | 
			
		||||
        if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
            headers {
 | 
			
		||||
                append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    private fun shouldHaveNewLogout() =
 | 
			
		||||
@@ -114,9 +153,23 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private suspend fun maybeLogoutIfAvailable() =
 | 
			
		||||
        responseOrSuccessIf404(client.tryToGet(url("/logout")))
 | 
			
		||||
        responseOrSuccessIf404(client.tryToGet(url("/logout")) {
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    private suspend fun doLogout() = maybeResponse(client.tryToDelete(url("/api/session/current")))
 | 
			
		||||
    private suspend fun doLogout() = maybeResponse(client.tryToDelete(url("/api/session/current")) {
 | 
			
		||||
        if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
            headers {
 | 
			
		||||
                append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    suspend fun getItems(
 | 
			
		||||
        type: String,
 | 
			
		||||
@@ -139,6 +192,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
            parameter("updatedsince", updatedSince)
 | 
			
		||||
            parameter("items", items ?: appSettingsService.getItemsNumber())
 | 
			
		||||
            parameter("offset", offset)
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun getItemsWithoutCatch(): StatusAndData<List<SelfossModel.Item>> =
 | 
			
		||||
@@ -149,6 +208,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
            }
 | 
			
		||||
            parameter("type", "all")
 | 
			
		||||
            parameter("items", 1)
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun stats(): StatusAndData<SelfossModel.Stats> =
 | 
			
		||||
@@ -157,6 +222,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                parameter("username", appSettingsService.getUserName())
 | 
			
		||||
                parameter("password", appSettingsService.getPassword())
 | 
			
		||||
            }
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun tags(): StatusAndData<List<SelfossModel.Tag>> =
 | 
			
		||||
@@ -165,6 +236,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                parameter("username", appSettingsService.getUserName())
 | 
			
		||||
                parameter("password", appSettingsService.getPassword())
 | 
			
		||||
            }
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun update(): StatusAndData<String> =
 | 
			
		||||
@@ -173,6 +250,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                parameter("username", appSettingsService.getUserName())
 | 
			
		||||
                parameter("password", appSettingsService.getPassword())
 | 
			
		||||
            }
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun spouts(): StatusAndData<Map<String, SelfossModel.Spout>> =
 | 
			
		||||
@@ -181,6 +264,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                parameter("username", appSettingsService.getUserName())
 | 
			
		||||
                parameter("password", appSettingsService.getPassword())
 | 
			
		||||
            }
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun sourcesStats(): StatusAndData<ArrayList<SelfossModel.SourceStats>> =
 | 
			
		||||
@@ -189,6 +278,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                parameter("username", appSettingsService.getUserName())
 | 
			
		||||
                parameter("password", appSettingsService.getPassword())
 | 
			
		||||
            }
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun sourcesDetailed(): StatusAndData<ArrayList<SelfossModel.SourceDetail>> =
 | 
			
		||||
@@ -197,10 +292,23 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                parameter("username", appSettingsService.getUserName())
 | 
			
		||||
                parameter("password", appSettingsService.getPassword())
 | 
			
		||||
            }
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun apiInformation(): StatusAndData<SelfossModel.ApiInformation> =
 | 
			
		||||
        bodyOrFailure(client.tryToGet(url("/api/about")))
 | 
			
		||||
        bodyOrFailure(client.tryToGet(url("/api/about")) {
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun markAsRead(id: String): SuccessResponse =
 | 
			
		||||
        maybeResponse(client.tryToPost(url("/mark/$id")) {
 | 
			
		||||
@@ -208,6 +316,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                parameter("username", appSettingsService.getUserName())
 | 
			
		||||
                parameter("password", appSettingsService.getPassword())
 | 
			
		||||
            }
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun unmarkAsRead(id: String): SuccessResponse =
 | 
			
		||||
@@ -216,6 +330,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                parameter("username", appSettingsService.getUserName())
 | 
			
		||||
                parameter("password", appSettingsService.getPassword())
 | 
			
		||||
            }
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun starr(id: String): SuccessResponse =
 | 
			
		||||
@@ -224,6 +344,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                parameter("username", appSettingsService.getUserName())
 | 
			
		||||
                parameter("password", appSettingsService.getPassword())
 | 
			
		||||
            }
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun unstarr(id: String): SuccessResponse =
 | 
			
		||||
@@ -232,6 +358,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                parameter("username", appSettingsService.getUserName())
 | 
			
		||||
                parameter("password", appSettingsService.getPassword())
 | 
			
		||||
            }
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
    suspend fun markAllAsRead(ids: List<String>): SuccessResponse =
 | 
			
		||||
@@ -243,6 +375,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                    append("password", appSettingsService.getPassword())
 | 
			
		||||
                }
 | 
			
		||||
                ids.map { append("ids[]", it) }
 | 
			
		||||
            },
 | 
			
		||||
            block = {
 | 
			
		||||
                if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                    headers {
 | 
			
		||||
                        append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ))
 | 
			
		||||
 | 
			
		||||
@@ -278,6 +418,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                append("url", url)
 | 
			
		||||
                append("spout", spout)
 | 
			
		||||
                append(tagsParamName, tags)
 | 
			
		||||
            },
 | 
			
		||||
            block = {
 | 
			
		||||
                if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                    headers {
 | 
			
		||||
                        append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@@ -315,6 +463,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                append("url", url)
 | 
			
		||||
                append("spout", spout)
 | 
			
		||||
                append(tagsParamName, tags)
 | 
			
		||||
            },
 | 
			
		||||
            block = {
 | 
			
		||||
                if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                    headers {
 | 
			
		||||
                        append(HttpHeaders.Authorization, constructBasicAuthValue(BasicAuthCredentials(username = appSettingsService.getBasicUserName(), password = appSettingsService.getBasicPassword()))
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@@ -324,5 +480,18 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                parameter("username", appSettingsService.getUserName())
 | 
			
		||||
                parameter("password", appSettingsService.getPassword())
 | 
			
		||||
            }
 | 
			
		||||
            if (appSettingsService.getBasicUserName().isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()) {
 | 
			
		||||
                headers {
 | 
			
		||||
                    append(
 | 
			
		||||
                        HttpHeaders.Authorization,
 | 
			
		||||
                        constructBasicAuthValue(
 | 
			
		||||
                            BasicAuthCredentials(
 | 
			
		||||
                                username = appSettingsService.getBasicUserName(),
 | 
			
		||||
                                password = appSettingsService.getBasicPassword()
 | 
			
		||||
                            )
 | 
			
		||||
                        )
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
}
 | 
			
		||||
@@ -8,9 +8,12 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
    // Api related
 | 
			
		||||
    private var _apiVersion: Int = -1
 | 
			
		||||
    private var _publicAccess: Boolean? = null
 | 
			
		||||
    private var _selfSigned: Boolean? = null
 | 
			
		||||
    private var _baseUrl: String = ""
 | 
			
		||||
    private var _userName: String = ""
 | 
			
		||||
    private var _basicUserName: String = ""
 | 
			
		||||
    private var _password: String = ""
 | 
			
		||||
    private var _basicPassword: String = ""
 | 
			
		||||
 | 
			
		||||
    // User settings related
 | 
			
		||||
    private var _itemsCaching: Boolean? = null
 | 
			
		||||
@@ -75,6 +78,22 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
        _publicAccess = settings.getBoolean(API_PUBLIC_ACCESS, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getSelfSigned(): Boolean {
 | 
			
		||||
        if (_selfSigned == null) {
 | 
			
		||||
            refreshSelfSigned()
 | 
			
		||||
        }
 | 
			
		||||
        return _selfSigned!!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateSelfSigned(selfSigned: Boolean) {
 | 
			
		||||
        settings.putBoolean(API_SELF_SIGNED, selfSigned)
 | 
			
		||||
        refreshSelfSigned()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun refreshSelfSigned() {
 | 
			
		||||
        _selfSigned = settings.getBoolean(API_SELF_SIGNED, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getBaseUrl(): String {
 | 
			
		||||
        if (_baseUrl.isEmpty()) {
 | 
			
		||||
            refreshBaseUrl()
 | 
			
		||||
@@ -96,6 +115,20 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
        return _password
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getBasicUserName(): String {
 | 
			
		||||
        if (_basicUserName.isEmpty()) {
 | 
			
		||||
            refreshBasicUsername()
 | 
			
		||||
        }
 | 
			
		||||
        return _basicUserName
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getBasicPassword(): String {
 | 
			
		||||
        if (_basicPassword.isEmpty()) {
 | 
			
		||||
            refreshBasicPassword()
 | 
			
		||||
        }
 | 
			
		||||
        return _basicPassword
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getItemsNumber(): Int {
 | 
			
		||||
        if (_itemsNumber == null) {
 | 
			
		||||
            refreshItemsNumber()
 | 
			
		||||
@@ -149,6 +182,14 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
        _password = settings.getString(PASSWORD, "")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun refreshBasicUsername() {
 | 
			
		||||
        _basicUserName = settings.getString(BASIC_LOGIN, "")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun refreshBasicPassword() {
 | 
			
		||||
        _basicPassword = settings.getString(BASIC_PASSWORD, "")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun refreshArticleViewerEnabled() {
 | 
			
		||||
        _articleViewer = settings.getBoolean(PREFER_ARTICLE_VIEWER, true)
 | 
			
		||||
    }
 | 
			
		||||
@@ -354,9 +395,12 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
    fun refreshApiSettings() {
 | 
			
		||||
        refreshPassword()
 | 
			
		||||
        refreshUsername()
 | 
			
		||||
        refreshBasicUsername()
 | 
			
		||||
        refreshBasicPassword()
 | 
			
		||||
        refreshBaseUrl()
 | 
			
		||||
        refreshApiVersion()
 | 
			
		||||
        refreshPublicAccess()
 | 
			
		||||
        refreshSelfSigned()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun refreshUserSettings() {
 | 
			
		||||
@@ -387,7 +431,17 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
        login: String,
 | 
			
		||||
        password: String
 | 
			
		||||
    ) {
 | 
			
		||||
        settings.putString(BASE_URL, url)
 | 
			
		||||
        val regex = """\/\/(\D+):(\D+)@""".toRegex()
 | 
			
		||||
        val matchResult = regex.find(url)
 | 
			
		||||
        if (matchResult != null) {
 | 
			
		||||
            val (basicLogin, basicPassword) = matchResult.destructured
 | 
			
		||||
            settings.putString(BASIC_LOGIN, basicLogin)
 | 
			
		||||
            settings.putString(BASIC_PASSWORD, basicPassword)
 | 
			
		||||
            val urlWithoutBasicAuth = url.replace(regex, "//")
 | 
			
		||||
            settings.putString(BASE_URL, urlWithoutBasicAuth)
 | 
			
		||||
        } else {
 | 
			
		||||
            settings.putString(BASE_URL, url)
 | 
			
		||||
        }
 | 
			
		||||
        settings.putString(LOGIN, login)
 | 
			
		||||
        settings.putString(PASSWORD, password)
 | 
			
		||||
        refreshApiSettings()
 | 
			
		||||
@@ -397,6 +451,8 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
        settings.remove(BASE_URL)
 | 
			
		||||
        settings.remove(LOGIN)
 | 
			
		||||
        settings.remove(PASSWORD)
 | 
			
		||||
        settings.remove(BASIC_LOGIN)
 | 
			
		||||
        settings.remove(BASIC_PASSWORD)
 | 
			
		||||
        refreshApiSettings()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -430,6 +486,8 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
 | 
			
		||||
        const val API_PUBLIC_ACCESS = "apiPublicAccess"
 | 
			
		||||
 | 
			
		||||
        const val API_SELF_SIGNED = "apiSelfSigned"
 | 
			
		||||
 | 
			
		||||
        const val API_ITEMS_NUMBER = "prefer_api_items_number"
 | 
			
		||||
 | 
			
		||||
        const val API_TIMEOUT = "api_timeout"
 | 
			
		||||
@@ -440,6 +498,10 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
 | 
			
		||||
        const val PASSWORD = "password"
 | 
			
		||||
 | 
			
		||||
        const val BASIC_LOGIN = "basic_login"
 | 
			
		||||
 | 
			
		||||
        const val BASIC_PASSWORD = "basic_password"
 | 
			
		||||
 | 
			
		||||
        const val PREFER_ARTICLE_VIEWER = "prefer_article_viewer"
 | 
			
		||||
 | 
			
		||||
        const val CARD_VIEW_ACTIVE = "card_view_active"
 | 
			
		||||
@@ -470,7 +532,6 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
 | 
			
		||||
 | 
			
		||||
        const val PERIODIC_REFRESH_MINUTES = "periodic_refresh_minutes"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        const val INFINITE_LOADING = "infinite_loading"
 | 
			
		||||
 | 
			
		||||
        const val ITEMS_CACHING = "items_caching"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user