Compare commits

..

No commits in common. "20aab0ea62c9ade648152b60cdee5f7ffd3bf9a1" and "172362b53320d805c7ad92bf8cc048951ceb44c6" have entirely different histories.

16 changed files with 142 additions and 192 deletions

View File

@ -8,15 +8,15 @@ plugins {
kotlin("android") kotlin("android")
kotlin("kapt") kotlin("kapt")
id("com.mikepenz.aboutlibraries.plugin") id("com.mikepenz.aboutlibraries.plugin")
id("org.jetbrains.kotlinx.kover") id("org.jetbrains.kotlinx.kover") version "0.6.1"
} }
fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String { fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
val result: String = ByteArrayOutputStream().use { outputStream -> var result: String = ByteArrayOutputStream().use { outputStream ->
project.exec { project.exec {
commandLine = cmd.split(" ") commandLine = cmd.split(" ")
standardOutput = outputStream standardOutput = outputStream
isIgnoreExitValue = ignore isIgnoreExitValue = ignore ?: false
} }
outputStream.toString() outputStream.toString()
} }
@ -24,8 +24,9 @@ fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
} }
fun gitVersion(): String { fun gitVersion(): String {
var process = ""
val maybeTagOfCurrentCommit = execWithOutput("git -C ../ describe --contains HEAD", true) val maybeTagOfCurrentCommit = execWithOutput("git -C ../ describe --contains HEAD", true)
val process = if (maybeTagOfCurrentCommit.isEmpty()) { process = if (maybeTagOfCurrentCommit.isEmpty()) {
println("No tag on current commit. Will take the latest one.") println("No tag on current commit. Will take the latest one.")
execWithOutput("git -C ../ for-each-ref refs/tags --sort=-refname --format='%(refname:short)' --count=1") execWithOutput("git -C ../ for-each-ref refs/tags --sort=-refname --format='%(refname:short)' --count=1")
} else { } else {
@ -57,22 +58,23 @@ android {
compileOptions { compileOptions {
isCoreLibraryDesugaringEnabled = true isCoreLibraryDesugaringEnabled = true
// Flag to enable support for the new language APIs // Flag to enable support for the new language APIs
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_11
} }
// For Kotlin projects // For Kotlin projects
kotlinOptions { kotlinOptions {
jvmTarget = "17" jvmTarget = "11"
} }
compileSdk = 34 compileSdk = 33
buildToolsVersion = "33.0.0"
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
} }
defaultConfig { defaultConfig {
applicationId = "bou.amine.apps.readerforselfossv2.android" applicationId = "bou.amine.apps.readerforselfossv2.android"
minSdk = 21 minSdk = 21
targetSdk = 34 targetSdk = 33
versionCode = versionCodeFromGit() versionCode = versionCodeFromGit()
versionName = versionNameFromGit() versionName = versionNameFromGit()
@ -85,7 +87,7 @@ android {
// tests // tests
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
packaging { packagingOptions {
resources { resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}" excludes += "/META-INF/{AL2.0,LGPL2.1}"
} }
@ -114,25 +116,25 @@ dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
implementation(project(":shared")) implementation(project(":shared"))
implementation("com.google.android.material:material:1.9.0") implementation("com.google.android.material:material:1.5.0")
implementation("androidx.appcompat:appcompat:1.6.1") implementation("androidx.appcompat:appcompat:1.4.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
implementation("androidx.preference:preference-ktx:1.2.1") implementation("androidx.preference:preference-ktx:1.1.1")
implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs"))) implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs")))
// Android Support // Android Support
implementation("androidx.appcompat:appcompat:1.6.1") implementation("androidx.appcompat:appcompat:1.4.1")
implementation("com.google.android.material:material:1.9.0") implementation("com.google.android.material:material:1.5.0")
implementation("androidx.recyclerview:recyclerview:1.3.1") implementation("androidx.recyclerview:recyclerview:1.3.0-alpha01")
implementation("androidx.legacy:legacy-support-v4:1.0.0") implementation("androidx.legacy:legacy-support-v4:1.0.0")
implementation("androidx.vectordrawable:vectordrawable:1.2.0-beta01") implementation("androidx.vectordrawable:vectordrawable:1.2.0-alpha02")
implementation("androidx.cardview:cardview:1.0.0") implementation("androidx.cardview:cardview:1.0.0")
implementation("androidx.annotation:annotation:1.7.0") implementation("androidx.annotation:annotation:1.3.0")
implementation("androidx.work:work-runtime-ktx:2.8.1") implementation("androidx.work:work-runtime-ktx:2.7.1")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("org.jsoup:jsoup:1.15.4") implementation("org.jsoup:jsoup:1.14.3")
//multidex //multidex
implementation("androidx.multidex:multidex:2.0.1") implementation("androidx.multidex:multidex:2.0.1")
@ -153,7 +155,7 @@ dependencies {
// Pager // Pager
implementation("me.relex:circleindicator:2.1.6") implementation("me.relex:circleindicator:2.1.6")
implementation("androidx.viewpager2:viewpager2:1.1.0-beta02") implementation("androidx.viewpager2:viewpager2:1.1.0-beta01")
//Dependency Injection //Dependency Injection
implementation("org.kodein.di:kodein-di:7.14.0") implementation("org.kodein.di:kodein-di:7.14.0")
@ -169,7 +171,7 @@ dependencies {
//PhotoView //PhotoView
implementation("com.github.chrisbanes:PhotoView:2.3.0") implementation("com.github.chrisbanes:PhotoView:2.3.0")
implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.core:core-ktx:1.8.0")
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
@ -182,7 +184,7 @@ dependencies {
//test //test
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk:1.12.0") testImplementation("io.mockk:mockk:1.12.0")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
implementation("ch.acra:acra-http:$acraVersion") implementation("ch.acra:acra-http:$acraVersion")

View File

@ -55,7 +55,6 @@
# maybe remove later ? # maybe remove later ?
-keep class * extends androidx.fragment.app.Fragment -keep class * extends androidx.fragment.app.Fragment
-dontwarn org.slf4j.impl.StaticLoggerBinder
# Keep `Companion` object fields of serializable classes. # Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects. # This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.

View File

@ -5,7 +5,6 @@ import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding
@ -37,10 +36,8 @@ class SourcesActivity : AppCompatActivity(), DIAware {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
binding.fab.rippleColor = ContextCompat.getColor(this, R.color.colorAccentDark) binding.fab.rippleColor = resources.getColor(R.color.colorAccentDark)
binding.fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent))
binding.fab.backgroundTintList =
ColorStateList.valueOf(ContextCompat.getColor(this, R.color.colorAccent))
} }
override fun onStop() { override fun onStop() {

View File

@ -6,7 +6,6 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView.ScaleType import android.widget.ImageView.ScaleType
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding
@ -61,7 +60,7 @@ class ItemCardAdapter(
binding.title.setOnTouchListener(LinkOnTouchListener()) binding.title.setOnTouchListener(LinkOnTouchListener())
binding.title.setLinkTextColor(ContextCompat.getColor(c, R.color.colorAccent)) binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
binding.sourceTitleAndDate.text = itm.sourceAuthorAndDate() binding.sourceTitleAndDate.text = itm.sourceAuthorAndDate()

View File

@ -4,7 +4,6 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding
@ -45,8 +44,7 @@ class ItemListAdapter(
binding.title.setOnTouchListener(LinkOnTouchListener()) binding.title.setOnTouchListener(LinkOnTouchListener())
binding.title.setLinkTextColor(ContextCompat.getColor(c, R.color.colorAccent)) binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
binding.sourceTitleAndDate.text = itm.sourceAuthorAndDate() binding.sourceTitleAndDate.text = itm.sourceAuthorAndDate()

View File

@ -10,21 +10,13 @@ import android.graphics.drawable.ColorDrawable
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.TypedValue import android.util.TypedValue
import android.view.GestureDetector import android.view.*
import android.view.InflateException
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse import android.webkit.WebResourceResponse
import android.webkit.WebSettings import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.core.widget.NestedScrollView import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import bou.amine.apps.readerforselfossv2.android.ImageActivity import bou.amine.apps.readerforselfossv2.android.ImageActivity
@ -43,7 +35,6 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.rest.MercuryApi import bou.amine.apps.readerforselfossv2.rest.MercuryApi
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.ImageMimeType
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.getImages import bou.amine.apps.readerforselfossv2.utils.getImages
import bou.amine.apps.readerforselfossv2.utils.getThumbnail import bou.amine.apps.readerforselfossv2.utils.getThumbnail
@ -63,7 +54,7 @@ import org.kodein.di.instance
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
import java.net.URL import java.net.URL
import java.util.Locale import java.util.*
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
@ -125,15 +116,9 @@ class ArticleFragment : Fragment(), DIAware {
fab = binding.fab fab = binding.fab
fab.backgroundTintList = fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent))
ColorStateList.valueOf(
ContextCompat.getColor(
requireContext(),
R.color.colorAccent
)
)
fab.rippleColor = ContextCompat.getColor(requireContext(), R.color.colorAccentDark) fab.rippleColor = resources.getColor(R.color.colorAccentDark)
val floatingToolbar: FloatingToolbar = handleFloatingToolbar() val floatingToolbar: FloatingToolbar = handleFloatingToolbar()
@ -218,8 +203,7 @@ class ArticleFragment : Fragment(), DIAware {
} }
floatingToolbar.attachFab(fab) floatingToolbar.attachFab(fab)
floatingToolbar.background = floatingToolbar.background = ColorDrawable(resources.getColor(R.color.colorAccent))
ColorDrawable(ContextCompat.getColor(requireContext(), R.color.colorAccent))
floatingToolbar.setClickListener( floatingToolbar.setClickListener(
object : FloatingToolbar.ItemClickListener { object : FloatingToolbar.ItemClickListener {
@ -304,7 +288,7 @@ class ArticleFragment : Fragment(), DIAware {
contentText = data.content.orEmpty() contentText = data.content.orEmpty()
htmlToWebview() htmlToWebview()
handleLeadImage(data.lead_image_url) handleLeadImage(data?.lead_image_url)
binding.nestedScrollView.scrollTo(0, 0) binding.nestedScrollView.scrollTo(0, 0)
binding.progressBar.visibility = View.GONE binding.progressBar.visibility = View.GONE
@ -329,19 +313,11 @@ class ArticleFragment : Fragment(), DIAware {
private fun handleImageLoading() { private fun handleImageLoading() {
binding.webcontent.webViewClient = object : WebViewClient() { binding.webcontent.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading( @Deprecated("Deprecated in Java")
view: WebView?, override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
request: WebResourceRequest? return if (context != null && url.isUrlValid() && binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
): Boolean {
val url = request?.url?.toString()
return if (context != null && url != null && url.isUrlValid() && binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
try { try {
requireContext().startActivity( requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
Intent(
Intent.ACTION_VIEW,
Uri.parse(url)
)
)
} catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) {
e.sendSilentlyWithAcraWithName("activityNotFound > $url") e.sendSilentlyWithAcraWithName("activityNotFound > $url")
} }
@ -351,42 +327,50 @@ class ArticleFragment : Fragment(), DIAware {
} }
} }
override fun shouldInterceptRequest( @Deprecated("Deprecated in Java")
view: WebView, override fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
request: WebResourceRequest
): WebResourceResponse? {
val url = request.url.toString()
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL) val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
if (url.lowercase(Locale.US).contains(".jpg") || url.lowercase(Locale.US)
val supportedExtensions = ImageMimeType.values().map { it.extension } .contains(".jpeg")
) {
val matchingExtension = supportedExtensions.find {
url.lowercase(Locale.US).contains(it)
}
matchingExtension?.let {
try { try {
val image = Glide.with(view) val image =
.asBitmap() Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
.apply(glideOptions) return WebResourceResponse(
.load(url) IMAGE_JPG,
.submit() "UTF-8",
.get() getBitmapInputStream(image, Bitmap.CompressFormat.JPEG)
)
val mimeType = ImageMimeType.findMimeTypeByExtension(it) } catch (e: ExecutionException) {
if (mimeType != null) { // Do nothing
return WebResourceResponse( }
mimeType, } else if (url.lowercase(Locale.US).contains(".png")) {
"UTF-8", try {
getBitmapInputStream(image, Bitmap.CompressFormat.WEBP) val image =
) Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
} return WebResourceResponse(
IMAGE_JPG,
"UTF-8",
getBitmapInputStream(image, Bitmap.CompressFormat.PNG)
)
} catch (e: ExecutionException) {
// Do nothing
}
} else if (url.lowercase(Locale.US).contains(".webp")) {
try {
val image =
Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
return WebResourceResponse(
IMAGE_JPG,
"UTF-8",
getBitmapInputStream(image, Bitmap.CompressFormat.WEBP)
)
} catch (e: ExecutionException) { } catch (e: ExecutionException) {
// Do nothing // Do nothing
} }
} }
return super.shouldInterceptRequest(view, request) return super.shouldInterceptRequest(view, url)
} }
} }
} }
@ -398,23 +382,13 @@ class ArticleFragment : Fragment(), DIAware {
binding.webcontent.settings.standardFontFamily = a.getString(0) binding.webcontent.settings.standardFontFamily = a.getString(0)
a.recycle()
binding.webcontent.visibility = View.VISIBLE binding.webcontent.visibility = View.VISIBLE
val colorOnSurface = TypedValue() val colorOnSurface = TypedValue()
requireContext().theme.resolveAttribute( requireContext().theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true)
com.google.android.material.R.attr.colorOnSurface,
colorOnSurface,
true
)
val colorSurface = TypedValue() val colorSurface = TypedValue()
requireContext().theme.resolveAttribute( requireContext().theme.resolveAttribute(R.attr.colorSurface, colorSurface, true)
com.google.android.material.R.attr.colorSurface,
colorSurface,
true
)
binding.webcontent.settings.useWideViewPort = true binding.webcontent.settings.useWideViewPort = true
binding.webcontent.settings.loadWithOverviewMode = true binding.webcontent.settings.loadWithOverviewMode = true
@ -483,7 +457,7 @@ class ArticleFragment : Fragment(), DIAware {
| color: ${ | color: ${
String.format( String.format(
"#%06X", "#%06X",
0xFFFFFF and ContextCompat.getColor(requireContext(), R.color.colorAccent) 0xFFFFFF and resources.getColor(R.color.colorAccent)
) )
} !important; } !important;
| } | }

View File

@ -12,7 +12,6 @@ import android.view.View
import android.view.View.GONE import android.view.View.GONE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import bou.amine.apps.readerforselfossv2.android.HomeActivity import bou.amine.apps.readerforselfossv2.android.HomeActivity
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding
@ -21,7 +20,7 @@ import bou.amine.apps.readerforselfossv2.repository.Repository
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
import com.bumptech.glide.request.target.CustomViewTarget import com.bumptech.glide.request.target.ViewTarget
import com.bumptech.glide.request.transition.Transition import com.bumptech.glide.request.transition.Transition
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
@ -83,13 +82,13 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
) { ) {
val sourceGroup = binding.sourcesGroup val sourceGroup = binding.sourcesGroup
repository.getSourcesDetailsOrStats().forEachIndexed { _, source -> repository.getSourcesDetailsOrStats().forEach { source ->
val c = Chip(context) val c = Chip(context)
c.ellipsize = TextUtils.TruncateAt.END c.ellipsize = TextUtils.TruncateAt.END
Glide.with(context) Glide.with(context)
.load(source.getIcon(repository.baseUrl)) .load(source.getIcon(repository.baseUrl))
.into(object : CustomViewTarget<Chip, Drawable>(c) { .into(object : ViewTarget<Chip?, Drawable?>(c) {
override fun onResourceReady( override fun onResourceReady(
resource: Drawable, resource: Drawable,
transition: Transition<in Drawable?>? transition: Transition<in Drawable?>?
@ -101,13 +100,6 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
} }
} }
override fun onLoadFailed(errorDrawable: Drawable?) {
c.chipIcon = errorDrawable
}
override fun onResourceCleared(placeholder: Drawable?) {
c.chipIcon = placeholder
}
}) })
c.text = source.title.getHtmlDecoded() c.text = source.title.getHtmlDecoded()
@ -152,7 +144,7 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
val tags = repository.getTags() val tags = repository.getTags()
tags.forEachIndexed { _, tag -> tags.forEach { tag ->
val c = Chip(context) val c = Chip(context)
c.ellipsize = TextUtils.TruncateAt.END c.ellipsize = TextUtils.TruncateAt.END
c.text = tag.tag c.text = tag.tag
@ -164,7 +156,7 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
Color.parseColor(tag.color) Color.parseColor(tag.color)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
e.sendSilentlyWithAcraWithName("color issue " + tag.color) e.sendSilentlyWithAcraWithName("color issue " + tag.color)
ContextCompat.getColor(context, R.color.colorPrimary) resources.getColor(R.color.colorPrimary)
} }
gd.setColor(gdColor) gd.setColor(gdColor)
gd.shape = GradientDrawable.RECTANGLE gd.shape = GradientDrawable.RECTANGLE

View File

@ -71,25 +71,23 @@ class SettingsActivity : AppCompatActivity(),
} }
override fun onPreferenceStartFragment( override fun onPreferenceStartFragment(
caller: PreferenceFragmentCompat, caller: PreferenceFragmentCompat,
pref: Preference pref: Preference
): Boolean { ): Boolean {
val fragmentClassName = pref.fragment ?: return false
// Instantiate the new Fragment // Instantiate the new Fragment
val args = pref.extras val args = pref.extras
val fragment = supportFragmentManager.fragmentFactory.instantiate( val fragment = supportFragmentManager.fragmentFactory.instantiate(
classLoader, classLoader,
fragmentClassName pref.fragment
).apply { ).apply {
arguments = args arguments = args
setTargetFragment(caller, 0)
} }
// Replace the existing Fragment with the new Fragment // Replace the existing Fragment with the new Fragment
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.replace(R.id.settings, fragment) .replace(R.id.settings, fragment)
.addToBackStack(null) .addToBackStack(null)
.commit() .commit()
title = pref.title title = pref.title
supportActionBar?.title = title supportActionBar?.title = title
return true return true

View File

@ -95,7 +95,6 @@ class LinkOnTouchListener : View.OnTouchListener {
if (link.isNotEmpty()) { if (link.isNotEmpty()) {
if (action == MotionEvent.ACTION_UP) { if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget) link[0].onClick(widget)
widget.performClick()
} }
ret = true ret = true
} }

View File

@ -1,18 +1,18 @@
buildscript { buildscript {
dependencies { dependencies {
// SqlDelight // SqlDelight
classpath("com.squareup.sqldelight:gradle-plugin:1.5.5") classpath("com.squareup.sqldelight:gradle-plugin:1.5.4")
} }
} }
plugins { plugins {
//trick: for the same plugin versions in all sub-modules //trick: for the same plugin versions in all sub-modules
id("com.android.application").version("8.1.0").apply(false) id("com.android.application").version("7.4.0").apply(false)
id("com.android.library").version("8.1.0").apply(false) id("com.android.library").version("7.4.0").apply(false)
id("org.jetbrains.kotlin.android").version("1.9.10").apply(false) kotlin("android").version("1.7.20").apply(false)
kotlin("multiplatform").version("1.9.10").apply(false) kotlin("multiplatform").version("1.7.20").apply(false)
id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").apply(false) id("com.mikepenz.aboutlibraries.plugin").version("10.5.1").apply(false)
id("org.jetbrains.kotlinx.kover").version("0.6.1").apply(true) id("org.jetbrains.kotlinx.kover") version "0.6.1"
} }
allprojects { allprojects {
@ -20,6 +20,7 @@ allprojects {
// maven { url = uri("https://nexus.amine-louveau.fr/repository/maven-public/")} // maven { url = uri("https://nexus.amine-louveau.fr/repository/maven-public/")}
google() google()
mavenCentral() mavenCentral()
jcenter()
maven { url = uri("https://www.jitpack.io") } maven { url = uri("https://www.jitpack.io") }
} }
} }

View File

@ -13,15 +13,22 @@
#Tue Mar 22 16:50:00 CET 2022 #Tue Mar 22 16:50:00 CET 2022
#Gradle #Gradle
org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M" org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
#Kotlin #Kotlin
kotlin.code.style=official kotlin.code.style=official
#Android #Android
android.useAndroidX=true android.useAndroidX=true
kotlin.native.enableDependencyPropagation=false
#android.nonTransitiveRClass=true #android.nonTransitiveRClass=true
android.enableJetifier=true android.enableJetifier=true
android.nonTransitiveRClass=true
#MPP #MPP
kotlin.mpp.enableCInteropCommonization=true kotlin.mpp.enableCInteropCommonization=true
kotlin.mpp.enableGranularSourceSetsMetadata=true
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.caching=true org.gradle.caching=true
ignoreGitVersion=false ignoreGitVersion=false

View File

@ -1,6 +1,6 @@
#Thu Jul 13 11:41:19 CEST 2023 #Mon Jan 23 20:47:46 CET 2023
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View File

@ -1,5 +1,3 @@
val ktorVersion = "2.3.2"
object SqlDelight { object SqlDelight {
const val runtime = "com.squareup.sqldelight:runtime:1.5.4" const val runtime = "com.squareup.sqldelight:runtime:1.5.4"
const val android = "com.squareup.sqldelight:android-driver:1.5.4" const val android = "com.squareup.sqldelight:android-driver:1.5.4"
@ -11,12 +9,12 @@ plugins {
kotlin("multiplatform") kotlin("multiplatform")
id("com.android.library") id("com.android.library")
id("com.squareup.sqldelight") id("com.squareup.sqldelight")
kotlin("plugin.serialization") version "1.9.0" kotlin("plugin.serialization") version "1.4.10"
id("org.jetbrains.kotlinx.kover") id("org.jetbrains.kotlinx.kover") version "0.6.1"
} }
kotlin { kotlin {
androidTarget() android()
listOf( listOf(
iosX64(), iosX64(),
@ -31,17 +29,16 @@ kotlin {
sourceSets { sourceSets {
val commonMain by getting { val commonMain by getting {
dependencies { dependencies {
implementation("io.ktor:ktor-client-core:$ktorVersion") implementation("io.ktor:ktor-client-core:2.1.1")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion") implementation("io.ktor:ktor-client-content-negotiation:2.1.1")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") implementation("io.ktor:ktor-serialization-kotlinx-json:2.1.1")
implementation("io.ktor:ktor-client-logging:$ktorVersion") implementation("io.ktor:ktor-client-logging:2.1.1")
implementation("io.ktor:ktor-client-auth:$ktorVersion") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1") implementation("io.ktor:ktor-client-auth:2.1.1")
implementation("org.jsoup:jsoup:1.14.3")
implementation("org.jsoup:jsoup:1.15.4")
//Dependency Injection //Dependency Injection
implementation("org.kodein.di:kodein-di:7.14.0") implementation("org.kodein.di:kodein-di:7.12.0")
//Settings //Settings
implementation("com.russhwolf:multiplatform-settings-no-arg:1.0.0-RC") implementation("com.russhwolf:multiplatform-settings-no-arg:1.0.0-RC")
@ -61,15 +58,14 @@ kotlin {
} }
val androidMain by getting { val androidMain by getting {
dependencies { dependencies {
implementation("com.squareup.okhttp3:okhttp:4.11.0") implementation("io.ktor:ktor-client-okhttp:2.1.1")
implementation("io.ktor:ktor-client-okhttp:2.2.4")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
// Sql // Sql
implementation(SqlDelight.android) implementation(SqlDelight.android)
} }
} }
val androidUnitTest by getting { val androidTest by getting {
dependencies { dependencies {
implementation(kotlin("test-junit")) implementation(kotlin("test-junit"))
implementation("junit:junit:4.13.2") implementation("junit:junit:4.13.2")
@ -102,14 +98,15 @@ kotlin {
} }
android { android {
compileSdk = 33 compileSdk = 32
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig { defaultConfig {
minSdk = 21 minSdk = 21
targetSdk = 32
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_1_8
} }
namespace = "bou.amine.apps.readerforselfossv2" namespace = "bou.amine.apps.readerforselfossv2"
} }

View File

@ -1,19 +1,13 @@
package bou.amine.apps.readerforselfossv2.utils package bou.amine.apps.readerforselfossv2.utils
import android.net.Uri import android.net.Uri
import android.os.Build
import android.text.Html import android.text.Html
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import org.jsoup.Jsoup import org.jsoup.Jsoup
import java.util.* import java.util.*
actual fun String.getHtmlDecoded(): String { actual fun String.getHtmlDecoded(): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return Html.fromHtml(this).toString()
Html.fromHtml(this, Html.FROM_HTML_MODE_COMPACT).toString()
} else {
@Suppress("DEPRECATION")
Html.fromHtml(this).toString()
}
} }
actual fun SelfossModel.Item.getIcon(baseUrl: String): String { actual fun SelfossModel.Item.getIcon(baseUrl: String): String {
@ -25,10 +19,19 @@ actual fun SelfossModel.Item.getThumbnail(baseUrl: String): String {
} }
actual fun SelfossModel.Item.getImages(): ArrayList<String> { actual fun SelfossModel.Item.getImages(): ArrayList<String> {
val doc = Jsoup.parse(content) val allImages = ArrayList<String>()
val images = doc.getElementsByTag("img")
return ArrayList(images.map { it.attr("src") }) for ( image in Jsoup.parse(content).getElementsByTag("img")) {
val url = image.attr("src")
if (url.lowercase(Locale.US).contains(".jpg") ||
url.lowercase(Locale.US).contains(".jpeg") ||
url.lowercase(Locale.US).contains(".png") ||
url.lowercase(Locale.US).contains(".webp"))
{
allImages.add(url)
}
}
return allImages
} }
actual fun SelfossModel.Source.getIcon(baseUrl: String): String { actual fun SelfossModel.Source.getIcon(baseUrl: String): String {

View File

@ -17,7 +17,7 @@ fun SOURCE.toView(): SelfossModel.SourceDetail =
this.id.toInt(), this.id.toInt(),
this.title, this.title,
null, null,
this.tags.split(","), this.tags?.split(","),
this.spout, this.spout,
this.error, this.error,
this.icon, this.icon,

View File

@ -8,20 +8,4 @@ enum class ItemType(val position: Int, val type: String) {
companion object { companion object {
fun fromInt(value: Int) = values().first { it.position == value } fun fromInt(value: Int) = values().first { it.position == value }
} }
}
enum class ImageMimeType(val extension: String, val mimeType: String) {
JPG(".jpg", "image/jpeg"),
JPEG(".jpeg", "image/jpeg"),
PNG(".png", "image/png"),
WEBP(".webp", "image/webp"),
GIF(".gif", "image/gif"),
SVG(".svg", "image/svg+xml"),
BMP(".bmp", "image/bmp"),
TIFF(".tiff", "image/tiff"),
TIF(".tif", "image/tiff");
companion object {
fun findMimeTypeByExtension(ext: String) = values().find { it.extension == ext }?.mimeType
}
} }