Compare commits
10 Commits
8e9b87f00c
...
v124010191
Author | SHA1 | Date | |
---|---|---|---|
8f464d95fd | |||
5ccd6a3368 | |||
cdbded246e | |||
750c7758bd | |||
22f8b14ecd | |||
6e27d6d4e6 | |||
14ff4dbd05 | |||
390c2d0cf3 | |||
e58914ef58 | |||
a03f08fca1 |
23
CHANGELOG.md
23
CHANGELOG.md
@ -1,3 +1,26 @@
|
|||||||
|
**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**
|
**v123102961**
|
||||||
|
|
||||||
- chore: domain changes.
|
- chore: domain changes.
|
||||||
|
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 {
|
||||||
repository.updateApiInformation()
|
try {
|
||||||
|
repository.updateApiInformation()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (e.message?.startsWith("No transformation found") == true) {
|
||||||
|
Toast.makeText(
|
||||||
|
applicationContext,
|
||||||
|
R.string.application_selfoss_only,
|
||||||
|
Toast.LENGTH_LONG,
|
||||||
|
).show()
|
||||||
|
preferenceError()
|
||||||
|
showProgress(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
val result = repository.login()
|
val result = repository.login()
|
||||||
if (result) {
|
if (result) {
|
||||||
val (errorFetching, displaySelfossOnly) = repository.shouldBeSelfossInstance()
|
val errorFetching = repository.checkIfFetchFails()
|
||||||
if (!errorFetching && !displaySelfossOnly) {
|
if (!errorFetching) {
|
||||||
goToMain()
|
goToMain()
|
||||||
} else {
|
} else {
|
||||||
if (displaySelfossOnly) {
|
|
||||||
Toast.makeText(
|
|
||||||
applicationContext,
|
|
||||||
R.string.application_selfoss_only,
|
|
||||||
Toast.LENGTH_LONG,
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
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,12 +17,17 @@ 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 =
|
||||||
if (dateString.matches(oldVersionFormat)) {
|
try {
|
||||||
dateString.replace(" ", "T")
|
if (dateString.matches(oldVersionFormat)) {
|
||||||
} else if (dateString.matches(newVersionFormat)) {
|
dateString.replace(" ", "T")
|
||||||
dateString.split("+")[0]
|
} else if (dateString.matches(newVersionFormat)) {
|
||||||
} else {
|
dateString.split("+")[0]
|
||||||
throw Exception("Unrecognized format for $dateString")
|
} else {
|
||||||
|
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