Compare commits
	
		
			11 Commits
		
	
	
		
			v123102961
			...
			v124010191
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8f464d95fd | ||
| 
						 | 
					5ccd6a3368 | ||
| 
						 | 
					cdbded246e | ||
| 
						 | 
					750c7758bd | ||
| 
						 | 
					22f8b14ecd | ||
| 
						 | 
					6e27d6d4e6 | ||
| 
						 | 
					14ff4dbd05 | ||
| 
						 | 
					390c2d0cf3 | ||
| 
						 | 
					e58914ef58 | ||
| 
						 | 
					a03f08fca1 | ||
| 
						 | 
					8e9b87f00c | 
							
								
								
									
										30
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,3 +1,33 @@
 | 
				
			|||||||
 | 
					**v124010032**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- fix: Another date format thing.
 | 
				
			||||||
 | 
					- Changelog for v124010031 [CI SKIP]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**v124010031**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- fix: Checking if selfoss instance.
 | 
				
			||||||
 | 
					- fix: handle three characters lenght hexcode colors.
 | 
				
			||||||
 | 
					- Changelog for v123113311 [CI SKIP]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**v123113311**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- chore: Source tracker url in the menu.
 | 
				
			||||||
 | 
					- fix: Handle kodein proguard rules.
 | 
				
			||||||
 | 
					- Changelog for v123102961 [CI SKIP]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**v123102961**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- chore: domain changes.
 | 
				
			||||||
 | 
					- Changelog for v123102852 [CI SKIP]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**v123102852**
 | 
					**v123102852**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- chore: lint cleaning.
 | 
					- chore: lint cleaning.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								androidApp/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								androidApp/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							@@ -86,3 +86,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-dontwarn io.mockk.**
 | 
					-dontwarn io.mockk.**
 | 
				
			||||||
-keep class io.mockk.** { *; }
 | 
					-keep class io.mockk.** { *; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Kodein
 | 
				
			||||||
 | 
					-keep, allowobfuscation, allowoptimization class org.kodein.type.TypeReference
 | 
				
			||||||
 | 
					-keep, allowobfuscation, allowoptimization class org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-keep, allowobfuscation, allowoptimization class * extends org.kodein.type.TypeReference
 | 
				
			||||||
 | 
					-keep, allowobfuscation, allowoptimization class * extends org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest
 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
package bou.amine.apps.readerforselfossv2.android
 | 
					package bou.amine.apps.readerforselfossv2.android
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.content.Intent
 | 
					import android.content.Intent
 | 
				
			||||||
 | 
					import android.net.Uri
 | 
				
			||||||
import android.os.Bundle
 | 
					import android.os.Bundle
 | 
				
			||||||
import android.view.Menu
 | 
					import android.view.Menu
 | 
				
			||||||
import android.view.MenuItem
 | 
					import android.view.MenuItem
 | 
				
			||||||
@@ -565,6 +566,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
 | 
					    override fun onOptionsItemSelected(item: MenuItem): Boolean {
 | 
				
			||||||
        when (item.itemId) {
 | 
					        when (item.itemId) {
 | 
				
			||||||
 | 
					            R.id.issue_tracker -> {
 | 
				
			||||||
 | 
					                val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
 | 
				
			||||||
 | 
					                startActivity(browserIntent)
 | 
				
			||||||
 | 
					                return true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            R.id.action_filter -> {
 | 
					            R.id.action_filter -> {
 | 
				
			||||||
                val filterSheetFragment = FilterSheetFragment()
 | 
					                val filterSheetFragment = FilterSheetFragment()
 | 
				
			||||||
                filterSheetFragment.show(supportFragmentManager, FilterSheetFragment.TAG)
 | 
					                filterSheetFragment.show(supportFragmentManager, FilterSheetFragment.TAG)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import android.animation.Animator
 | 
				
			|||||||
import android.animation.AnimatorListenerAdapter
 | 
					import android.animation.AnimatorListenerAdapter
 | 
				
			||||||
import android.annotation.SuppressLint
 | 
					import android.annotation.SuppressLint
 | 
				
			||||||
import android.content.Intent
 | 
					import android.content.Intent
 | 
				
			||||||
 | 
					import android.net.Uri
 | 
				
			||||||
import android.os.Bundle
 | 
					import android.os.Bundle
 | 
				
			||||||
import android.text.TextUtils
 | 
					import android.text.TextUtils
 | 
				
			||||||
import android.view.Menu
 | 
					import android.view.Menu
 | 
				
			||||||
@@ -139,20 +140,25 @@ class LoginActivity : AppCompatActivity(), DIAware {
 | 
				
			|||||||
        repository.refreshLoginInformation(url, login, password)
 | 
					        repository.refreshLoginInformation(url, login, password)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        CoroutineScope(Dispatchers.Main).launch {
 | 
					        CoroutineScope(Dispatchers.Main).launch {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
                repository.updateApiInformation()
 | 
					                repository.updateApiInformation()
 | 
				
			||||||
            val result = repository.login()
 | 
					            } catch (e: Exception) {
 | 
				
			||||||
            if (result) {
 | 
					                if (e.message?.startsWith("No transformation found") == true) {
 | 
				
			||||||
                val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance()
 | 
					 | 
				
			||||||
                if (!errorFetching && !displaySelfossOnly) {
 | 
					 | 
				
			||||||
                    goToMain()
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    if (displaySelfossOnly) {
 | 
					 | 
				
			||||||
                    Toast.makeText(
 | 
					                    Toast.makeText(
 | 
				
			||||||
                        applicationContext,
 | 
					                        applicationContext,
 | 
				
			||||||
                        R.string.application_selfoss_only,
 | 
					                        R.string.application_selfoss_only,
 | 
				
			||||||
                        Toast.LENGTH_LONG,
 | 
					                        Toast.LENGTH_LONG,
 | 
				
			||||||
                    ).show()
 | 
					                    ).show()
 | 
				
			||||||
 | 
					                    preferenceError()
 | 
				
			||||||
 | 
					                    showProgress(false)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            val result = repository.login()
 | 
				
			||||||
 | 
					            if (result) {
 | 
				
			||||||
 | 
					                val errorFetching = repository.checkIfFetchFails()
 | 
				
			||||||
 | 
					                if (!errorFetching) {
 | 
				
			||||||
 | 
					                    goToMain()
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
                    preferenceError()
 | 
					                    preferenceError()
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
@@ -254,10 +260,17 @@ class LoginActivity : AppCompatActivity(), DIAware {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
 | 
					    override fun onOptionsItemSelected(item: MenuItem): Boolean {
 | 
				
			||||||
        return when (item.itemId) {
 | 
					        return when (item.itemId) {
 | 
				
			||||||
 | 
					            R.id.issue_tracker -> {
 | 
				
			||||||
 | 
					                val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
 | 
				
			||||||
 | 
					                startActivity(browserIntent)
 | 
				
			||||||
 | 
					                return true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            R.id.about -> {
 | 
					            R.id.about -> {
 | 
				
			||||||
                LibsBuilder()
 | 
					                LibsBuilder()
 | 
				
			||||||
                    .withAboutIconShown(true)
 | 
					                    .withAboutIconShown(true)
 | 
				
			||||||
                    .withAboutVersionShown(true)
 | 
					                    .withAboutVersionShown(true)
 | 
				
			||||||
 | 
					                    .withAboutSpecial2("Bug reports").withAboutSpecial2Description(AppSettingsService.trackerUrl)
 | 
				
			||||||
 | 
					                    .withAboutSpecial1("Project Page").withAboutSpecial1Description(AppSettingsService.sourceUrl)
 | 
				
			||||||
                    .start(this)
 | 
					                    .start(this)
 | 
				
			||||||
                true
 | 
					                true
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,7 @@ class ItemCardAdapter(
 | 
				
			|||||||
    override var items: ArrayList<SelfossModel.Item>,
 | 
					    override var items: ArrayList<SelfossModel.Item>,
 | 
				
			||||||
    override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit,
 | 
					    override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit,
 | 
				
			||||||
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
 | 
					) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
 | 
				
			||||||
 | 
					    private lateinit var binding: CardItemBinding
 | 
				
			||||||
    private val c: Context = app.baseContext
 | 
					    private val c: Context = app.baseContext
 | 
				
			||||||
    private val imageMaxHeight: Int =
 | 
					    private val imageMaxHeight: Int =
 | 
				
			||||||
        c.resources.getDimension(R.dimen.card_image_max_height).toInt()
 | 
					        c.resources.getDimension(R.dimen.card_image_max_height).toInt()
 | 
				
			||||||
@@ -46,10 +47,48 @@ class ItemCardAdapter(
 | 
				
			|||||||
        parent: ViewGroup,
 | 
					        parent: ViewGroup,
 | 
				
			||||||
        viewType: Int,
 | 
					        viewType: Int,
 | 
				
			||||||
    ): ViewHolder {
 | 
					    ): ViewHolder {
 | 
				
			||||||
        val binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
					        binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
				
			||||||
        return ViewHolder(binding)
 | 
					        return ViewHolder(binding)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun handleClickListeners(position: Int) {
 | 
				
			||||||
 | 
					        binding.favButton.setOnClickListener {
 | 
				
			||||||
 | 
					            val item = items[position]
 | 
				
			||||||
 | 
					            if (item.starred) {
 | 
				
			||||||
 | 
					                CoroutineScope(Dispatchers.IO).launch {
 | 
				
			||||||
 | 
					                    repository.unstarr(item)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                binding.favButton.isSelected = false
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                CoroutineScope(Dispatchers.IO).launch {
 | 
				
			||||||
 | 
					                    repository.starr(item)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                binding.favButton.isSelected = true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        binding.shareBtn.setOnClickListener {
 | 
				
			||||||
 | 
					            val item = items[position]
 | 
				
			||||||
 | 
					            c.shareLink(item.getLinkDecoded(), item.title.getHtmlDecoded())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        binding.browserBtn.setOnClickListener {
 | 
				
			||||||
 | 
					            c.openInBrowserAsNewTask(items[position])
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun handleLinkOpening(position: Int) {
 | 
				
			||||||
 | 
					        binding.root.setOnClickListener {
 | 
				
			||||||
 | 
					            repository.setReaderItems(items)
 | 
				
			||||||
 | 
					            c.openItemUrl(
 | 
				
			||||||
 | 
					                position,
 | 
				
			||||||
 | 
					                items[position].getLinkDecoded(),
 | 
				
			||||||
 | 
					                appSettingsService.isArticleViewerEnabled(),
 | 
				
			||||||
 | 
					                app,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun onBindViewHolder(
 | 
					    override fun onBindViewHolder(
 | 
				
			||||||
        holder: ViewHolder,
 | 
					        holder: ViewHolder,
 | 
				
			||||||
        position: Int,
 | 
					        position: Int,
 | 
				
			||||||
@@ -57,6 +96,9 @@ class ItemCardAdapter(
 | 
				
			|||||||
        with(holder) {
 | 
					        with(holder) {
 | 
				
			||||||
            val itm = items[position]
 | 
					            val itm = items[position]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            handleClickListeners(position)
 | 
				
			||||||
 | 
					            handleLinkOpening(position)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            binding.favButton.isSelected = itm.starred
 | 
					            binding.favButton.isSelected = itm.starred
 | 
				
			||||||
            if (appSettingsService.getPublicAccess()) {
 | 
					            if (appSettingsService.getPublicAccess()) {
 | 
				
			||||||
                binding.favButton.visibility = View.GONE
 | 
					                binding.favButton.visibility = View.GONE
 | 
				
			||||||
@@ -96,48 +138,5 @@ class ItemCardAdapter(
 | 
				
			|||||||
        return items.size
 | 
					        return items.size
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root) {
 | 
					    inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root)
 | 
				
			||||||
        init {
 | 
					 | 
				
			||||||
            handleClickListeners()
 | 
					 | 
				
			||||||
            handleLinkOpening()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private fun handleClickListeners() {
 | 
					 | 
				
			||||||
            binding.favButton.setOnClickListener {
 | 
					 | 
				
			||||||
                val item = items[bindingAdapterPosition]
 | 
					 | 
				
			||||||
                if (item.starred) {
 | 
					 | 
				
			||||||
                    CoroutineScope(Dispatchers.IO).launch {
 | 
					 | 
				
			||||||
                        repository.unstarr(item)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    binding.favButton.isSelected = false
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    CoroutineScope(Dispatchers.IO).launch {
 | 
					 | 
				
			||||||
                        repository.starr(item)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    binding.favButton.isSelected = true
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            binding.shareBtn.setOnClickListener {
 | 
					 | 
				
			||||||
                val item = items[bindingAdapterPosition]
 | 
					 | 
				
			||||||
                c.shareLink(item.getLinkDecoded(), item.title.getHtmlDecoded())
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            binding.browserBtn.setOnClickListener {
 | 
					 | 
				
			||||||
                c.openInBrowserAsNewTask(items[bindingAdapterPosition])
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private fun handleLinkOpening() {
 | 
					 | 
				
			||||||
            binding.root.setOnClickListener {
 | 
					 | 
				
			||||||
                repository.setReaderItems(items)
 | 
					 | 
				
			||||||
                c.openItemUrl(
 | 
					 | 
				
			||||||
                    bindingAdapterPosition,
 | 
					 | 
				
			||||||
                    items[bindingAdapterPosition].getLinkDecoded(),
 | 
					 | 
				
			||||||
                    appSettingsService.isArticleViewerEnabled(),
 | 
					 | 
				
			||||||
                    app,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,7 @@ class ItemListAdapter(
 | 
				
			|||||||
    override var items: ArrayList<SelfossModel.Item>,
 | 
					    override var items: ArrayList<SelfossModel.Item>,
 | 
				
			||||||
    override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit,
 | 
					    override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit,
 | 
				
			||||||
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
 | 
					) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
 | 
				
			||||||
 | 
					    private lateinit var binding: ListItemBinding
 | 
				
			||||||
    private val c: Context = app.baseContext
 | 
					    private val c: Context = app.baseContext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override val di: DI by closestDI(app)
 | 
					    override val di: DI by closestDI(app)
 | 
				
			||||||
@@ -35,7 +36,7 @@ class ItemListAdapter(
 | 
				
			|||||||
        parent: ViewGroup,
 | 
					        parent: ViewGroup,
 | 
				
			||||||
        viewType: Int,
 | 
					        viewType: Int,
 | 
				
			||||||
    ): ViewHolder {
 | 
					    ): ViewHolder {
 | 
				
			||||||
        val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
					        binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
				
			||||||
        return ViewHolder(binding)
 | 
					        return ViewHolder(binding)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -46,6 +47,16 @@ class ItemListAdapter(
 | 
				
			|||||||
        with(holder) {
 | 
					        with(holder) {
 | 
				
			||||||
            val itm = items[position]
 | 
					            val itm = items[position]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            binding.root.setOnClickListener {
 | 
				
			||||||
 | 
					                repository.setReaderItems(items)
 | 
				
			||||||
 | 
					                c.openItemUrl(
 | 
				
			||||||
 | 
					                    bindingAdapterPosition,
 | 
				
			||||||
 | 
					                    items[bindingAdapterPosition].getLinkDecoded(),
 | 
				
			||||||
 | 
					                    appSettingsService.isArticleViewerEnabled(),
 | 
				
			||||||
 | 
					                    app,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            binding.title.text = itm.title.getHtmlDecoded()
 | 
					            binding.title.text = itm.title.getHtmlDecoded()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            binding.title.setOnTouchListener(LinkOnTouchListener())
 | 
					            binding.title.setOnTouchListener(LinkOnTouchListener())
 | 
				
			||||||
@@ -68,21 +79,5 @@ class ItemListAdapter(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    override fun getItemCount(): Int = items.size
 | 
					    override fun getItemCount(): Int = items.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) {
 | 
					    inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root)
 | 
				
			||||||
        init {
 | 
					 | 
				
			||||||
            handleLinkOpening()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private fun handleLinkOpening() {
 | 
					 | 
				
			||||||
            binding.root.setOnClickListener {
 | 
					 | 
				
			||||||
                repository.setReaderItems(items)
 | 
					 | 
				
			||||||
                c.openItemUrl(
 | 
					 | 
				
			||||||
                    bindingAdapterPosition,
 | 
					 | 
				
			||||||
                    items[bindingAdapterPosition].getLinkDecoded(),
 | 
					 | 
				
			||||||
                    appSettingsService.isArticleViewerEnabled(),
 | 
					 | 
				
			||||||
                    app,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,6 +50,33 @@ class SourcesListAdapter(
 | 
				
			|||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        val itm = items[position]
 | 
					        val itm = items[position]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val deleteBtn: Button = holder.mView.findViewById(R.id.deleteBtn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        deleteBtn.setOnClickListener {
 | 
				
			||||||
 | 
					            val (id, title) = items[position]
 | 
				
			||||||
 | 
					            CoroutineScope(Dispatchers.IO).launch {
 | 
				
			||||||
 | 
					                val successfullyDeletedSource = repository.deleteSource(id, title)
 | 
				
			||||||
 | 
					                if (successfullyDeletedSource) {
 | 
				
			||||||
 | 
					                    items.removeAt(position)
 | 
				
			||||||
 | 
					                    notifyItemRemoved(position)
 | 
				
			||||||
 | 
					                    notifyItemRangeChanged(position, itemCount)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Toast.makeText(
 | 
				
			||||||
 | 
					                        app,
 | 
				
			||||||
 | 
					                        R.string.can_delete_source,
 | 
				
			||||||
 | 
					                        Toast.LENGTH_SHORT,
 | 
				
			||||||
 | 
					                    ).show()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        holder.mView.setOnClickListener {
 | 
				
			||||||
 | 
					            val source = items[position]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            repository.setSelectedSource(source)
 | 
				
			||||||
 | 
					            app.startActivity(Intent(app, UpsertSourceActivity::class.java))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (itm.getIcon(repository.baseUrl).isEmpty()) {
 | 
					        if (itm.getIcon(repository.baseUrl).isEmpty()) {
 | 
				
			||||||
            binding.itemImage.setBackgroundAndText(itm.title.getHtmlDecoded())
 | 
					            binding.itemImage.setBackgroundAndText(itm.title.getHtmlDecoded())
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -72,38 +99,5 @@ class SourcesListAdapter(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    override fun getItemCount(): Int = items.size
 | 
					    override fun getItemCount(): Int = items.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inner class ViewHolder(private val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
 | 
					    inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView)
 | 
				
			||||||
        init {
 | 
					 | 
				
			||||||
            handleClickListeners()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private fun handleClickListeners() {
 | 
					 | 
				
			||||||
            val deleteBtn: Button = mView.findViewById(R.id.deleteBtn)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            deleteBtn.setOnClickListener {
 | 
					 | 
				
			||||||
                val (id, title) = items[bindingAdapterPosition]
 | 
					 | 
				
			||||||
                CoroutineScope(Dispatchers.IO).launch {
 | 
					 | 
				
			||||||
                    val successfullyDeletedSource = repository.deleteSource(id, title)
 | 
					 | 
				
			||||||
                    if (successfullyDeletedSource) {
 | 
					 | 
				
			||||||
                        items.removeAt(bindingAdapterPosition)
 | 
					 | 
				
			||||||
                        notifyItemRemoved(bindingAdapterPosition)
 | 
					 | 
				
			||||||
                        notifyItemRangeChanged(bindingAdapterPosition, itemCount)
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        Toast.makeText(
 | 
					 | 
				
			||||||
                            app,
 | 
					 | 
				
			||||||
                            R.string.can_delete_source,
 | 
					 | 
				
			||||||
                            Toast.LENGTH_SHORT,
 | 
					 | 
				
			||||||
                        ).show()
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            mView.setOnClickListener {
 | 
					 | 
				
			||||||
                val source = items[bindingAdapterPosition]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                repository.setSelectedSource(source)
 | 
					 | 
				
			||||||
                app.startActivity(Intent(app, UpsertSourceActivity::class.java))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,7 +52,6 @@ import org.kodein.di.DIAware
 | 
				
			|||||||
import org.kodein.di.android.x.closestDI
 | 
					import org.kodein.di.android.x.closestDI
 | 
				
			||||||
import org.kodein.di.instance
 | 
					import org.kodein.di.instance
 | 
				
			||||||
import java.net.MalformedURLException
 | 
					import java.net.MalformedURLException
 | 
				
			||||||
import java.net.SocketTimeoutException
 | 
					 | 
				
			||||||
import java.net.URL
 | 
					import java.net.URL
 | 
				
			||||||
import java.util.*
 | 
					import java.util.*
 | 
				
			||||||
import java.util.concurrent.ExecutionException
 | 
					import java.util.concurrent.ExecutionException
 | 
				
			||||||
@@ -264,10 +263,7 @@ class ArticleFragment : Fragment(), DIAware {
 | 
				
			|||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    openInBrowserAfterFailing()
 | 
					                    openInBrowserAfterFailing()
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } catch (e: SocketTimeoutException) {
 | 
					 | 
				
			||||||
                openInBrowserAfterFailing()
 | 
					 | 
				
			||||||
            } catch (e: Exception) {
 | 
					            } catch (e: Exception) {
 | 
				
			||||||
                e.sendSilentlyWithAcraWithName("getContentFromMercury > $url")
 | 
					 | 
				
			||||||
                openInBrowserAfterFailing()
 | 
					                openInBrowserAfterFailing()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ import bou.amine.apps.readerforselfossv2.android.R
 | 
				
			|||||||
import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding
 | 
					import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding
 | 
				
			||||||
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName
 | 
					import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName
 | 
				
			||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
 | 
					import bou.amine.apps.readerforselfossv2.repository.Repository
 | 
				
			||||||
 | 
					import bou.amine.apps.readerforselfossv2.utils.getColorHexCode
 | 
				
			||||||
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
 | 
					import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
 | 
				
			||||||
import bou.amine.apps.readerforselfossv2.utils.getIcon
 | 
					import bou.amine.apps.readerforselfossv2.utils.getIcon
 | 
				
			||||||
import com.bumptech.glide.Glide
 | 
					import com.bumptech.glide.Glide
 | 
				
			||||||
@@ -147,9 +148,9 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
 | 
				
			|||||||
                    val gd = GradientDrawable()
 | 
					                    val gd = GradientDrawable()
 | 
				
			||||||
                    val gdColor =
 | 
					                    val gdColor =
 | 
				
			||||||
                        try {
 | 
					                        try {
 | 
				
			||||||
                            Color.parseColor(tag.color)
 | 
					                            Color.parseColor(tag.getColorHexCode())
 | 
				
			||||||
                        } catch (e: IllegalArgumentException) {
 | 
					                        } catch (e: IllegalArgumentException) {
 | 
				
			||||||
                            e.sendSilentlyWithAcraWithName("color issue " + tag.color)
 | 
					                            e.sendSilentlyWithAcraWithName("color issue " + tag.color + " / " + tag.getColorHexCode())
 | 
				
			||||||
                            resources.getColor(R.color.colorPrimary)
 | 
					                            resources.getColor(R.color.colorPrimary)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    gd.setColor(gdColor)
 | 
					                    gd.setColor(gdColor)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,12 @@
 | 
				
			|||||||
        android:orderInCategory="101"
 | 
					        android:orderInCategory="101"
 | 
				
			||||||
        android:title="@string/menu_home_refresh" />
 | 
					        android:title="@string/menu_home_refresh" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <item
 | 
				
			||||||
 | 
					        android:id="@+id/issue_tracker"
 | 
				
			||||||
 | 
					        app:showAsAction="never"
 | 
				
			||||||
 | 
					        android:orderInCategory="103"
 | 
				
			||||||
 | 
					        android:title="@string/issue_tracker_link" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <item android:id="@+id/action_disconnect"
 | 
					    <item android:id="@+id/action_disconnect"
 | 
				
			||||||
          android:title="@string/action_disconnect"
 | 
					          android:title="@string/action_disconnect"
 | 
				
			||||||
          android:orderInCategory="104"
 | 
					          android:orderInCategory="104"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,13 @@
 | 
				
			|||||||
      xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
					      xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <item
 | 
				
			||||||
 | 
					        android:id="@+id/issue_tracker"
 | 
				
			||||||
 | 
					        app:showAsAction="never"
 | 
				
			||||||
 | 
					        android:orderInCategory="101"
 | 
				
			||||||
 | 
					        android:title="@string/issue_tracker_link" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <item android:id="@+id/about"
 | 
					    <item android:id="@+id/about"
 | 
				
			||||||
          android:title="@string/action_about"
 | 
					          android:title="@string/action_about"
 | 
				
			||||||
          android:orderInCategory="102"
 | 
					          android:orderInCategory="102"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,8 @@ class DatesTest {
 | 
				
			|||||||
    private val oldVersionDate = "2013-05-07 13:46:00"
 | 
					    private val oldVersionDate = "2013-05-07 13:46:00"
 | 
				
			||||||
    private val oldVersionDateVariant = "2021-03-21 10:32:00.000000"
 | 
					    private val oldVersionDateVariant = "2021-03-21 10:32:00.000000"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val bugVersionDate = "2023-12-19T10:30:53-05:00"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    fun new_version_date_should_be_parsed() {
 | 
					    fun new_version_date_should_be_parsed() {
 | 
				
			||||||
        val date = DateUtils.parseDate(newVersionDate)
 | 
					        val date = DateUtils.parseDate(newVersionDate)
 | 
				
			||||||
@@ -52,4 +54,14 @@ class DatesTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        assertEquals(expected, date)
 | 
					        assertEquals(expected, date)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun bug_version_variant_date_should_be_parsed() {
 | 
				
			||||||
 | 
					        val date = DateUtils.parseDate(bugVersionDate)
 | 
				
			||||||
 | 
					        val expected =
 | 
				
			||||||
 | 
					            LocalDateTime(1991, 3, 18, 3, 0, 0, 0).toInstant(TimeZone.currentSystemDefault())
 | 
				
			||||||
 | 
					                .toEpochMilliseconds()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertEquals(expected, date)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
package bou.amine.apps.readerforselfossv2.utils
 | 
					package bou.amine.apps.readerforselfossv2.utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.text.format.DateUtils
 | 
					import android.text.format.DateUtils
 | 
				
			||||||
 | 
					import io.github.aakira.napier.Napier
 | 
				
			||||||
import kotlinx.datetime.*
 | 
					import kotlinx.datetime.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
actual class DateUtils {
 | 
					actual class DateUtils {
 | 
				
			||||||
@@ -16,6 +17,7 @@ actual class DateUtils {
 | 
				
			|||||||
        // For now, we handle this in a hacky way, because kotlin only accepts iso formats
 | 
					        // For now, we handle this in a hacky way, because kotlin only accepts iso formats
 | 
				
			||||||
        actual fun parseDate(dateString: String): Long {
 | 
					        actual fun parseDate(dateString: String): Long {
 | 
				
			||||||
            var isoDateString: String =
 | 
					            var isoDateString: String =
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
                    if (dateString.matches(oldVersionFormat)) {
 | 
					                    if (dateString.matches(oldVersionFormat)) {
 | 
				
			||||||
                        dateString.replace(" ", "T")
 | 
					                        dateString.replace(" ", "T")
 | 
				
			||||||
                    } else if (dateString.matches(newVersionFormat)) {
 | 
					                    } else if (dateString.matches(newVersionFormat)) {
 | 
				
			||||||
@@ -23,6 +25,10 @@ actual class DateUtils {
 | 
				
			|||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        throw Exception("Unrecognized format for $dateString")
 | 
					                        throw Exception("Unrecognized format for $dateString")
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                } catch (e: Exception) {
 | 
				
			||||||
 | 
					                    Napier.e(e.stackTraceToString(), tag = "DateUtils.parseDate")
 | 
				
			||||||
 | 
					                    "1991-03-18T03:00:00"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return LocalDateTime.parse(isoDateString).toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds()
 | 
					            return LocalDateTime.parse(isoDateString).toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,6 @@ import bou.amine.apps.readerforselfossv2.rest.SelfossApi
 | 
				
			|||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
 | 
					import bou.amine.apps.readerforselfossv2.service.AppSettingsService
 | 
				
			||||||
import bou.amine.apps.readerforselfossv2.utils.*
 | 
					import bou.amine.apps.readerforselfossv2.utils.*
 | 
				
			||||||
import io.github.aakira.napier.Napier
 | 
					import io.github.aakira.napier.Napier
 | 
				
			||||||
import io.ktor.client.call.*
 | 
					 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.flow.MutableStateFlow
 | 
					import kotlinx.coroutines.flow.MutableStateFlow
 | 
				
			||||||
@@ -429,22 +428,19 @@ class Repository(
 | 
				
			|||||||
        return result
 | 
					        return result
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suspend fun shouldBeSelfossInstance(): Pair<Boolean, Boolean> {
 | 
					    suspend fun checkIfFetchFails(): Boolean {
 | 
				
			||||||
        var fetchFailed = true
 | 
					        var fetchFailed = true
 | 
				
			||||||
        var showSelfossOnlyModal = false
 | 
					 | 
				
			||||||
        if (isNetworkAvailable()) {
 | 
					        if (isNetworkAvailable()) {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                // Trying to fetch one item, and check someone is trying to use the app with
 | 
					                // Trying to fetch one item, and check someone is trying to use the app with
 | 
				
			||||||
                // a random rss feed, that would throw a NoTransformationFoundException
 | 
					                // a random rss feed, that would throw a NoTransformationFoundException
 | 
				
			||||||
                fetchFailed = !api.getItemsWithoutCatch().success
 | 
					                fetchFailed = !api.getItemsWithoutCatch().success
 | 
				
			||||||
            } catch (e: NoTransformationFoundException) {
 | 
					 | 
				
			||||||
                showSelfossOnlyModal = true
 | 
					 | 
				
			||||||
            } catch (e: Throwable) {
 | 
					            } catch (e: Throwable) {
 | 
				
			||||||
                Napier.e(e.stackTraceToString(), tag = "RepositoryImpl.shouldBeSelfossInstance")
 | 
					                Napier.e(e.stackTraceToString(), tag = "RepositoryImpl.shouldBeSelfossInstance")
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Pair(fetchFailed, showSelfossOnlyModal)
 | 
					        return fetchFailed
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suspend fun logout() {
 | 
					    suspend fun logout() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,3 +73,13 @@ fun SelfossModel.Item.toEntity(): ITEM =
 | 
				
			|||||||
        this.tags.joinToString(","),
 | 
					        this.tags.joinToString(","),
 | 
				
			||||||
        this.author,
 | 
					        this.author,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun SelfossModel.Tag.getColorHexCode(): String =
 | 
				
			||||||
 | 
					    if (this.color.length == 4) { // #000
 | 
				
			||||||
 | 
					        val char1 = this.color.get(1)
 | 
				
			||||||
 | 
					        val char2 = this.color.get(2)
 | 
				
			||||||
 | 
					        val char3 = this.color.get(3)
 | 
				
			||||||
 | 
					        "#$char1$char1$char2$char2$char3$char3"
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        this.color
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
		Reference in New Issue
	
	Block a user