Compare commits

..

8 Commits

16 changed files with 192 additions and 142 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,3 +9,19 @@ enum class ItemType(val position: Int, val type: String) {
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
}
}