Compare commits

...

21 Commits

Author SHA1 Message Date
f170d1157d chore: do not send reports on simulators.
All checks were successful
Check master code / build (push) Successful in 13m14s
Create tag / build (push) Successful in 7m34s
Create tag / createTagAndChangelog (push) Successful in 43s
Create tag / release (push) Successful in 5m15s
2025-03-09 18:17:41 +01:00
af4752f0f0 Merge pull request 'chore: do not send reports on simulators.' (#188) from chore-acra-simulator into master
Some checks failed
Check master code / build (push) Has been cancelled
Reviewed-on: #188
2025-03-09 17:11:28 +00:00
f0fa1a17b6 chore: do not send reports on simulators.
Some checks failed
Check PR code / build (pull_request) Has been cancelled
Check PR code / Lint (pull_request) Has been cancelled
Check PR code / translations (pull_request) Has been cancelled
2025-03-09 18:07:14 +01:00
bb84d1541c Merge pull request 'fix: Url validation was not failing login. Added tests.' (#186) from fix-invalid-url into master
Some checks failed
Check master code / build (push) Successful in 8m39s
Create tag / createTagAndChangelog (push) Has been cancelled
Create tag / release (push) Has been cancelled
Create tag / build (push) Has been cancelled
Reviewed-on: #186
2025-03-09 16:54:18 +00:00
c9227b2c1c Merge pull request 'chore: crowding ci integration.' (#187) from chore-crowdin-ci into master
Some checks failed
Check master code / build (push) Has been cancelled
Reviewed-on: #187
2025-03-09 16:54:02 +00:00
6eaad0c7c5 chore: we don't need to check if the url is valid in upsert screen.
All checks were successful
Check PR code / Lint (pull_request) Successful in 4m19s
Check PR code / translations (pull_request) Successful in 2m2s
Check PR code / build (pull_request) Successful in 15m54s
2025-03-09 16:24:38 +01:00
a1c98aa7d0 fix: Url validation was not failing login. Added tests. 2025-03-09 16:24:38 +01:00
d5ec118679 chore: crowding ci integration.
All checks were successful
Check PR code / Lint (pull_request) Successful in 3m20s
Check PR code / translations (pull_request) Successful in 3m44s
Check PR code / build (pull_request) Successful in 16m24s
2025-03-09 16:19:59 +01:00
a1c0241a58 Show a confirmation dialog before deleting sources (#185)
All checks were successful
Check master code / build (push) Successful in 15m3s
## Types of changes

- [ x ] I have read the **CONTRIBUTING** document.
- [ x ] My code follows the code style of this project.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ x ] All new and existing tests passed.
- [ x ] This is **NOT** translation related.

This is implements feature #156

I added a confirmation dialogue which pops up after tapping the delete source button. The popup displays the full name of the source to be deleted and allows the user to decide not to delete the source to prevent erroneous deletions.

I moved most of the logic into the viewholder. Can be easily reverted if you prefer.

All tests pass. I tested correct behavior in emulated versions of android API 25, 34 and 35.

Co-authored-by: Amine <amine.bouabdallaoui@pm.me>
Reviewed-on: #185
Co-authored-by: davidoskky <davidoskky@yahoo.it>
Co-committed-by: davidoskky <davidoskky@yahoo.it>
2025-03-09 13:49:32 +00:00
f38936f9b4 Changelog for v125020581 2025-02-27 21:08:25 +00:00
a90ccec707 fix: url can be empty ?
All checks were successful
Check master code / build (push) Successful in 14m37s
Create tag / build (push) Successful in 7m25s
Create tag / createTagAndChangelog (push) Successful in 44s
Create tag / release (push) Successful in 5m12s
2025-02-27 21:40:06 +01:00
2564b19726 Changelog for v125020471 2025-02-16 14:43:28 +00:00
61c7bb20cc chore: no more docker-compose.
All checks were successful
Create tag / build (push) Successful in 21m1s
Create tag / createTagAndChangelog (push) Successful in 1m21s
Create tag / release (push) Successful in 9m3s
Check master code / build (push) Successful in 34m35s
2025-02-16 15:17:51 +01:00
6a0f5baf0a bump: gradle plugin.
Some checks failed
Check master code / build (push) Has been cancelled
2025-02-16 14:57:34 +01:00
39f9505c00 Merge pull request 'fix: check index exists.' (#183) from fix-index into master
All checks were successful
Check master code / build (push) Successful in 8m3s
Reviewed-on: #183
2025-02-16 13:37:42 +00:00
6a6d447456 fix: check index exists.
All checks were successful
Check PR code / Lint (pull_request) Successful in 3m57s
Check PR code / build (pull_request) Successful in 13m44s
2025-02-16 13:57:42 +01:00
0bb4fe6aed Changelog for v125020411 2025-02-10 20:16:56 +00:00
7df4c3368c Merge pull request 'bump' (#182) from bump into master
All checks were successful
Check master code / build (push) Successful in 11m6s
Create tag / build (push) Successful in 7m35s
Create tag / createTagAndChangelog (push) Successful in 44s
Create tag / release (push) Successful in 4m35s
Reviewed-on: #182
2025-02-10 19:35:40 +00:00
c69635b5ae chore: non transiant R classes.
All checks were successful
Check PR code / Lint (pull_request) Successful in 3m36s
Check PR code / build (pull_request) Successful in 16m13s
2025-02-09 22:27:53 +01:00
3a829df70e Merge pull request 'fix: One more missing context.' (#181) from fix-one-more-context into master
All checks were successful
Check master code / build (push) Successful in 13m26s
Reviewed-on: #181
2025-02-09 20:44:29 +00:00
b20f6888f5 fix: One more missing context.
All checks were successful
Check PR code / Lint (pull_request) Successful in 1m1s
Check PR code / build (pull_request) Successful in 15m1s
2025-02-09 14:42:17 +01:00
39 changed files with 313 additions and 169 deletions

View File

@ -0,0 +1,10 @@
project_id_env: CROWDIN_PROJECT_ID
api_token_env: CROWDIN_PERSONAL_TOKEN
base_path: "../../../"
files:
- source: /androidApp/src/main/res/values/strings.xml
translation: /androidApp/src/main/res/values-%android_code%/%original_file_name%
translate_attributes: '0'
content_segmentation: '0'
preserve_hierarchy: true

View File

@ -26,9 +26,10 @@ jobs:
- uses: KengoTODA/actions-setup-docker-compose@v1
with:
version: "2.23.3"
- name: run selfoss
run: |
docker compose -f .gitea/workflows/assets/docker-compose.yml up -d
# TESTS ARE RUN LOCALLY
# - name: run selfoss
# run: |
# docker compose -f .gitea/workflows/assets/docker-compose.yml up -d
- name: coverage
run: |
./gradlew :koverHtmlReport
@ -39,7 +40,8 @@ jobs:
retention-days: 1
overwrite: true
include-hidden-files: true
- name: Clean
if: always()
run: |
docker compose -f .gitea/workflows/assets/docker-compose.yml stop
# TESTS ARE RUN LOCALLY
# - name: Clean
# if: always()
# run: |
# docker compose -f .gitea/workflows/assets/docker-compose.yml stop

View File

@ -3,6 +3,7 @@ on:
pull_request:
branches:
- master
- chore-crowdin-ci
jobs:
Lint:
@ -23,6 +24,56 @@ jobs:
run: ktlint 'shared/**/*.kt' 'androidApp/**/*.kt' '!shared/build'
- name: Detecting...
run: ./detekt-cli-1.23.7/bin/detekt-cli -c detekt.yml --excludes '**/shared/build/**/*.kt'
translations:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: upload translation sources
uses: crowdin/github-action@v2
with:
config: './.gitea/workflows/assets/crowdin.yml'
upload_sources: true
upload_translations: false
download_translations: false
create_pull_request: false
push_translations: false
env:
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
- name: wait
run: sleep 10s
- name: download translations
uses: crowdin/github-action@v2
with:
config: './.gitea/workflows/assets/crowdin.yml'
upload_sources: false
upload_translations: false
download_translations: true
create_pull_request: false
push_translations: false
env:
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
- name: Check for uncommitted changes
id: check-changes
uses: mskri/check-uncommitted-changes-action@v1.0.1
- name: Commit Changes
if: steps.check-changes.outputs.changes != ''
run: |
git config --global user.email aminecmi+giteadrone@pm.me
git config --global user.name giteadrone
git add ./androidApp/src/main/res/*
git commit -m "translation: translation files"
- name: Push changes
if: steps.check-changes.outputs.changes != ''
uses: appleboy/git-push-action@v1.0.0
with:
author_name: giteadrone
author_email: aminecmi+giteadrone@pm.me
remote: ${{ secrets.REMOTE_URL }}
ssh_key: ${{ secrets.PRIVATE_KEY }}
branch: ${{ github.head_ref || github.ref_name }}
build:
needs: Lint
uses: ./.gitea/workflows/common_build.yml

4
.gitignore vendored
View File

@ -323,4 +323,6 @@ fabric.properties
crowdin.properties
.kotlin/
build-cache/
build-cache/
act

View File

@ -1,3 +1,30 @@
**v125020581
- fix: url can be empty ?
- Changelog for v125020471
--------------------------------------------------------------------
**v125020471
- chore: no more docker-compose.
- bump: gradle plugin.
- Merge pull request 'fix: check index exists.' (#183) from fix-index into master
- fix: check index exists.
- Changelog for v125020411
--------------------------------------------------------------------
**v125020411
- Merge pull request 'bump' (#182) from bump into master
- chore: non transiant R classes.
- Merge pull request 'fix: One more missing context.' (#181) from fix-one-more-context into master
- bump
- fix: One more missing context.
--------------------------------------------------------------------
**v125010241
- Merge pull request 'fix: Link not opening.' (#178) from fix-open-link into master

View File

@ -56,7 +56,7 @@ class HomeActivityTest {
fun testMenuActions() {
onView(withId(R.id.action_search)).perform(click())
onView(
withId(R.id.search_src_text),
withId(com.google.android.material.R.id.search_src_text),
).check(matches(isFocused()))
onView(isRoot()).perform(ViewActions.pressBack())

View File

@ -60,9 +60,23 @@ class LoginActivityTest {
fun urlError() {
performLogin("10.0.2.2:8888")
onView(withId(R.id.urlView)).perform(click())
onView(withId(R.id.urlView)).check(matches(withError(R.string.login_url_problem)))
}
@Test
fun connectError() {
performLogin("http://10.0.2.2:8889")
onView(withId(R.id.urlView)).perform(click())
onView(withId(R.id.urlView)).check(matches(withError(R.string.wrong_infos)))
}
@Test
fun urlSlashError() {
performLogin("https://google.fr/toto")
onView(withId(R.id.urlView)).perform(click())
onView(withId(R.id.urlView)).check(matches(withError(R.string.login_url_problem)))
}
@Test
fun multiError() {
onView(withId(R.id.signInButton)).perform(click())

View File

@ -149,9 +149,10 @@ class LoginActivity :
.toString()
.trim()
failInvalidUrl(url)
failLoginDetails(password, login)
val cancelUrl = failInvalidUrl(url)
if (cancelUrl) return
val cancelDetails = failLoginDetails(password, login)
if (cancelDetails) return
showProgress(true)
appSettingsService.updateSelfSigned(binding.selfSigned.isChecked)
@ -193,7 +194,7 @@ class LoginActivity :
private fun failLoginDetails(
password: String,
login: String,
) {
): Boolean {
var lastFocusedView: View? = null
var cancel = false
if (isWithLogin) {
@ -210,9 +211,10 @@ class LoginActivity :
}
}
maybeCancelAndFocusView(cancel, lastFocusedView)
return cancel
}
private fun failInvalidUrl(url: String) {
private fun failInvalidUrl(url: String): Boolean {
val focusView = binding.urlView
var cancel = false
if (url.isBaseUrlInvalid()) {
@ -232,6 +234,7 @@ class LoginActivity :
}
}
maybeCancelAndFocusView(cancel, focusView)
return cancel
}
private fun maybeCancelAndFocusView(

View File

@ -109,6 +109,7 @@ class MyApp :
super.attachBaseContext(base)
initAcra {
sendReportsInDevMode = false
reportFormat = StringFormat.JSON
reportContent =
listOf(

View File

@ -161,12 +161,14 @@ class ReaderActivity :
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
if (allItems[position].starred) {
canRemoveFromFavorite()
} else {
canFavorite()
if (!allItems.isNullOrEmpty() && allItems.size >= position) {
if (allItems[position].starred) {
canRemoveFromFavorite()
} else {
canFavorite()
}
readItem(allItems[position])
}
readItem(allItems[position])
}
},
)

View File

@ -9,11 +9,9 @@ import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
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
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -31,7 +29,6 @@ class UpsertSourceActivity :
override val di by closestDI()
private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -76,13 +73,7 @@ class UpsertSourceActivity :
override fun onResume() {
super.onResume()
val baseUrl = appSettingsService.getBaseUrl()
if (baseUrl.isEmpty() || baseUrl.isBaseUrlInvalid()) {
mustLoginToAddSource()
} else {
handleSpoutsSpinner()
}
handleSpoutsSpinner()
}
@Suppress("detekt:SwallowedException")
@ -157,13 +148,6 @@ class UpsertSourceActivity :
}
}
private fun mustLoginToAddSource() {
Toast.makeText(this, getString(R.string.addStringNoUrl), Toast.LENGTH_SHORT).show()
val i = Intent(this, LoginActivity::class.java)
startActivity(i)
finish()
}
private fun handleSaveSource() {
val url = binding.sourceUri.text.toString()

View File

@ -6,9 +6,8 @@ import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.UpsertSourceActivity
@ -32,69 +31,21 @@ class SourcesListAdapter(
private val items: ArrayList<SelfossModel.SourceDetail>,
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>(),
DIAware {
private val c: Context = app.baseContext
private lateinit var binding: SourceListItemBinding
override val di: DI by closestDI(app)
private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance()
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int,
): ViewHolder {
binding = SourceListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding.root)
val binding = SourceListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(
holder: ViewHolder,
position: Int,
) {
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()) {
binding.itemImage.setBackgroundAndText(itm.title.getHtmlDecoded())
} else {
c.circularDrawable(itm.getIcon(repository.baseUrl), binding.itemImage, appSettingsService)
}
if (!itm.error.isNullOrBlank()) {
binding.errorText.visibility = View.VISIBLE
binding.errorText.text = itm.error
} else {
binding.errorText.visibility = View.GONE
}
binding.sourceTitle.text = itm.title.getHtmlDecoded()
holder.bind(items[position], position)
}
override fun getItemId(position: Int) = position.toLong()
@ -104,6 +55,72 @@ class SourcesListAdapter(
override fun getItemCount(): Int = items.size
inner class ViewHolder(
val mView: ConstraintLayout,
) : RecyclerView.ViewHolder(mView)
val binding: SourceListItemBinding,
) : RecyclerView.ViewHolder(binding.root) {
private val context: Context = app.applicationContext
private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance()
fun bind(
source: SelfossModel.SourceDetail,
position: Int,
) {
binding.apply {
sourceTitle.text = source.title.getHtmlDecoded()
if (source.getIcon(repository.baseUrl).isEmpty()) {
itemImage.setBackgroundAndText(source.title.getHtmlDecoded())
} else {
context.circularDrawable(source.getIcon(repository.baseUrl), itemImage, appSettingsService)
}
errorText.apply {
visibility = if (!source.error.isNullOrBlank()) View.VISIBLE else View.GONE
text = source.error
}
deleteBtn.setOnClickListener { showDeleteConfirmationDialog(source, position) }
root.setOnClickListener {
repository.setSelectedSource(source)
app.startActivity(Intent(app, UpsertSourceActivity::class.java))
}
}
}
private fun showDeleteConfirmationDialog(
source: SelfossModel.SourceDetail,
position: Int,
) {
AlertDialog
.Builder(app)
.setTitle(app.getString(R.string.confirm_delete_title))
.setMessage(app.getString(R.string.confirm_delete_message, source.title))
.setPositiveButton(android.R.string.ok) { _, _ -> deleteSource(source, position) }
.setNegativeButton(android.R.string.cancel, null)
.show()
}
private fun deleteSource(
source: SelfossModel.SourceDetail,
position: Int,
) {
CoroutineScope(Dispatchers.IO).launch {
val successfullyDeletedSource = repository.deleteSource(source.id, source.title)
launch(Dispatchers.Main) {
if (successfullyDeletedSource) {
items.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, itemCount)
} else {
Toast
.makeText(
app,
R.string.can_delete_source,
Toast.LENGTH_SHORT,
).show()
}
}
}
}
}
}

View File

@ -75,7 +75,7 @@ class ArticleFragment :
private var colorSurface: Int = 0
private var fontSize: Int = DEFAULT_FONT_SIZE
private lateinit var item: SelfossModel.Item
private lateinit var url: String
private var url: String? = null
private lateinit var contentText: String
private lateinit var contentSource: String
private lateinit var contentImage: String
@ -118,8 +118,8 @@ class ArticleFragment :
e.sendSilentlyWithAcra()
}
colorOnSurface = getColorFromAttr(R.attr.colorOnSurface)
colorSurface = getColorFromAttr(R.attr.colorSurface)
colorOnSurface = getColorFromAttr(com.google.android.material.R.attr.colorOnSurface)
colorSurface = getColorFromAttr(com.google.android.material.R.attr.colorSurface)
contentText = item.content
contentTitle = item.title.getHtmlDecoded()
@ -168,8 +168,8 @@ class ArticleFragment :
private fun handleContent() {
if (contentText.isEmptyOrNullOrNullString()) {
if (repository.isNetworkAvailable()) {
getContentFromMercury()
if (repository.isNetworkAvailable() && url.isUrlValid()) {
getContentFromMercury(url!!)
}
} else {
binding.titleView.text = contentTitle
@ -271,7 +271,7 @@ class ArticleFragment :
}
@Suppress("detekt:SwallowedException")
private fun getContentFromMercury() {
private fun getContentFromMercury(url: String) {
binding.progressBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch {
@ -424,10 +424,10 @@ class ArticleFragment :
var baseUrl: String? = null
try {
val itemUrl = URL(url)
val itemUrl = URL(url.orEmpty())
baseUrl = itemUrl.protocol + "://" + itemUrl.host
} catch (e: MalformedURLException) {
e.sendSilentlyWithAcraWithName("htmlToWebview > $url")
e.sendSilentlyWithAcraWithName("htmlToWebview > ${url.orEmpty()}")
}
val fontName: String =

View File

@ -83,7 +83,15 @@ class FilterSheetFragment :
val sourceGroup = binding.sourcesGroup
repository.getSourcesDetailsOrStats().forEachIndexed { _, source ->
val c = Chip(context)
val c: Chip? =
maybeIfContext {
Chip(it)
} as Chip?
if (c == null) {
return
}
c.ellipsize = TextUtils.TruncateAt.END
maybeIfContext {
@ -145,7 +153,11 @@ class FilterSheetFragment :
val tags = repository.getTags()
tags.forEachIndexed { _, tag ->
val c = Chip(context)
val c: Chip? = maybeIfContext { Chip(it) } as Chip?
if (c == null) {
return
}
c.ellipsize = TextUtils.TruncateAt.END
c.text = tag.tag

View File

@ -11,22 +11,24 @@ import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcra
import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
fun Context.shareLink(
itemUrl: String,
itemUrl: String?,
itemTitle: String,
) {
val sendIntent = Intent()
sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
sendIntent.action = Intent.ACTION_SEND
sendIntent.putExtra(Intent.EXTRA_TEXT, itemUrl.toStringUriWithHttp())
sendIntent.putExtra(Intent.EXTRA_SUBJECT, itemTitle)
sendIntent.type = "text/plain"
startActivity(
Intent
.createChooser(
sendIntent,
getString(R.string.share),
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
)
if (itemUrl.isUrlValid()) {
val sendIntent = Intent()
sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
sendIntent.action = Intent.ACTION_SEND
sendIntent.putExtra(Intent.EXTRA_TEXT, itemUrl!!.toStringUriWithHttp())
sendIntent.putExtra(Intent.EXTRA_SUBJECT, itemTitle)
sendIntent.type = "text/plain"
startActivity(
Intent
.createChooser(
sendIntent,
getString(R.string.share),
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
)
}
}
@ColorInt

View File

@ -15,12 +15,12 @@ import android.widget.Toast
import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.ReaderActivity
import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
fun Context.openItemUrl(
currentItem: Int,
linkDecoded: String,
linkDecoded: String?,
articleViewer: Boolean,
app: Activity,
) {
@ -37,12 +37,13 @@ fun Context.openItemUrl(
intent.putExtra("currentItem", currentItem)
app.startActivity(intent)
} else {
this.openUrlInBrowserAsNewTask(linkDecoded)
this.openUrlInBrowserAsNewTask(linkDecoded!!)
}
}
}
fun String.isUrlValid(): Boolean = this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
fun String?.isUrlValid(): Boolean =
!this.isEmptyOrNullOrNullString() && this!!.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
fun String.isBaseUrlInvalid(): Boolean {
val baseUrl = this.toHttpUrlOrNull()
@ -56,14 +57,16 @@ fun String.isBaseUrlInvalid(): Boolean {
}
fun Context.openItemUrlInBrowserAsNewTask(i: SelfossModel.Item) {
this.openUrlInBrowserAsNewTask(i.getLinkDecoded().toStringUriWithHttp())
this.openUrlInBrowserAsNewTask(i.getLinkDecoded())
}
fun Context.openUrlInBrowserAsNewTask(url: String) {
val intent = Intent(Intent.ACTION_VIEW)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.data = Uri.parse(url)
this.mayBeStartActivity(intent)
fun Context.openUrlInBrowserAsNewTask(url: String?) {
if (url.isUrlValid()) {
val intent = Intent(Intent.ACTION_VIEW)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.data = Uri.parse(url)
this.mayBeStartActivity(intent)
}
}
fun Context.openUrlInBrowser(url: String) {

View File

@ -22,5 +22,5 @@ class AcraReportingAdministrator : ReportingAdministrator {
context: Context,
config: CoreConfiguration,
crashReportData: CrashReportData,
): Boolean = crashReportData.get("BRAND") != "redroid"
): Boolean = crashReportData.get("BRAND") != "redroid" && !crashReportData.get("PHONE_MODEL").toString().startsWith("sdk_gphone")
}

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"No s'han llegit totes les publicacions"</string>
<string name="all_posts_read">"S'han llegit totes les publicacions"</string>
<string name="undo_string">"Desfés"</string>
<string name="addStringNoUrl">"Inicieu la sessió per afegir fonts."</string>
<string name="cant_get_sources">"No es pot obtenir la llista de fonts."</string>
<string name="cant_create_source">"No es pot crear la font."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -129,4 +127,6 @@
<string name="action_about">"Quant a"</string>
<string name="marked_as_read">"Element llegit"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"Nicht alle Beiträge wurden gelesen"</string>
<string name="all_posts_read">"Alle Beiträge wurden gelesen"</string>
<string name="undo_string">"Rückgängig"</string>
<string name="addStringNoUrl">"Melde dich an um Quellen hinzuzufügen."</string>
<string name="cant_get_sources">"Quellen können nicht abgerufen werden."</string>
<string name="cant_create_source">"Quelle kann nicht gespeichert werden."</string>
<string name="cant_get_spouts_no_network">"Fehler beim Laden der Spouts-Liste aufgrund von Netzwerkproblemen."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Blocksatz</string>
<string name="settings_reader_font">Schriftgröße im Lesemodus</string>
<string name="remove_source">Quelle entfernen</string>
<string name="pref_theme_title">Heller/Dunkler Modus</string>
<string name="mode_dark">Dunkler Modus</string>
<string name="mode_system">Systemeinstellungen übernehmen</string>
<string name="mode_light">Heller Modus</string>
@ -129,4 +127,6 @@
<string name="action_about">"Über"</string>
<string name="marked_as_read">"Artikel gelesen"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"No todas las publicaciones fueron leídas"</string>
<string name="all_posts_read">"Todas las publicaciones fueron leídas"</string>
<string name="undo_string">"Deshacer"</string>
<string name="addStringNoUrl">"Iniciar sesión para añadir fuentes."</string>
<string name="cant_get_sources">"No se puede obtener la lista de fuentes."</string>
<string name="cant_create_source">"No se puede crear la fuente."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justificado</string>
<string name="settings_reader_font">Modo lectura</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -129,4 +127,6 @@
<string name="action_about">"Acerca de"</string>
<string name="marked_as_read">"Artículo leído"</string>
<string name="marked_as_unread">"Artículo no leído"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"Tous les posts n'ont pas été lus"</string>
<string name="all_posts_read">"Tous les posts sont lus"</string>
<string name="undo_string">"Annuler"</string>
<string name="addStringNoUrl">"Identifiez-vous pour ajouter une source."</string>
<string name="cant_get_sources">"Impossible de récupérer la liste des sources."</string>
<string name="cant_create_source">"Impossible de créer la source."</string>
<string name="cant_get_spouts_no_network">"Impossible d'obtenir la liste des spouts en raison d'un problème de réseau."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justifier le texte</string>
<string name="settings_reader_font">Police du lecteur d\'articles</string>
<string name="remove_source">Supprimer la source</string>
<string name="pref_theme_title">Thème Clair/Sombre</string>
<string name="mode_dark">Thème sombre</string>
<string name="mode_system">Utiliser les paramètres système</string>
<string name="mode_light">Thème clair</string>
@ -129,4 +127,6 @@
<string name="action_about">"À propos"</string>
<string name="marked_as_read">"Marqué comme lu"</string>
<string name="marked_as_unread">"Marqué comme non lu"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"Non se leron todas as publicacións"</string>
<string name="all_posts_read">"Leronse todas as publicacións"</string>
<string name="undo_string">"Desfacer"</string>
<string name="addStringNoUrl">"Accede pra engadir fontes."</string>
<string name="cant_get_sources">"Non se pode obter a lista de fontes."</string>
<string name="cant_create_source">"Non se pode crear unha fonte."</string>
<string name="cant_get_spouts_no_network">"Non se pode obter a lista de spouts por mor dun erro de rede."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Xustificado</string>
<string name="settings_reader_font">Modo lector</string>
<string name="remove_source">Eliminar fonte</string>
<string name="pref_theme_title">Modo Claro/Escuro</string>
<string name="mode_dark">Modo escuro</string>
<string name="mode_system">Seguir axustes do sistema</string>
<string name="mode_light">Modo claro</string>
@ -129,4 +127,6 @@
<string name="action_about">"Acerca de"</string>
<string name="marked_as_read">"Elemento lido"</string>
<string name="marked_as_unread">"Elemento non lido"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"Semua pos belum dibaca"</string>
<string name="all_posts_read">"Semua pos sudah dibaca"</string>
<string name="undo_string">"Urung"</string>
<string name="addStringNoUrl">"Masuk untuk menambah sumber."</string>
<string name="cant_get_sources">"Tidak bisa mendapatkan daftar sumber."</string>
<string name="cant_create_source">"Tidak dapat membuat sumber."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -129,4 +127,6 @@
<string name="action_about">"Tentang"</string>
<string name="marked_as_read">"Membaca item"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"All posts weren't read"</string>
<string name="all_posts_read">"Tutti i messaggi sono stati letti"</string>
<string name="undo_string">"Annulla"</string>
<string name="addStringNoUrl">"Autenticati per aggiungere fonti."</string>
<string name="cant_get_sources">"Can't get sources list."</string>
<string name="cant_create_source">"Can't create source."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -129,4 +127,6 @@
<string name="action_about">"Informazioni"</string>
<string name="marked_as_read">"Articolo letto"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"모든 게시물을 읽지 않았습니다."</string>
<string name="all_posts_read">"모든 게시물을 읽었습니다."</string>
<string name="undo_string">"실행 취소"</string>
<string name="addStringNoUrl">"로그인 소스를 추가 해야 합니다."</string>
<string name="cant_get_sources">"소스 리스트를 얻을 수 없습니다."</string>
<string name="cant_create_source">"소스를 만들 수 없습니다."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -129,4 +127,6 @@
<string name="action_about">"정보"</string>
<string name="marked_as_read">"항목 읽기"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"Fout bij markeren als gelezen"</string>
<string name="all_posts_read">"Alle artikelen gemarkeerd als gelezen"</string>
<string name="undo_string">"Ongedaan maken"</string>
<string name="addStringNoUrl">"Login om bronnen toe te voegen"</string>
<string name="cant_get_sources">"Kan de lijst met bronnen niet ophalen"</string>
<string name="cant_create_source">"Kan bron niet creëeren"</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -129,4 +127,6 @@
<string name="action_about">"Over"</string>
<string name="marked_as_read">"Artikel gelezen"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"Nenhum post foi lido"</string>
<string name="all_posts_read">"Todos os posts foram lidos"</string>
<string name="undo_string">"Desfazer"</string>
<string name="addStringNoUrl">"Faça login para adicionar fontes."</string>
<string name="cant_get_sources">"Não é possível obter a lista de fontes."</string>
<string name="cant_create_source">"Não é possível criar fonte."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -129,4 +127,6 @@
<string name="action_about">"Sobre"</string>
<string name="marked_as_read">"Item lido"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"Todas as postagens não foram lidas"</string>
<string name="all_posts_read">"Todas as postagens foram lidas"</string>
<string name="undo_string">"Desfazer"</string>
<string name="addStringNoUrl">"Logar para adicionar fontes."</string>
<string name="cant_get_sources">"Não é possível obter a lista de fontes."</string>
<string name="cant_create_source">"Não é possível criar a fonte."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -129,4 +127,6 @@
<string name="action_about">"Sobre"</string>
<string name="marked_as_read">"Item lido"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"All posts weren't read"</string>
<string name="all_posts_read">"All posts were read"</string>
<string name="undo_string">"Undo"</string>
<string name="addStringNoUrl">"Log in to add sources."</string>
<string name="cant_get_sources">"Can't get sources list."</string>
<string name="cant_create_source">"Can't create source."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -129,4 +127,6 @@
<string name="action_about">"මේ ගැන"</string>
<string name="marked_as_read">"Item read"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"Tüm mesajlar okunmadı"</string>
<string name="all_posts_read">"Tüm mesajlar okundu"</string>
<string name="undo_string">"Geri al"</string>
<string name="addStringNoUrl">"Kaynakları eklemek için giriş yapın."</string>
<string name="cant_get_sources">"Kaynakları listesi alınamıyor."</string>
<string name="cant_create_source">"Kaynak oluşturulamıyor."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -129,4 +127,6 @@
<string name="action_about">"Hakkında"</string>
<string name="marked_as_read">"Öğeleri oku"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"所有帖子都未读"</string>
<string name="all_posts_read">"所有帖子已读"</string>
<string name="undo_string">"撤销"</string>
<string name="addStringNoUrl">"登录以添加数据源。"</string>
<string name="cant_get_sources">"无法获取数据列表。"</string>
<string name="cant_create_source">"无法创建源数据。"</string>
<string name="cant_get_spouts_no_network">"由于网络问题,无法获取 spouts 列表。"</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">左右对齐</string>
<string name="settings_reader_font">阅读器字体</string>
<string name="remove_source">删除源</string>
<string name="pref_theme_title">浅色/深色模式</string>
<string name="mode_dark">深色模式</string>
<string name="mode_system">遵循系统设置</string>
<string name="mode_light">浅色模式</string>
@ -129,4 +127,6 @@
<string name="action_about">"关于我们"</string>
<string name="marked_as_read">"已读"</string>
<string name="marked_as_unread">"未读条目"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -24,7 +24,6 @@
<string name="all_posts_not_read">"所有帖子都未读"</string>
<string name="all_posts_read">"所有帖子已读"</string>
<string name="undo_string">"撤销"</string>
<string name="addStringNoUrl">"登录以添加数据源。"</string>
<string name="cant_get_sources">"无法获取数据列表。"</string>
<string name="cant_create_source">"无法创建源数据。"</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -107,7 +106,6 @@
<string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -129,4 +127,6 @@
<string name="action_about">"关于我们"</string>
<string name="marked_as_read">"已读"</string>
<string name="marked_as_unread">"未讀項目"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -23,7 +23,6 @@
<string name="all_posts_not_read">"All posts weren't read"</string>
<string name="all_posts_read">"All posts were read"</string>
<string name="undo_string">"Undo"</string>
<string name="addStringNoUrl">"Log in to add sources."</string>
<string name="cant_get_sources">"Can't get sources list."</string>
<string name="cant_create_source">"Can't create source."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
@ -109,7 +108,6 @@
<string name="open_sans_font_id" translatable="false">open_sans</string>
<string name="roboto_font_id" translatable="false">roboto</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
@ -131,4 +129,6 @@
<string name="action_about">"About"</string>
<string name="marked_as_read">"Item read"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="confirm_delete_title">Confirm Deletion</string>
<string name="confirm_delete_message">Are you sure you want to delete the following source?\n%s</string>
</resources>

View File

@ -1,7 +1,7 @@
plugins {
// trick: for the same plugin versions in all sub-modules
id("com.android.application").version("8.8.0").apply(false)
id("com.android.library").version("8.8.0").apply(false)
id("com.android.application").version("8.8.1").apply(false)
id("com.android.library").version("8.8.1").apply(false)
id("org.jetbrains.kotlin.android").version("2.1.0").apply(false)
kotlin("multiplatform").version("2.1.0").apply(false)
id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").apply(false)

View File

@ -0,0 +1,7 @@
**v125020411**
- Merge pull request 'bump' (#182) from bump into master
- chore: non transiant R classes.
- Merge pull request 'fix: One more missing context.' (#181) from fix-one-more-context into master
- bump
- fix: One more missing context.

View File

@ -0,0 +1,7 @@
**v125020471**
- chore: no more docker-compose.
- bump: gradle plugin.
- Merge pull request 'fix: check index exists.' (#183) from fix-index into master
- fix: check index exists.
- Changelog for v125020411

View File

@ -0,0 +1,4 @@
**v125020581**
- fix: url can be empty ?
- Changelog for v125020471

View File

@ -19,11 +19,11 @@ kotlin.code.style=official
android.useAndroidX=true
#android.nonTransitiveRClass=true
android.enableJetifier=false
android.nonTransitiveRClass=false
android.nonTransitiveRClass=true
#MPP
kotlin.mpp.enableCInteropCommonization=true
org.gradle.parallel=true
org.gradle.caching=true
ignoreGitVersion=false
kotlin.native.cacheKind.iosX64=none
org.gradle.configureondemand=true
org.gradle.configureondemand=true

View File

@ -127,8 +127,8 @@ class SelfossModel {
val tags: List<String>,
val author: String? = null,
) {
fun getLinkDecoded(): String {
var stringUrl: String
fun getLinkDecoded(): String? {
var stringUrl: String?
stringUrl =
if (link.contains("//news.google.com/news/") && link.contains("&amp;url=")) {
link.substringAfter("&amp;url=")
@ -146,11 +146,7 @@ class SelfossModel {
stringUrl = "http:$stringUrl"
}
if (stringUrl.isEmptyOrNullOrNullString()) {
throw ModelException("Link $link was translated to $stringUrl, but was empty. Handle this.")
}
return stringUrl
return if (stringUrl.isEmptyOrNullOrNullString()) null else stringUrl
}
fun sourceAuthorAndDate(): String {