Compare commits
	
		
			19 Commits
		
	
	
		
			27eafe4ff4
			...
			v122123641
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					c718b966a1 | ||
| 
						 | 
					99438e142f | ||
| 
						 | 
					4d8076c3cf | ||
| 
						 | 
					db75c5b74a | ||
| 
						 | 
					966a082147 | ||
| 
						 | 
					cd20a5ec29 | ||
| 
						 | 
					cc4c1c9201 | ||
| 
						 | 
					ff021d572c | ||
| 
						 | 
					89992967be | ||
| 
						 | 
					3c68bde62b | ||
| 
						 | 
					c38251f5b3 | ||
| 
						 | 
					a01f6d2322 | ||
| 
						 | 
					417a33eb25 | ||
| 
						 | 
					2e7f7f23b3 | ||
| 
						 | 
					e5e182761e | ||
| 
						 | 
					a094d88799 | ||
| e51915d1cd | |||
| 3a654f6ede | |||
| 5227751dca | 
							
								
								
									
										10
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								.drone.yml
									
									
									
									
									
								
							@@ -35,7 +35,6 @@ steps:
 | 
			
		||||
trigger:
 | 
			
		||||
  event:
 | 
			
		||||
    - push
 | 
			
		||||
    - pull_request
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
kind: pipeline
 | 
			
		||||
@@ -43,12 +42,19 @@ type: docker
 | 
			
		||||
name: Publish
 | 
			
		||||
 | 
			
		||||
steps:
 | 
			
		||||
  - name: createTag
 | 
			
		||||
  - name: createTagAndChangelog
 | 
			
		||||
    image: ubuntu:latest
 | 
			
		||||
    commands:
 | 
			
		||||
      - apt-get update && apt-get install -y git
 | 
			
		||||
      - git fetch --tags -p
 | 
			
		||||
      - VER=$(git describe --tags --abbrev=0)
 | 
			
		||||
      - CHANGELOG=$(git log $VER..HEAD --pretty="- %s")
 | 
			
		||||
      - echo "**$VER**\n\n$CHANGELOG\n\n--------------------------------------------------------------------\n\n$(cat CHANGELOG.md)" > CHANGELOG.md
 | 
			
		||||
      - git add CHANGELOG.md
 | 
			
		||||
      - git commit -m "Changelog for $VER [CI SKIP]"
 | 
			
		||||
      - ./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 master
 | 
			
		||||
      - git push pushing --tags
 | 
			
		||||
    environment:
 | 
			
		||||
      TZ: Europe/Paris
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,3 +1,23 @@
 | 
			
		||||
**v122123631**
 | 
			
		||||
 | 
			
		||||
- build: Added back maven repos (see https://gitlab.com/fdroid/fdroiddata/-/commit/1fb9d60dc58511abc2bb4eb321977922a0682c8b#note_1223925153)
 | 
			
		||||
- build: Added back maven repos (see https://gitlab.com/fdroid/fdroiddata/-/commit/1fb9d60dc58511abc2bb4eb321977922a0682c8b#note_1223925153)
 | 
			
		||||
- debug: trying to resolve `Canvas: trying to use a recycled bitmap`.
 | 
			
		||||
- fix: NPE may be caused by the binding or the title that was null.
 | 
			
		||||
- chore: Skip drone pipeline on changelog push.
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
**v122123621**
 | 
			
		||||
 | 
			
		||||
- fix: Automatic CHANGELOG generation.
 | 
			
		||||
- Merge pull request 'Sources Upsert' (#119) from sources-edit into master
 | 
			
		||||
- Source update screen.
 | 
			
		||||
- Sources menu.
 | 
			
		||||
- chore: Automatic CHANGELOG generation.
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# V2/Multiplatform rewrite
 | 
			
		||||
 | 
			
		||||
**v1**
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@
 | 
			
		||||
                android:value=".HomeActivity" />
 | 
			
		||||
        </activity>
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".AddSourceActivity"
 | 
			
		||||
            android:name=".UpsertSourceActivity"
 | 
			
		||||
            android:parentActivityName=".SourcesActivity"
 | 
			
		||||
            android:exported="true">
 | 
			
		||||
            <meta-data
 | 
			
		||||
 
 | 
			
		||||
@@ -603,6 +603,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
 | 
			
		||||
                settingsLauncher.launch(Intent(this, SettingsActivity::class.java))
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            R.id.action_sources -> {
 | 
			
		||||
                startActivity(Intent(this, SourcesActivity::class.java))
 | 
			
		||||
                return true
 | 
			
		||||
            }
 | 
			
		||||
            else -> return super.onOptionsItemSelected(item)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,7 @@ package bou.amine.apps.readerforselfossv2.android
 | 
			
		||||
import android.app.NotificationChannel
 | 
			
		||||
import android.app.NotificationManager
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.graphics.drawable.Drawable
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.widget.ImageView
 | 
			
		||||
import android.widget.Toast
 | 
			
		||||
import androidx.lifecycle.DefaultLifecycleObserver
 | 
			
		||||
import androidx.lifecycle.LifecycleOwner
 | 
			
		||||
@@ -18,8 +15,6 @@ import bou.amine.apps.readerforselfossv2.dao.DriverFactory
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.repository.Repository
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
 | 
			
		||||
import com.bumptech.glide.Glide
 | 
			
		||||
import com.bumptech.glide.request.RequestOptions
 | 
			
		||||
import com.github.ln_12.library.ConnectivityStatus
 | 
			
		||||
import io.github.aakira.napier.DebugAntilog
 | 
			
		||||
import io.github.aakira.napier.Napier
 | 
			
		||||
@@ -83,6 +78,8 @@ class MyApp : MultiDexApplication(), DIAware {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        repository.migrate(driverFactory)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun attachBaseContext(base: Context?) {
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,7 @@ class SourcesActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        binding.fab.setOnClickListener {
 | 
			
		||||
            startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java))
 | 
			
		||||
            startActivity(Intent(this@SourcesActivity, UpsertSourceActivity::class.java))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,15 @@ package bou.amine.apps.readerforselfossv2.android
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.*
 | 
			
		||||
import android.widget.AdapterView
 | 
			
		||||
import android.widget.ArrayAdapter
 | 
			
		||||
import android.widget.TextView
 | 
			
		||||
import android.widget.Toast
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity
 | 
			
		||||
import androidx.constraintlayout.widget.ConstraintLayout
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityAddSourceBinding
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityUpsertSourceBinding
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.repository.Repository
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
@@ -19,38 +22,59 @@ import org.kodein.di.android.closestDI
 | 
			
		||||
import org.kodein.di.instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AddSourceActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
class UpsertSourceActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
 | 
			
		||||
    private var existingSource: SelfossModel.Source? = null
 | 
			
		||||
    private var mSpoutsValue: String? = null
 | 
			
		||||
 | 
			
		||||
    private lateinit var binding: ActivityAddSourceBinding
 | 
			
		||||
    private lateinit var binding: ActivityUpsertSourceBinding
 | 
			
		||||
 | 
			
		||||
    override val di by closestDI()
 | 
			
		||||
    private val repository : Repository by instance()
 | 
			
		||||
    private val appSettingsService : AppSettingsService by instance()
 | 
			
		||||
    private val repository: Repository by instance()
 | 
			
		||||
    private val appSettingsService: AppSettingsService by instance()
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
        binding = ActivityAddSourceBinding.inflate(layoutInflater)
 | 
			
		||||
        binding = ActivityUpsertSourceBinding.inflate(layoutInflater)
 | 
			
		||||
        val view = binding.root
 | 
			
		||||
 | 
			
		||||
        existingSource = repository.getSelectedSource()
 | 
			
		||||
        if (existingSource != null) {
 | 
			
		||||
            binding.formContainer.visibility = View.GONE
 | 
			
		||||
            binding.progress.visibility = View.VISIBLE
 | 
			
		||||
        }
 | 
			
		||||
        val title = if (existingSource == null) R.string.add_source else R.string.update_source
 | 
			
		||||
 | 
			
		||||
        supportFragmentManager.addOnBackStackChangedListener {
 | 
			
		||||
            if (supportFragmentManager.backStackEntryCount == 0) {
 | 
			
		||||
                setTitle(title)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setContentView(view)
 | 
			
		||||
 | 
			
		||||
        setSupportActionBar(binding.toolbar)
 | 
			
		||||
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
 | 
			
		||||
        supportActionBar?.setDisplayShowHomeEnabled(true)
 | 
			
		||||
        supportActionBar?.title = resources.getString(title)
 | 
			
		||||
 | 
			
		||||
        maybeGetDetailsFromIntentSharing(intent, binding.sourceUri, binding.nameInput)
 | 
			
		||||
 | 
			
		||||
        maybeGetDetailsFromIntentSharing(intent)
 | 
			
		||||
 | 
			
		||||
        binding.saveBtn.setOnClickListener {
 | 
			
		||||
            handleSaveSource(
 | 
			
		||||
                binding.tags,
 | 
			
		||||
                binding.nameInput.text.toString(),
 | 
			
		||||
                binding.sourceUri.text.toString()
 | 
			
		||||
            )
 | 
			
		||||
            handleSaveSource()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun initFields(items: Map<String, SelfossModel.Spout>) {
 | 
			
		||||
        binding.nameInput.setText(existingSource!!.title)
 | 
			
		||||
        binding.tags.setText(existingSource!!.tags.joinToString(", "))
 | 
			
		||||
        binding.sourceUri.setText(existingSource!!.params?.url)
 | 
			
		||||
        binding.spoutsSpinner.setSelection(items.keys.indexOf(existingSource!!.spout))
 | 
			
		||||
        binding.progress.visibility = View.GONE
 | 
			
		||||
        binding.formContainer.visibility = View.VISIBLE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onResume() {
 | 
			
		||||
        super.onResume()
 | 
			
		||||
 | 
			
		||||
@@ -58,17 +82,13 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
        if (baseUrl.isEmpty() || baseUrl.isBaseUrlInvalid()) {
 | 
			
		||||
            mustLoginToAddSource()
 | 
			
		||||
        } else {
 | 
			
		||||
            handleSpoutsSpinner(binding.spoutsSpinner, binding.progress, binding.formContainer)
 | 
			
		||||
            handleSpoutsSpinner()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun handleSpoutsSpinner(
 | 
			
		||||
        spoutsSpinner: Spinner,
 | 
			
		||||
        mProgress: ProgressBar,
 | 
			
		||||
        formContainer: ConstraintLayout
 | 
			
		||||
    ) {
 | 
			
		||||
    private fun handleSpoutsSpinner() {
 | 
			
		||||
        val spoutsKV = HashMap<String, String>()
 | 
			
		||||
        spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
 | 
			
		||||
        binding.spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
 | 
			
		||||
            override fun onItemSelected(adapterView: AdapterView<*>, view: View?, i: Int, l: Long) {
 | 
			
		||||
                if (view != null) {
 | 
			
		||||
                    val spoutName = (view as TextView).text.toString()
 | 
			
		||||
@@ -84,11 +104,11 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
 | 
			
		||||
        fun handleSpoutFailure(networkIssue: Boolean = false) {
 | 
			
		||||
            Toast.makeText(
 | 
			
		||||
                this@AddSourceActivity,
 | 
			
		||||
                this@UpsertSourceActivity,
 | 
			
		||||
                if (networkIssue) R.string.cant_get_spouts_no_network else R.string.cant_get_spouts,
 | 
			
		||||
                Toast.LENGTH_SHORT
 | 
			
		||||
            ).show()
 | 
			
		||||
            mProgress.visibility = View.GONE
 | 
			
		||||
            binding.progress.visibility = View.GONE
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoroutineScope(Dispatchers.Main).launch {
 | 
			
		||||
@@ -100,17 +120,21 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
                        spoutsKV[value.name] = key
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    mProgress.visibility = View.GONE
 | 
			
		||||
                    formContainer.visibility = View.VISIBLE
 | 
			
		||||
                    binding.progress.visibility = View.GONE
 | 
			
		||||
                    binding.formContainer.visibility = View.VISIBLE
 | 
			
		||||
 | 
			
		||||
                    val spinnerArrayAdapter =
 | 
			
		||||
                        ArrayAdapter(
 | 
			
		||||
                            this@AddSourceActivity,
 | 
			
		||||
                            this@UpsertSourceActivity,
 | 
			
		||||
                            android.R.layout.simple_spinner_item,
 | 
			
		||||
                            itemsStrings
 | 
			
		||||
                        )
 | 
			
		||||
                    spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
 | 
			
		||||
                    spoutsSpinner.adapter = spinnerArrayAdapter
 | 
			
		||||
                    binding.spoutsSpinner.adapter = spinnerArrayAdapter
 | 
			
		||||
 | 
			
		||||
                    if (existingSource != null) {
 | 
			
		||||
                        initFields(items)
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    handleSpoutFailure()
 | 
			
		||||
                }
 | 
			
		||||
@@ -121,13 +145,11 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun maybeGetDetailsFromIntentSharing(
 | 
			
		||||
        intent: Intent,
 | 
			
		||||
        sourceUri: EditText,
 | 
			
		||||
        nameInput: EditText
 | 
			
		||||
        intent: Intent
 | 
			
		||||
    ) {
 | 
			
		||||
        if (Intent.ACTION_SEND == intent.action && "text/plain" == intent.type) {
 | 
			
		||||
            sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT))
 | 
			
		||||
            nameInput.setText(intent.getStringExtra(Intent.EXTRA_TITLE))
 | 
			
		||||
            binding.sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT))
 | 
			
		||||
            binding.nameInput.setText(intent.getStringExtra(Intent.EXTRA_TITLE))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -138,7 +160,8 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
        finish()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun handleSaveSource(tags: EditText, title: String, url: String) {
 | 
			
		||||
    private fun handleSaveSource() {
 | 
			
		||||
        val url = binding.sourceUri.text.toString()
 | 
			
		||||
 | 
			
		||||
        val sourceDetailsUnavailable =
 | 
			
		||||
            title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty()
 | 
			
		||||
@@ -149,18 +172,27 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
            }
 | 
			
		||||
            else -> {
 | 
			
		||||
                CoroutineScope(Dispatchers.Main).launch {
 | 
			
		||||
                    val successfullyAddedSource = repository.createSource(
 | 
			
		||||
                        title,
 | 
			
		||||
                        url,
 | 
			
		||||
                        mSpoutsValue!!,
 | 
			
		||||
                        tags.text.toString(),
 | 
			
		||||
                        "",
 | 
			
		||||
                    )
 | 
			
		||||
                    val successfullyAddedSource = if (existingSource != null) {
 | 
			
		||||
                        repository.updateSource(
 | 
			
		||||
                            existingSource!!.id,
 | 
			
		||||
                            binding.nameInput.text.toString(),
 | 
			
		||||
                            url,
 | 
			
		||||
                            mSpoutsValue!!,
 | 
			
		||||
                            binding.tags.text.toString()
 | 
			
		||||
                        )
 | 
			
		||||
                    } else {
 | 
			
		||||
                        repository.createSource(
 | 
			
		||||
                            binding.nameInput.text.toString(),
 | 
			
		||||
                            url,
 | 
			
		||||
                            mSpoutsValue!!,
 | 
			
		||||
                            binding.tags.text.toString(),
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                    if (successfullyAddedSource) {
 | 
			
		||||
                        finish()
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Toast.makeText(
 | 
			
		||||
                            this@AddSourceActivity,
 | 
			
		||||
                            this@UpsertSourceActivity,
 | 
			
		||||
                            R.string.cant_create_source,
 | 
			
		||||
                            Toast.LENGTH_SHORT
 | 
			
		||||
                        ).show()
 | 
			
		||||
@@ -169,4 +201,9 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDestroy() {
 | 
			
		||||
        super.onDestroy()
 | 
			
		||||
        repository.unsetSelectedSource()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,12 +2,14 @@ package bou.amine.apps.readerforselfossv2.android.adapters
 | 
			
		||||
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.widget.Button
 | 
			
		||||
import android.widget.Toast
 | 
			
		||||
import androidx.constraintlayout.widget.ConstraintLayout
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.UpsertSourceActivity
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.R
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.databinding.SourceListItemBinding
 | 
			
		||||
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
 | 
			
		||||
@@ -94,6 +96,14 @@ class SourcesListAdapter(
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            mView.setOnClickListener {
 | 
			
		||||
                val source = items[bindingAdapterPosition]
 | 
			
		||||
 | 
			
		||||
                repository.setSelectedSource(source)
 | 
			
		||||
                app.startActivity(Intent(app, UpsertSourceActivity::class.java))
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -66,8 +66,7 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
    private lateinit var allImages : ArrayList<String>
 | 
			
		||||
    private lateinit var fab: FloatingActionButton
 | 
			
		||||
    private lateinit var textAlignment: String
 | 
			
		||||
    private var _binding: FragmentArticleBinding? = null
 | 
			
		||||
    private val binding get() = _binding
 | 
			
		||||
    private lateinit var binding: FragmentArticleBinding
 | 
			
		||||
 | 
			
		||||
    override val di : DI by closestDI()
 | 
			
		||||
    private val repository: Repository by instance()
 | 
			
		||||
@@ -95,7 +94,7 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
        savedInstanceState: Bundle?
 | 
			
		||||
    ): View {
 | 
			
		||||
        try {
 | 
			
		||||
            _binding = FragmentArticleBinding.inflate(inflater, container, false)
 | 
			
		||||
            binding = FragmentArticleBinding.inflate(inflater, container, false)
 | 
			
		||||
 | 
			
		||||
            url = item.getLinkDecoded()
 | 
			
		||||
            contentText = item.content
 | 
			
		||||
@@ -110,13 +109,13 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
 | 
			
		||||
            refreshAlignment()
 | 
			
		||||
 | 
			
		||||
            fab = binding!!.fab
 | 
			
		||||
            fab = binding.fab
 | 
			
		||||
 | 
			
		||||
            fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent))
 | 
			
		||||
 | 
			
		||||
            fab.rippleColor = resources.getColor(R.color.colorAccentDark)
 | 
			
		||||
 | 
			
		||||
            val floatingToolbar: FloatingToolbar = binding!!.floatingToolbar
 | 
			
		||||
            val floatingToolbar: FloatingToolbar = binding.floatingToolbar
 | 
			
		||||
            floatingToolbar.attachFab(fab)
 | 
			
		||||
 | 
			
		||||
            floatingToolbar.background = ColorDrawable(resources.getColor(R.color.colorAccent))
 | 
			
		||||
@@ -164,35 +163,35 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
                floatingToolbar.show()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            binding!!.source.text = contentSource
 | 
			
		||||
            binding.source.text = contentSource
 | 
			
		||||
            if (typeface != null) {
 | 
			
		||||
                binding!!.source.typeface = typeface
 | 
			
		||||
                binding.source.typeface = typeface
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (contentText.isEmptyOrNullOrNullString()) {
 | 
			
		||||
                getContentFromMercury()
 | 
			
		||||
            } else {
 | 
			
		||||
                binding!!.titleView.text = contentTitle
 | 
			
		||||
                binding.titleView.text = contentTitle
 | 
			
		||||
                if (typeface != null) {
 | 
			
		||||
                    binding!!.titleView.typeface = typeface
 | 
			
		||||
                    binding.titleView.typeface = typeface
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                htmlToWebview()
 | 
			
		||||
 | 
			
		||||
                if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
 | 
			
		||||
                    binding!!.imageView.visibility = View.VISIBLE
 | 
			
		||||
                    binding.imageView.visibility = View.VISIBLE
 | 
			
		||||
                    Glide
 | 
			
		||||
                        .with(requireContext())
 | 
			
		||||
                        .asBitmap()
 | 
			
		||||
                        .load(contentImage)
 | 
			
		||||
                        .apply(RequestOptions.fitCenterTransform())
 | 
			
		||||
                        .into(binding!!.imageView)
 | 
			
		||||
                        .into(binding.imageView)
 | 
			
		||||
                } else {
 | 
			
		||||
                    binding!!.imageView.visibility = View.GONE
 | 
			
		||||
                    binding.imageView.visibility = View.GONE
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            binding!!.nestedScrollView.setOnScrollChangeListener(
 | 
			
		||||
            binding.nestedScrollView.setOnScrollChangeListener(
 | 
			
		||||
                NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
 | 
			
		||||
                    if (scrollY > oldScrollY) {
 | 
			
		||||
                        floatingToolbar.hide()
 | 
			
		||||
@@ -221,12 +220,7 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
                .show()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return binding!!.root
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDestroyView() {
 | 
			
		||||
        super.onDestroyView()
 | 
			
		||||
        _binding = null
 | 
			
		||||
        return binding.root
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun refreshAlignment() {
 | 
			
		||||
@@ -239,16 +233,16 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
 | 
			
		||||
    private fun getContentFromMercury() {
 | 
			
		||||
        if (repository.isNetworkAvailable()) {
 | 
			
		||||
            binding!!.progressBar.visibility = View.VISIBLE
 | 
			
		||||
            binding.progressBar.visibility = View.VISIBLE
 | 
			
		||||
 | 
			
		||||
            CoroutineScope(Dispatchers.Main).launch {
 | 
			
		||||
                try {
 | 
			
		||||
                    val response = mercuryApi.query(url)
 | 
			
		||||
                    if (response.success && response.data != null && !response.data?.content.isNullOrEmpty()) {
 | 
			
		||||
                        binding!!.titleView.text = response.data!!.title.orEmpty()
 | 
			
		||||
                        binding.titleView.text = response.data!!.title.orEmpty()
 | 
			
		||||
                        try {
 | 
			
		||||
                            if (typeface != null) {
 | 
			
		||||
                                binding!!.titleView.typeface = typeface
 | 
			
		||||
                                binding.titleView.typeface = typeface
 | 
			
		||||
                            }
 | 
			
		||||
                        } catch (e: Exception) {
 | 
			
		||||
                            e.sendSilentlyWithAcraWithName("getContentFromMercury > typeface")
 | 
			
		||||
@@ -272,7 +266,7 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
 | 
			
		||||
                        if (!response.data?.lead_image_url.isNullOrEmpty() && context != null) {
 | 
			
		||||
                            try {
 | 
			
		||||
                                binding!!.imageView.visibility = View.VISIBLE
 | 
			
		||||
                                binding.imageView.visibility = View.VISIBLE
 | 
			
		||||
                                try {
 | 
			
		||||
                                    Glide
 | 
			
		||||
                                        .with(requireContext())
 | 
			
		||||
@@ -281,7 +275,7 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
                                            response.data!!.lead_image_url.orEmpty()
 | 
			
		||||
                                        )
 | 
			
		||||
                                        .apply(RequestOptions.fitCenterTransform())
 | 
			
		||||
                                        .into(binding!!.imageView)
 | 
			
		||||
                                        .into(binding.imageView)
 | 
			
		||||
                                } catch (e: Exception) {
 | 
			
		||||
                                    e.sendSilentlyWithAcraWithName("getContentFromMercury > glide lead image")
 | 
			
		||||
                                }
 | 
			
		||||
@@ -289,12 +283,12 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
                                e.sendSilentlyWithAcraWithName("getContentFromMercury > outside glide lead image")
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            binding!!.imageView.visibility = View.GONE
 | 
			
		||||
                            binding.imageView.visibility = View.GONE
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        try {
 | 
			
		||||
                            binding!!.nestedScrollView.scrollTo(0, 0)
 | 
			
		||||
                            binding!!.progressBar.visibility = View.GONE
 | 
			
		||||
                            binding.nestedScrollView.scrollTo(0, 0)
 | 
			
		||||
                            binding.progressBar.visibility = View.GONE
 | 
			
		||||
                        } catch (e: Exception) {
 | 
			
		||||
                            e.sendSilentlyWithAcraWithName("getContentFromMercury > scrollview")
 | 
			
		||||
                        }
 | 
			
		||||
@@ -317,8 +311,8 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
        val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        binding!!.webcontent.settings.standardFontFamily = a.getString(0)
 | 
			
		||||
        binding!!.webcontent.visibility = View.VISIBLE
 | 
			
		||||
        binding.webcontent.settings.standardFontFamily = a.getString(0)
 | 
			
		||||
        binding.webcontent.visibility = View.VISIBLE
 | 
			
		||||
 | 
			
		||||
        val colorOnSurface = TypedValue()
 | 
			
		||||
        requireContext().theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true)
 | 
			
		||||
@@ -326,14 +320,14 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
        val colorSurface = TypedValue()
 | 
			
		||||
        requireContext().theme.resolveAttribute(R.attr.colorSurface, colorSurface, true)
 | 
			
		||||
 | 
			
		||||
        binding!!.webcontent.settings.useWideViewPort = true
 | 
			
		||||
        binding!!.webcontent.settings.loadWithOverviewMode = true
 | 
			
		||||
        binding!!.webcontent.settings.javaScriptEnabled = false
 | 
			
		||||
        binding.webcontent.settings.useWideViewPort = true
 | 
			
		||||
        binding.webcontent.settings.loadWithOverviewMode = true
 | 
			
		||||
        binding.webcontent.settings.javaScriptEnabled = false
 | 
			
		||||
 | 
			
		||||
        binding!!.webcontent.webViewClient = object : WebViewClient() {
 | 
			
		||||
        binding.webcontent.webViewClient = object : WebViewClient() {
 | 
			
		||||
            @Deprecated("Deprecated in Java")
 | 
			
		||||
            override fun shouldOverrideUrlLoading(view: WebView?, url : String): Boolean {
 | 
			
		||||
                if (binding!!.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
 | 
			
		||||
                if (binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
 | 
			
		||||
                    requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
 | 
			
		||||
                }
 | 
			
		||||
                return true
 | 
			
		||||
@@ -377,9 +371,9 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        binding!!.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event)}
 | 
			
		||||
        binding.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event)}
 | 
			
		||||
 | 
			
		||||
        binding!!.webcontent.settings.layoutAlgorithm =
 | 
			
		||||
        binding.webcontent.settings.layoutAlgorithm =
 | 
			
		||||
                WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
 | 
			
		||||
 | 
			
		||||
        var baseUrl: String? = null
 | 
			
		||||
@@ -410,7 +404,7 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
            ""
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        binding!!.webcontent.loadDataWithBaseURL(
 | 
			
		||||
        binding.webcontent.loadDataWithBaseURL(
 | 
			
		||||
            baseUrl,
 | 
			
		||||
            """<html>
 | 
			
		||||
                |<head>
 | 
			
		||||
@@ -463,17 +457,17 @@ class ArticleFragment : Fragment(), DIAware {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun scrollDown() {
 | 
			
		||||
        val height = binding!!.nestedScrollView.measuredHeight
 | 
			
		||||
        binding!!.nestedScrollView.smoothScrollBy(0, height/2)
 | 
			
		||||
        val height = binding.nestedScrollView.measuredHeight
 | 
			
		||||
        binding.nestedScrollView.smoothScrollBy(0, height/2)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun scrollUp() {
 | 
			
		||||
        val height = binding!!.nestedScrollView.measuredHeight
 | 
			
		||||
        binding!!.nestedScrollView.smoothScrollBy(0, -height/2)
 | 
			
		||||
        val height = binding.nestedScrollView.measuredHeight
 | 
			
		||||
        binding.nestedScrollView.smoothScrollBy(0, -height/2)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun openInBrowserAfterFailing() {
 | 
			
		||||
        binding!!.progressBar.visibility = View.GONE
 | 
			
		||||
        binding.progressBar.visibility = View.GONE
 | 
			
		||||
        requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -492,10 +486,10 @@ 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 (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)
 | 
			
		||||
            val position : Int = allImages.indexOf(binding.webcontent.hitTestResult.extra)
 | 
			
		||||
 | 
			
		||||
            val intent = Intent(activity, ImageActivity::class.java)
 | 
			
		||||
            intent.putExtra("allImages", allImages)
 | 
			
		||||
 
 | 
			
		||||
@@ -67,18 +67,22 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
 | 
			
		||||
                    val c = Chip(context)
 | 
			
		||||
                    c.text = tag.tag
 | 
			
		||||
 | 
			
		||||
                    val gd = GradientDrawable()
 | 
			
		||||
                    val gdColor = try {
 | 
			
		||||
                        Color.parseColor(tag.color)
 | 
			
		||||
                    } catch (e: IllegalArgumentException) {
 | 
			
		||||
                        e.sendSilentlyWithAcraWithName("color issue " + tag.color)
 | 
			
		||||
                        resources.getColor(R.color.colorPrimary)
 | 
			
		||||
                    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
 | 
			
		||||
 | 
			
		||||
                    c.setOnCloseIconClickListener {
 | 
			
		||||
                        (it as Chip).isCloseIconVisible = false
 | 
			
		||||
@@ -127,7 +131,11 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
 | 
			
		||||
                                dataSource: DataSource?,
 | 
			
		||||
                                isFirstResource: Boolean
 | 
			
		||||
                            ): Boolean {
 | 
			
		||||
                                c.chipIcon = resource
 | 
			
		||||
                                try {
 | 
			
		||||
                                    c.chipIcon = resource
 | 
			
		||||
                                } catch (e: Exception) {
 | 
			
		||||
                                    e.sendSilentlyWithAcraWithName("sources > onResourceReady")
 | 
			
		||||
                                }
 | 
			
		||||
                                return false
 | 
			
		||||
                            }
 | 
			
		||||
                        }).preload()
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,7 @@ data class ParecelableItem(
 | 
			
		||||
    val link: String,
 | 
			
		||||
    val sourcetitle: String,
 | 
			
		||||
    val tags: String,
 | 
			
		||||
    val author: String
 | 
			
		||||
    val author: String?
 | 
			
		||||
) : Parcelable {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
    xmlns:tools="http://schemas.android.com/tools"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
    android:layout_height="match_parent"
 | 
			
		||||
    tools:context="bou.amine.apps.readerforselfossv2.android.AddSourceActivity">
 | 
			
		||||
    tools:context="bou.amine.apps.readerforselfossv2.android.UpsertSourceActivity">
 | 
			
		||||
    <LinearLayout
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
@@ -41,36 +41,20 @@
 | 
			
		||||
            app:layout_constraintHorizontal_bias="1.0"
 | 
			
		||||
            app:layout_constraintVertical_bias="0.0">
 | 
			
		||||
 | 
			
		||||
            <TextView
 | 
			
		||||
                android:text="@string/add_source"
 | 
			
		||||
                android:layout_width="wrap_content"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:id="@+id/textView2"
 | 
			
		||||
                android:textAppearance="@style/TextAppearance.AppCompat.Large"
 | 
			
		||||
                android:textAlignment="center"
 | 
			
		||||
                android:layout_marginTop="16dp"
 | 
			
		||||
                app:layout_constraintTop_toTopOf="parent"
 | 
			
		||||
                android:layout_marginEnd="16dp"
 | 
			
		||||
                app:layout_constraintRight_toRightOf="parent"
 | 
			
		||||
                android:layout_marginRight="16dp"
 | 
			
		||||
                android:layout_marginStart="16dp"
 | 
			
		||||
                app:layout_constraintLeft_toLeftOf="parent"
 | 
			
		||||
                android:layout_marginLeft="16dp"
 | 
			
		||||
                android:gravity="center_horizontal" />
 | 
			
		||||
 | 
			
		||||
            <EditText
 | 
			
		||||
                android:id="@+id/nameInput"
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:layout_marginTop="16dp"
 | 
			
		||||
                android:autofillHints="false"
 | 
			
		||||
                android:ems="10"
 | 
			
		||||
                android:id="@+id/nameInput"
 | 
			
		||||
                android:layout_marginTop="32dp"
 | 
			
		||||
                app:layout_constraintTop_toBottomOf="@+id/textView2"
 | 
			
		||||
                android:hint="@string/add_source_hint_name"
 | 
			
		||||
                android:inputType="text"
 | 
			
		||||
                android:textColorHint="?android:textColorPrimary"
 | 
			
		||||
                app:layout_constraintLeft_toLeftOf="parent"
 | 
			
		||||
                app:layout_constraintRight_toRightOf="parent"
 | 
			
		||||
                android:inputType="text"
 | 
			
		||||
                android:hint="@string/add_source_hint_name"
 | 
			
		||||
                android:textColorHint="?android:textColorPrimary"
 | 
			
		||||
                android:autofillHints="false" />
 | 
			
		||||
                app:layout_constraintTop_toTopOf="parent" />
 | 
			
		||||
 | 
			
		||||
            <EditText
 | 
			
		||||
                android:layout_width="match_parent"
 | 
			
		||||
@@ -20,6 +20,11 @@
 | 
			
		||||
          android:orderInCategory="2"
 | 
			
		||||
          app:showAsAction="ifRoom"/>
 | 
			
		||||
 | 
			
		||||
    <item android:id="@+id/action_sources"
 | 
			
		||||
        android:title="@string/menu_home_sources"
 | 
			
		||||
        android:orderInCategory="97"
 | 
			
		||||
        app:showAsAction="never"/>
 | 
			
		||||
 | 
			
		||||
    <item android:id="@+id/action_settings"
 | 
			
		||||
        android:title="@string/title_activity_settings"
 | 
			
		||||
        android:orderInCategory="98"
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,4 +6,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
</resources>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -129,4 +129,6 @@
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -231,7 +231,8 @@ class RepositoryTest {
 | 
			
		||||
            listOf("tags"),
 | 
			
		||||
            "spouts\\rss\\fulltextrss",
 | 
			
		||||
            "",
 | 
			
		||||
            "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
 | 
			
		||||
            "b3aa8a664d08eb15d6ff1db2fa83e0d9.png",
 | 
			
		||||
            SelfossModel.SourceParams("url")
 | 
			
		||||
        ))
 | 
			
		||||
        runBlocking {
 | 
			
		||||
            repository.getNewerItems()
 | 
			
		||||
@@ -551,7 +552,8 @@ class RepositoryTest {
 | 
			
		||||
                listOf("Test", "second"),
 | 
			
		||||
                "spouts\\rss\\fulltextrss",
 | 
			
		||||
                "",
 | 
			
		||||
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
 | 
			
		||||
                "d8c92cdb1ef119ea85c4b9205c879ca7.png",
 | 
			
		||||
                SelfossModel.SourceParams("url")
 | 
			
		||||
            ),
 | 
			
		||||
            SelfossModel.Source(
 | 
			
		||||
                2,
 | 
			
		||||
@@ -559,7 +561,8 @@ class RepositoryTest {
 | 
			
		||||
                listOf("second"),
 | 
			
		||||
                "spouts\\rss\\fulltextrss",
 | 
			
		||||
                "",
 | 
			
		||||
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
 | 
			
		||||
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png",
 | 
			
		||||
                SelfossModel.SourceParams("url")
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        val sourcesDB = listOf(
 | 
			
		||||
@@ -569,7 +572,8 @@ class RepositoryTest {
 | 
			
		||||
                "Test,second",
 | 
			
		||||
                "spouts\\rss\\fulltextrss",
 | 
			
		||||
                "",
 | 
			
		||||
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
 | 
			
		||||
                "d8c92cdb1ef119ea85c4b9205c879ca7.png",
 | 
			
		||||
                "url"
 | 
			
		||||
            ),
 | 
			
		||||
            SOURCE(
 | 
			
		||||
                "2",
 | 
			
		||||
@@ -577,7 +581,8 @@ class RepositoryTest {
 | 
			
		||||
                "second",
 | 
			
		||||
                "spouts\\rss\\fulltextrss",
 | 
			
		||||
                "",
 | 
			
		||||
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
 | 
			
		||||
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png",
 | 
			
		||||
                "url"
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@@ -707,7 +712,7 @@ class RepositoryTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun create_source() {
 | 
			
		||||
        coEvery { api.createSourceForVersion(any(), any(), any(), any(), any()) } returns
 | 
			
		||||
        coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
 | 
			
		||||
                SuccessResponse(true)
 | 
			
		||||
 | 
			
		||||
        initializeRepository()
 | 
			
		||||
@@ -718,7 +723,6 @@ class RepositoryTest {
 | 
			
		||||
                "https://test.com/feed",
 | 
			
		||||
                "spouts\\rss\\fulltextrss",
 | 
			
		||||
                "Test, New",
 | 
			
		||||
                ""
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -728,7 +732,6 @@ class RepositoryTest {
 | 
			
		||||
                any(),
 | 
			
		||||
                any(),
 | 
			
		||||
                any(),
 | 
			
		||||
                any(),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        assertSame(true, response)
 | 
			
		||||
@@ -736,7 +739,7 @@ class RepositoryTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun create_source_but_response_fails() {
 | 
			
		||||
        coEvery { api.createSourceForVersion(any(), any(), any(), any(), any()) } returns
 | 
			
		||||
        coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
 | 
			
		||||
                SuccessResponse(false)
 | 
			
		||||
 | 
			
		||||
        initializeRepository()
 | 
			
		||||
@@ -746,8 +749,7 @@ class RepositoryTest {
 | 
			
		||||
                "test",
 | 
			
		||||
                "https://test.com/feed",
 | 
			
		||||
                "spouts\\rss\\fulltextrss",
 | 
			
		||||
                "Test, New",
 | 
			
		||||
                ""
 | 
			
		||||
                "Test, New"
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -756,8 +758,7 @@ class RepositoryTest {
 | 
			
		||||
                any(),
 | 
			
		||||
                any(),
 | 
			
		||||
                any(),
 | 
			
		||||
                any(),
 | 
			
		||||
                any(),
 | 
			
		||||
                any()
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        assertSame(false, response)
 | 
			
		||||
@@ -765,7 +766,7 @@ class RepositoryTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun create_source_without_connection() {
 | 
			
		||||
        coEvery { api.createSourceForVersion(any(), any(), any(), any(), any()) } returns
 | 
			
		||||
        coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
 | 
			
		||||
                SuccessResponse(true)
 | 
			
		||||
 | 
			
		||||
        initializeRepository(MutableStateFlow(false))
 | 
			
		||||
@@ -775,8 +776,7 @@ class RepositoryTest {
 | 
			
		||||
                "test",
 | 
			
		||||
                "https://test.com/feed",
 | 
			
		||||
                "spouts\\rss\\fulltextrss",
 | 
			
		||||
                "Test, New",
 | 
			
		||||
                ""
 | 
			
		||||
                "Test, New"
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -786,7 +786,6 @@ class RepositoryTest {
 | 
			
		||||
                any(),
 | 
			
		||||
                any(),
 | 
			
		||||
                any(),
 | 
			
		||||
                any()
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        assertSame(false, response)
 | 
			
		||||
@@ -1034,7 +1033,8 @@ class RepositoryTest {
 | 
			
		||||
                listOf("Test", "second"),
 | 
			
		||||
                "spouts\\rss\\fulltextrss",
 | 
			
		||||
                "",
 | 
			
		||||
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
 | 
			
		||||
                "d8c92cdb1ef119ea85c4b9205c879ca7.png",
 | 
			
		||||
                SelfossModel.SourceParams("url")
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        repository.searchFilter = "search"
 | 
			
		||||
 
 | 
			
		||||
@@ -17,12 +17,11 @@ plugins {
 | 
			
		||||
 | 
			
		||||
allprojects {
 | 
			
		||||
    repositories {
 | 
			
		||||
        maven { url = uri("https://nexus.amine-louveau.fr/repository/maven-public/")}
 | 
			
		||||
        // IMPORTANT : Add back when new library added
 | 
			
		||||
        // google()
 | 
			
		||||
        // mavenCentral()
 | 
			
		||||
        // jcenter()
 | 
			
		||||
        // maven { url = uri("https://www.jitpack.io") }
 | 
			
		||||
        // maven { url = uri("https://nexus.amine-louveau.fr/repository/maven-public/")}
 | 
			
		||||
        google()
 | 
			
		||||
        mavenCentral()
 | 
			
		||||
        jcenter()
 | 
			
		||||
        maven { url = uri("https://www.jitpack.io") }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,20 +2,18 @@ val pushCache: String by settings
 | 
			
		||||
 | 
			
		||||
pluginManagement {
 | 
			
		||||
    repositories {
 | 
			
		||||
        maven { url = uri("https://nexus.amine-louveau.fr/repository/maven-public/")}
 | 
			
		||||
        // IMPORTANT : Add back when new plugin added
 | 
			
		||||
        // google()
 | 
			
		||||
        // gradlePluginPortal()
 | 
			
		||||
        // mavenCentral()
 | 
			
		||||
        // maven { url = uri("https://nexus.amine-louveau.fr/repository/maven-public/")}
 | 
			
		||||
        google()
 | 
			
		||||
        gradlePluginPortal()
 | 
			
		||||
        mavenCentral()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencyResolutionManagement {
 | 
			
		||||
    repositories {
 | 
			
		||||
        maven { url = uri("https://nexus.amine-louveau.fr/repository/maven-public/")}
 | 
			
		||||
        // IMPORTANT : Add back when new library added
 | 
			
		||||
        // google()
 | 
			
		||||
        // mavenCentral()
 | 
			
		||||
        // maven { url = uri("https://nexus.amine-louveau.fr/repository/maven-public/")}
 | 
			
		||||
        google()
 | 
			
		||||
        mavenCentral()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ class MercuryModel {
 | 
			
		||||
 | 
			
		||||
    @Serializable
 | 
			
		||||
    class ParsedContent(
 | 
			
		||||
        val title: String,
 | 
			
		||||
        val title: String?,
 | 
			
		||||
        val content: String?,
 | 
			
		||||
        val lead_image_url: String?,
 | 
			
		||||
        val url: String
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
 | 
			
		||||
import kotlinx.serialization.descriptors.SerialDescriptor
 | 
			
		||||
import kotlinx.serialization.encoding.Decoder
 | 
			
		||||
import kotlinx.serialization.encoding.Encoder
 | 
			
		||||
import kotlinx.serialization.encoding.encodeCollection
 | 
			
		||||
import kotlinx.serialization.json.*
 | 
			
		||||
 | 
			
		||||
class SelfossModel {
 | 
			
		||||
@@ -55,7 +56,12 @@ class SelfossModel {
 | 
			
		||||
        val tags: List<String>,
 | 
			
		||||
        val spout: String,
 | 
			
		||||
        val error: String,
 | 
			
		||||
        val icon: String?
 | 
			
		||||
        val icon: String?,
 | 
			
		||||
        val params: SourceParams?
 | 
			
		||||
    )
 | 
			
		||||
    @Serializable
 | 
			
		||||
    data class SourceParams(
 | 
			
		||||
        val url: String
 | 
			
		||||
    )
 | 
			
		||||
    @Serializable
 | 
			
		||||
    data class Item(
 | 
			
		||||
@@ -73,7 +79,7 @@ class SelfossModel {
 | 
			
		||||
        val sourcetitle: String,
 | 
			
		||||
        @Serializable(with = TagsListSerializer::class)
 | 
			
		||||
        val tags: List<String>,
 | 
			
		||||
        val author: String
 | 
			
		||||
        val author: String?
 | 
			
		||||
    ) {
 | 
			
		||||
        // TODO: maybe find a better way to handle these kind of urls
 | 
			
		||||
        fun getLinkDecoded(): String {
 | 
			
		||||
@@ -104,7 +110,7 @@ class SelfossModel {
 | 
			
		||||
 | 
			
		||||
        fun sourceAuthorAndDate(): String {
 | 
			
		||||
            var txt = this.sourcetitle.getHtmlDecoded()
 | 
			
		||||
            if (this.author.isNotEmpty()) {
 | 
			
		||||
            if (!this.author.isNullOrBlank()) {
 | 
			
		||||
                txt += " (by ${this.author}) "
 | 
			
		||||
            }
 | 
			
		||||
            txt += DateUtils.parseRelativeDate(this.datetime)
 | 
			
		||||
@@ -122,7 +128,7 @@ class SelfossModel {
 | 
			
		||||
    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() }
 | 
			
		||||
                is JsonArray -> json.toList().map { it.toString().replace("^\"|\"$".toRegex(), "") }
 | 
			
		||||
                else -> json.toString().split(",")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -132,7 +138,7 @@ class SelfossModel {
 | 
			
		||||
            get() = PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING)
 | 
			
		||||
 | 
			
		||||
        override fun serialize(encoder: Encoder, value: List<String>) {
 | 
			
		||||
            TODO("Not yet implemented")
 | 
			
		||||
            encoder.encodeCollection(PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING), value.size) { this.toString() }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,6 +48,7 @@ class Repository(
 | 
			
		||||
    private var fetchedTags = false
 | 
			
		||||
 | 
			
		||||
    private var _readerItems = ArrayList<SelfossModel.Item>()
 | 
			
		||||
    private var _selectedSource: SelfossModel.Source? = null
 | 
			
		||||
 | 
			
		||||
    suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
 | 
			
		||||
        // TODO: Use the updatedSince parameter
 | 
			
		||||
@@ -343,7 +344,6 @@ class Repository(
 | 
			
		||||
        url: String,
 | 
			
		||||
        spout: String,
 | 
			
		||||
        tags: String,
 | 
			
		||||
        filter: String
 | 
			
		||||
    ): Boolean {
 | 
			
		||||
        var response = false
 | 
			
		||||
        if (isNetworkAvailable()) {
 | 
			
		||||
@@ -352,13 +352,27 @@ class Repository(
 | 
			
		||||
                url,
 | 
			
		||||
                spout,
 | 
			
		||||
                tags,
 | 
			
		||||
                filter
 | 
			
		||||
            ).isSuccess == true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return response
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun updateSource(
 | 
			
		||||
        id: Int,
 | 
			
		||||
        title: String,
 | 
			
		||||
        url: String,
 | 
			
		||||
        spout: String,
 | 
			
		||||
        tags: String
 | 
			
		||||
    ): Boolean {
 | 
			
		||||
        var response = false
 | 
			
		||||
        if (isNetworkAvailable()) {
 | 
			
		||||
            response = api.updateSourceForVersion(id, title, url, spout, tags).isSuccess == true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return response
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun deleteSource(id: Int, title: String): Boolean {
 | 
			
		||||
        var success = false
 | 
			
		||||
        if (isNetworkAvailable()) {
 | 
			
		||||
@@ -511,6 +525,7 @@ class Repository(
 | 
			
		||||
            item.link,
 | 
			
		||||
            item.sourcetitle,
 | 
			
		||||
            item.tags.joinToString(","),
 | 
			
		||||
            item.author,
 | 
			
		||||
            item.id.toString()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@@ -575,4 +590,20 @@ class Repository(
 | 
			
		||||
    fun getReaderItems(): ArrayList<SelfossModel.Item> {
 | 
			
		||||
        return _readerItems
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun migrate(driverFactory: DriverFactory) {
 | 
			
		||||
        ReaderForSelfossDB.Schema.migrate(driverFactory.createDriver(), 0, 1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setSelectedSource(source: SelfossModel.Source) {
 | 
			
		||||
        _selectedSource = source
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun unsetSelectedSource() {
 | 
			
		||||
        _selectedSource = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getSelectedSource(): SelfossModel.Source? {
 | 
			
		||||
        return _selectedSource
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -23,6 +23,9 @@ suspend fun maybeResponse(r: HttpResponse?): SuccessResponse {
 | 
			
		||||
    return if (r != null && r.status.isSuccess()) {
 | 
			
		||||
        r.body()
 | 
			
		||||
    } else {
 | 
			
		||||
        if (r != null) {
 | 
			
		||||
            Napier.i("Error ${r.status}", tag = "maybeResponse")
 | 
			
		||||
        }
 | 
			
		||||
        SuccessResponse(false)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -76,10 +76,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
 | 
			
		||||
    // 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() = appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword().isNotEmpty()
 | 
			
		||||
    private fun hasLoginInfo() =
 | 
			
		||||
        appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword()
 | 
			
		||||
            .isNotEmpty()
 | 
			
		||||
 | 
			
		||||
    suspend fun login(): SuccessResponse =
 | 
			
		||||
        if (appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword().isNotEmpty()) {
 | 
			
		||||
        if (appSettingsService.getUserName().isNotEmpty() && appSettingsService.getPassword()
 | 
			
		||||
                .isNotEmpty()
 | 
			
		||||
        ) {
 | 
			
		||||
            if (shouldHavePostLogin()) {
 | 
			
		||||
                postLogin()
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -99,7 +103,9 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
        parameter("password", appSettingsService.getPassword())
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    private fun shouldHaveNewLogout() = appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0
 | 
			
		||||
    private fun shouldHaveNewLogout() =
 | 
			
		||||
        appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0
 | 
			
		||||
 | 
			
		||||
    suspend fun logout(): SuccessResponse =
 | 
			
		||||
        if (shouldHaveNewLogout()) {
 | 
			
		||||
            doLogout()
 | 
			
		||||
@@ -107,7 +113,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
            maybeLogoutIfAvailable()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private suspend fun maybeLogoutIfAvailable() = responseOrSuccessIf404(client.tryToGet(url("/logout")))
 | 
			
		||||
    private suspend fun maybeLogoutIfAvailable() =
 | 
			
		||||
        responseOrSuccessIf404(client.tryToGet(url("/logout")))
 | 
			
		||||
 | 
			
		||||
    private suspend fun doLogout() = maybeResponse(client.tryToDelete(url("/api/session/current")))
 | 
			
		||||
 | 
			
		||||
@@ -236,13 +243,12 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
        url: String,
 | 
			
		||||
        spout: String,
 | 
			
		||||
        tags: String,
 | 
			
		||||
        filter: String
 | 
			
		||||
    ): SuccessResponse =
 | 
			
		||||
        maybeResponse(
 | 
			
		||||
            if (appSettingsService.getApiVersion() > 1) {
 | 
			
		||||
                createSource("tags[]", title, url, spout, tags, filter)
 | 
			
		||||
                createSource("tags[]", title, url, spout, tags)
 | 
			
		||||
            } else {
 | 
			
		||||
                createSource("tags", title, url, spout, tags, filter)
 | 
			
		||||
                createSource("tags", title, url, spout, tags)
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
@@ -251,8 +257,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
        title: String,
 | 
			
		||||
        url: String,
 | 
			
		||||
        spout: String,
 | 
			
		||||
        tags: String,
 | 
			
		||||
        filter: String
 | 
			
		||||
        tags: String
 | 
			
		||||
    ): HttpResponse? =
 | 
			
		||||
        client.tryToSubmitForm(
 | 
			
		||||
            url = url("/source"),
 | 
			
		||||
@@ -265,7 +270,43 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
 | 
			
		||||
                append("url", url)
 | 
			
		||||
                append("spout", spout)
 | 
			
		||||
                append(tagsParamName, tags)
 | 
			
		||||
                append("filter", filter)
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    suspend fun updateSourceForVersion(
 | 
			
		||||
        id: Int,
 | 
			
		||||
        title: String,
 | 
			
		||||
        url: String,
 | 
			
		||||
        spout: String,
 | 
			
		||||
        tags: String
 | 
			
		||||
    ): SuccessResponse =
 | 
			
		||||
        maybeResponse(
 | 
			
		||||
            if (appSettingsService.getApiVersion() > 1) {
 | 
			
		||||
                updateSource(id, "tags[]", title, url, spout, tags)
 | 
			
		||||
            } else {
 | 
			
		||||
                updateSource(id, "tags", title, url, spout, tags)
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    private suspend fun updateSource(
 | 
			
		||||
        id: Int,
 | 
			
		||||
        tagsParamName: String,
 | 
			
		||||
        title: String,
 | 
			
		||||
        url: String,
 | 
			
		||||
        spout: String,
 | 
			
		||||
        tags: String,
 | 
			
		||||
    ): HttpResponse? =
 | 
			
		||||
        client.tryToSubmitForm(
 | 
			
		||||
            url = url("/source/$id"),
 | 
			
		||||
            formParameters = Parameters.build {
 | 
			
		||||
                if (!shouldHavePostLogin()) {
 | 
			
		||||
                    append("username", appSettingsService.getUserName())
 | 
			
		||||
                    append("password", appSettingsService.getPassword())
 | 
			
		||||
                }
 | 
			
		||||
                append("title", title)
 | 
			
		||||
                append("url", url)
 | 
			
		||||
                append("spout", spout)
 | 
			
		||||
                append(tagsParamName, tags)
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,8 @@ fun SOURCE.toView(): SelfossModel.Source =
 | 
			
		||||
        this.tags.split(","),
 | 
			
		||||
        this.spout,
 | 
			
		||||
        this.error,
 | 
			
		||||
        this.icon
 | 
			
		||||
        this.icon,
 | 
			
		||||
        if (this.url != null) SelfossModel.SourceParams(this.url) else null
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
fun SelfossModel.Source.toEntity(): SOURCE =
 | 
			
		||||
@@ -29,7 +30,8 @@ fun SelfossModel.Source.toEntity(): SOURCE =
 | 
			
		||||
        this.tags.joinToString(","),
 | 
			
		||||
        this.spout,
 | 
			
		||||
        this.error,
 | 
			
		||||
        this.icon.orEmpty()
 | 
			
		||||
        this.icon.orEmpty(),
 | 
			
		||||
        this.params?.url
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
fun SelfossModel.Tag.toEntity(): TAG =
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,6 @@
 | 
			
		||||
ALTER TABLE ITEM ADD COLUMN `author` TEXT NOT NULL;
 | 
			
		||||
CREATE TABLE ITEM_BACKUP AS SELECT `id`, `datetime`, `title`, `content`,
 | 
			
		||||
 `unread`, `starred`, `thumbnail`, `icon`, `link`, `sourcetitle`,
 | 
			
		||||
 `tags` FROM ITEM;
 | 
			
		||||
ALTER TABLE ITEM_BACKUP ADD COLUMN `author` TEXT;
 | 
			
		||||
DROP TABLE ITEM;
 | 
			
		||||
ALTER TABLE ITEM_BACKUP RENAME TO ITEM;
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
ALTER TABLE SOURCE ADD COLUMN `url` TEXT;
 | 
			
		||||
@@ -10,7 +10,7 @@ CREATE TABLE ITEM (
 | 
			
		||||
    `link` TEXT NOT NULL,
 | 
			
		||||
    `sourcetitle` TEXT NOT NULL,
 | 
			
		||||
    `tags` TEXT NOT NULL,
 | 
			
		||||
    `author` TEXT NOT NULL,
 | 
			
		||||
    `author` TEXT,
 | 
			
		||||
    PRIMARY KEY(`id`)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@@ -31,4 +31,4 @@ deleteItemsWhereSource:
 | 
			
		||||
DELETE FROM ITEM WHERE `sourcetitle` = ?;
 | 
			
		||||
 | 
			
		||||
updateItem:
 | 
			
		||||
UPDATE ITEM SET `datetime` = ?, `title` = ?, `content` = ?, `unread` = ?, `starred` = ?, `thumbnail` = ?, `icon` = ?, `link` = ?, `sourcetitle` = ?, `tags` = ? WHERE `id` = ?;
 | 
			
		||||
UPDATE ITEM SET `datetime` = ?, `title` = ?, `content` = ?, `unread` = ?, `starred` = ?, `thumbnail` = ?, `icon` = ?, `link` = ?, `sourcetitle` = ?, `tags` = ?, `author` = ? WHERE `id` = ?;
 | 
			
		||||
@@ -5,6 +5,7 @@ CREATE TABLE SOURCE (
 | 
			
		||||
    `spout` TEXT NOT NULL,
 | 
			
		||||
    `error` TEXT NOT NULL,
 | 
			
		||||
    `icon` TEXT NOT NULL,
 | 
			
		||||
    `url` TEXT,
 | 
			
		||||
    PRIMARY KEY(`id`)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user