fixed getValue null bug and gradle cleanups

This commit is contained in:
Xilin Jia 2024-03-13 21:39:17 +00:00
parent 8225b6a9bd
commit 0b75b4c542
14 changed files with 227 additions and 259 deletions

1
.gitignore vendored
View File

@ -39,6 +39,7 @@ contributers.py
# View hierarchy captures # View hierarchy captures
captures captures
# other # other
.directory
*.odg# *.odg#
proguard proguard
libs libs

View File

@ -2,14 +2,13 @@ plugins {
id('com.android.application') id('com.android.application')
id 'kotlin-android' id 'kotlin-android'
id 'kotlin-kapt' id 'kotlin-kapt'
id 'com.google.devtools.ksp'
id('com.github.triplet.play') version '3.8.3' apply false id('com.github.triplet.play') version '3.8.3' apply false
} }
apply from: "../common.gradle"
apply from: "../playFlavor.gradle"
android { android {
namespace "ac.mdiq.podcini" namespace "ac.mdiq.podcini"
lint { lintOptions {
disable 'ObsoleteLintCustomCheck', 'CheckResult', 'UnusedAttribute', 'BatteryLife', 'InflateParams', disable 'ObsoleteLintCustomCheck', 'CheckResult', 'UnusedAttribute', 'BatteryLife', 'InflateParams',
'RestrictedApi', 'TrustAllX509TrustManager', 'ExportedReceiver', 'AllowBackup', 'VectorDrawableCompat', 'RestrictedApi', 'TrustAllX509TrustManager', 'ExportedReceiver', 'AllowBackup', 'VectorDrawableCompat',
'StaticFieldLeak', 'UseCompoundDrawables', 'NestedWeights', 'Overdraw', 'UselessParent', 'TextFields', 'StaticFieldLeak', 'UseCompoundDrawables', 'NestedWeights', 'Overdraw', 'UselessParent', 'TextFields',
@ -22,11 +21,25 @@ android {
buildConfig true buildConfig true
} }
defaultConfig { defaultConfig {
// Version code schema: minSdk 21
compileSdk 34
targetSdk 34
kotlinOptions {
jvmTarget = '17'
}
vectorDrawables.useSupportLibrary false
vectorDrawables.generatedDensities = []
testApplicationId "ac.mdiq.podcini.tests"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// Version code schema (not used):
// "1.2.3-beta4" -> 1020304 // "1.2.3-beta4" -> 1020304
// "1.2.3" -> 1020395 // "1.2.3" -> 1020395
versionCode 3020106 versionCode 3020107
versionName "4.2.3" versionName "4.2.4"
def commit = "" def commit = ""
try { try {
@ -65,33 +78,129 @@ android {
keyPassword project.getProperties().getOrDefault("releaseKeyPassword", "password") keyPassword project.getProperties().getOrDefault("releaseKeyPassword", "password")
} }
} }
buildTypes { buildTypes {
debug { debug {
applicationIdSuffix ".debug" applicationIdSuffix ".debug"
resValue "string", "app_name", "Podcini Debug"
resValue "string", "provider_authority", "ac.mdiq.podcini.debug.provider" resValue "string", "provider_authority", "ac.mdiq.podcini.debug.provider"
} }
release { release {
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard.cfg"
resValue "string", "app_name", "Podcini"
resValue "string", "provider_authority", "ac.mdiq.podcini.provider" resValue "string", "provider_authority", "ac.mdiq.podcini.provider"
minifyEnabled true minifyEnabled true
shrinkResources true shrinkResources true
signingConfig signingConfigs.releaseConfig signingConfig signingConfigs.releaseConfig
} }
} }
applicationVariants.all { variant ->
variant.outputs.all { output -> flavorDimensions += ["market"]
productFlavors {
free {
dimension "market"
}
play {
dimension "market"
}
}
applicationVariants.configureEach { variant ->
variant.outputs.configureEach { output ->
def applicationName = "Podcini" def applicationName = "Podcini"
outputFileName = "${applicationName}_${variant.buildType.name}_${defaultConfig.versionName}.apk" outputFileName = "${applicationName}_${variant.buildType.name}_${defaultConfig.versionName}.apk"
} }
} }
packagingOptions {
resources {
excludes += ["META-INF/LICENSE.txt",
"META-INF/NOTICE.txt",
"META-INF/CHANGES",
"META-INF/README.md"]
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
testOptions {
animationsDisabled = true
unitTests {
includeAndroidResources = true
}
}
lint {
disable "GradleDependency"
checkDependencies true
warningsAsErrors true
abortOnError true
checkGeneratedSources = true
}
buildFeatures {
viewBinding true
}
androidResources { androidResources {
additionalParameters "--no-version-vectors" additionalParameters "--no-version-vectors"
} }
} }
tasks.withType(Test).configureEach {
testLogging {
exceptionFormat "full"
events "skipped", "passed", "failed"
showStandardStreams true
displayGranularity 2
}
}
gradle.projectsEvaluated {
tasks.withType(JavaCompile).tap {
configureEach {
options.compilerArgs << "-Xlint"
}
}
}
apply plugin: 'com.github.spotbugs'
spotbugs {
effort = 'max'
reportLevel = 'medium'
excludeFilter = rootProject.file('config/spotbugs/exclude.xml')
ignoreFailures = true // Handled by printing task
}
gradle.taskGraph.beforeTask { task ->
if (task.name.toLowerCase().contains('spotbugs')) {
task.doLast {
def reportFile = task.project.file("build/reports/spotbugs/playDebug.xml")
if (!reportFile.exists()) return
def slurped = new groovy.xml.XmlSlurper().parse(reportFile)
def foundErrors = false
slurped['BugInstance'].each { bug ->
logger.error "[SpotBugs] ${bug['LongMessage']} [${bug.@'type'}]"
bug['SourceLine'].each { line ->
logger.error "[SpotBugs] ${line['Message']}"
foundErrors = true
}
}
if (foundErrors) {
throw new TaskExecutionException(task,
new Exception("SpotBugs violations were found. See output above for details."))
}
}
}
}
dependencies { dependencies {
implementation "androidx.core:core-ktx:$coreVersion" implementation "androidx.core:core-ktx:1.12.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
constraints { constraints {
@ -103,83 +212,87 @@ dependencies {
} }
} }
kapt "androidx.annotation:annotation:$annotationVersion" ksp "androidx.annotation:annotation:1.7.1"
implementation "androidx.appcompat:appcompat:$appcompatVersion" implementation "androidx.appcompat:appcompat:1.6.1"
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0' implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
implementation "androidx.fragment:fragment-ktx:$fragmentVersion" implementation "androidx.fragment:fragment-ktx:1.6.2"
implementation 'androidx.gridlayout:gridlayout:1.0.0' implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation "androidx.media:media:$mediaVersion" implementation "androidx.media:media:1.7.0"
implementation "androidx.media3:media3-exoplayer:$media3Version" implementation "androidx.media3:media3-exoplayer:1.2.1"
implementation "androidx.media3:media3-ui:$media3Version" implementation "androidx.media3:media3-ui:1.2.1"
implementation "androidx.media3:media3-datasource-okhttp:$media3Version" implementation "androidx.media3:media3-datasource-okhttp:1.2.1"
implementation "androidx.media3:media3-common:$media3Version" implementation "androidx.media3:media3-common:1.2.1"
implementation "androidx.palette:palette-ktx:$paletteVersion" implementation "androidx.palette:palette-ktx:1.0.0"
implementation "androidx.preference:preference-ktx:$preferenceVersion" implementation "androidx.preference:preference-ktx:1.2.1"
implementation "androidx.recyclerview:recyclerview:$recyclerViewVersion" implementation "androidx.recyclerview:recyclerview:1.3.2"
implementation "androidx.viewpager2:viewpager2:$viewPager2Version" implementation "androidx.viewpager2:viewpager2:1.1.0-beta02"
implementation "androidx.work:work-runtime:$workManagerVersion" implementation "androidx.work:work-runtime:2.9.0"
implementation "androidx.core:core-splashscreen:1.0.1" implementation "androidx.core:core-splashscreen:1.0.1"
implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.documentfile:documentfile:1.0.1'
implementation "com.google.android.material:material:$googleMaterialVersion" implementation "com.google.android.material:material:1.11.0"
implementation "org.apache.commons:commons-lang3:$commonslangVersion" implementation "org.apache.commons:commons-lang3:3.14.0"
implementation "commons-io:commons-io:$commonsioVersion" implementation "commons-io:commons-io:2.15.1"
implementation "org.jsoup:jsoup:$jsoupVersion" implementation "org.jsoup:jsoup:1.17.2"
implementation "com.github.bumptech.glide:glide:$glideVersion" implementation "com.github.bumptech.glide:glide:4.16.0"
implementation "com.github.bumptech.glide:okhttp3-integration:$glideVersion@aar" implementation "com.github.bumptech.glide:okhttp3-integration:4.16.0@aar"
kapt "com.github.bumptech.glide:compiler:$glideVersion" ksp "com.github.bumptech.glide:ksp:4.16.0"
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" implementation "com.squareup.okhttp3:okhttp:4.12.0"
implementation "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion" implementation "com.squareup.okhttp3:okhttp-urlconnection:4.12.0"
implementation "com.squareup.okio:okio:$okioVersion"
implementation "org.greenrobot:eventbus:$eventbusVersion"
kapt "org.greenrobot:eventbus-annotation-processor:$eventbusVersion"
implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
implementation "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion" implementation "com.squareup.okio:okio:3.7.0"
implementation "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
implementation 'com.leinardi.android:speed-dial:3.2.0' implementation "org.greenrobot:eventbus:3.3.1"
kapt "org.greenrobot:eventbus-annotation-processor:3.3.1"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
implementation "io.reactivex.rxjava2:rxjava:2.2.21"
implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.2.2"
implementation "com.joanzapata.iconify:android-iconify-material:2.2.2"
implementation 'com.leinardi.android:speed-dial:3.3.0'
implementation 'com.github.ByteHamster:SearchPreference:v2.5.0' implementation 'com.github.ByteHamster:SearchPreference:v2.5.0'
implementation 'com.github.skydoves:balloon:1.5.3' implementation 'com.github.skydoves:balloon:1.6.4'
implementation 'com.github.xabaras:RecyclerViewSwipeDecorator:1.3' implementation 'com.github.xabaras:RecyclerViewSwipeDecorator:1.3'
implementation "com.annimon:stream:$annimonStreamVersion" implementation "com.annimon:stream:1.2.2"
// Non-free dependencies: // Non-free dependencies:
playImplementation 'com.google.android.play:core-ktx:1.8.0' playImplementation 'com.google.android.play:core-ktx:1.8.1'
compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion" compileOnly "com.google.android.wearable:wearable:2.9.0"
// this one can not be updated?
androidTestImplementation 'com.nanohttpd:nanohttpd:2.1.1' androidTestImplementation 'com.nanohttpd:nanohttpd:2.1.1'
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion" androidTestImplementation "androidx.test.espresso:espresso-contrib:3.5.1"
androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion" androidTestImplementation "androidx.test.espresso:espresso-intents:3.5.1"
androidTestImplementation "androidx.test:runner:$runnerVersion" androidTestImplementation "androidx.test:runner:1.5.2"
androidTestImplementation "androidx.test:rules:$rulesVersion" androidTestImplementation "androidx.test:rules:1.5.0"
androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation "org.awaitility:awaitility:$awaitilityVersion" androidTestImplementation "org.awaitility:awaitility:4.2.0"
implementation "com.annimon:stream:$annimonStreamVersion" implementation "com.annimon:stream:1.2.2"
implementation 'com.github.mfietz:fyydlin:v0.5.0' implementation 'com.github.mfietz:fyydlin:v0.5.0'
// Non-free dependencies: // Non-free dependencies:
testImplementation "androidx.test:core:$testCoreVersion" testImplementation "androidx.test:core:1.5.0"
testImplementation "org.awaitility:awaitility:$awaitilityVersion" testImplementation "org.awaitility:awaitility:4.2.0"
testImplementation "junit:junit:$junitVersion" testImplementation "junit:junit:4.13.2"
testImplementation 'org.mockito:mockito-inline:5.2.0' testImplementation 'org.mockito:mockito-inline:5.2.0'
testImplementation "org.robolectric:robolectric:$robolectricVersion" testImplementation "org.robolectric:robolectric:4.11.1"
testImplementation 'javax.inject:javax.inject:1' testImplementation 'javax.inject:javax.inject:1'
playImplementation 'com.google.android.gms:play-services-base:17.5.0' playImplementation 'com.google.android.gms:play-services-base:18.3.0'
freeImplementation 'org.conscrypt:conscrypt-android:2.5.2' freeImplementation 'org.conscrypt:conscrypt-android:2.5.2'
playApi 'androidx.mediarouter:mediarouter:1.6.0' playApi 'androidx.mediarouter:mediarouter:1.6.0'
playApi "com.google.android.support:wearable:$wearableSupportVersion" playApi "com.google.android.support:wearable:2.9.0"
playApi 'com.google.android.gms:play-services-cast-framework:21.2.0' playApi 'com.google.android.gms:play-services-cast-framework:21.4.0'
} }
kapt { kapt {

View File

@ -1,8 +1,8 @@
package de.test.podcini.util.service.download package de.test.podcini.util.service.download
import ac.mdiq.podcini.BuildConfig
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
import ac.mdiq.podcini.BuildConfig
import fi.iki.elonen.NanoHTTPD import fi.iki.elonen.NanoHTTPD
import fi.iki.elonen.NanoHTTPD.Response.IStatus import fi.iki.elonen.NanoHTTPD.Response.IStatus
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils

View File

@ -19,17 +19,17 @@ class Atom : Namespace() {
state.items.add(state.currentItem!!) state.items.add(state.currentItem!!)
state.currentItem!!.feed = state.feed state.currentItem!!.feed = state.feed
} else if (localName.matches(isText.toRegex())) { } else if (localName.matches(isText.toRegex())) {
val type = attributes.getValue(TEXT_TYPE) val type: String? = attributes.getValue(TEXT_TYPE)
return AtomText(localName, this, type) return AtomText(localName, this, type)
} else if (LINK == localName) { } else if (LINK == localName) {
val href = attributes.getValue(LINK_HREF) val href: String? = attributes.getValue(LINK_HREF)
val rel = attributes.getValue(LINK_REL) val rel: String? = attributes.getValue(LINK_REL)
val parent = state.tagstack.peek() val parent = state.tagstack.peek()
if (parent.name.matches(isFeedItem.toRegex())) { if (parent.name.matches(isFeedItem.toRegex())) {
if (rel == null || LINK_REL_ALTERNATE == rel) { if (rel == null || LINK_REL_ALTERNATE == rel) {
state.currentItem!!.link = href state.currentItem!!.link = href
} else if (LINK_REL_ENCLOSURE == rel) { } else if (LINK_REL_ENCLOSURE == rel) {
val strSize = attributes.getValue(LINK_LENGTH) val strSize: String? = attributes.getValue(LINK_LENGTH)
var size: Long = 0 var size: Long = 0
try { try {
if (strSize != null) { if (strSize != null) {
@ -38,7 +38,7 @@ class Atom : Namespace() {
} catch (e: NumberFormatException) { } catch (e: NumberFormatException) {
Log.d(TAG, "Length attribute could not be parsed.") Log.d(TAG, "Length attribute could not be parsed.")
} }
val mimeType = getMimeType(attributes.getValue(LINK_TYPE), href) val mimeType: String? = getMimeType(attributes.getValue(LINK_TYPE), href)
val currItem = state.currentItem val currItem = state.currentItem
if (isMediaFile(mimeType) && currItem != null && !currItem.hasMedia()) { if (isMediaFile(mimeType) && currItem != null && !currItem.hasMedia()) {
@ -49,7 +49,7 @@ class Atom : Namespace() {
} }
} else if (parent.name.matches(isFeed.toRegex())) { } else if (parent.name.matches(isFeed.toRegex())) {
if (rel == null || LINK_REL_ALTERNATE == rel) { if (rel == null || LINK_REL_ALTERNATE == rel) {
val type = attributes.getValue(LINK_TYPE) val type: String? = attributes.getValue(LINK_TYPE)
/* /*
* Use as link if a) no type-attribute is given and * Use as link if a) no type-attribute is given and
* feed-object has no link yet b) type of link is * feed-object has no link yet b) type of link is
@ -60,20 +60,20 @@ class Atom : Namespace() {
state.feed.link = href state.feed.link = href
} else if (LINK_TYPE_ATOM == type || LINK_TYPE_RSS == type) { } else if (LINK_TYPE_ATOM == type || LINK_TYPE_RSS == type) {
// treat as podlove alternate feed // treat as podlove alternate feed
var title = attributes.getValue(LINK_TITLE) var title: String? = attributes.getValue(LINK_TITLE)
if (title.isEmpty()) { if (title.isNullOrEmpty()) {
title = href title = href?:""
} }
state.addAlternateFeedUrl(title!!, href) if (!href.isNullOrEmpty()) state.addAlternateFeedUrl(title, href)
} }
} else if (LINK_REL_ARCHIVES == rel) { } else if (LINK_REL_ARCHIVES == rel) {
val type = attributes.getValue(LINK_TYPE) val type: String? = attributes.getValue(LINK_TYPE)
if (LINK_TYPE_ATOM == type || LINK_TYPE_RSS == type) { if (LINK_TYPE_ATOM == type || LINK_TYPE_RSS == type) {
var title = attributes.getValue(LINK_TITLE) var title: String? = attributes.getValue(LINK_TITLE)
if (title.isEmpty()) { if (title.isNullOrEmpty()) {
title = href title = href?:""
} }
state.addAlternateFeedUrl(title!!, href) if (!href.isNullOrEmpty()) state.addAlternateFeedUrl(title, href)
} else if (LINK_TYPE_HTML == type || LINK_TYPE_XHTML == type) { } else if (LINK_TYPE_HTML == type || LINK_TYPE_XHTML == type) {
//A Link such as to a directory such as iTunes //A Link such as to a directory such as iTunes
} }
@ -205,7 +205,7 @@ class Atom : Namespace() {
*/ */
private const val isText = ("$TITLE|$CONTENT|$SUBTITLE|$SUMMARY") private const val isText = ("$TITLE|$CONTENT|$SUBTITLE|$SUMMARY")
private const val isFeed = FEED + "|" + ac.mdiq.podcini.feed.parser.namespace.Rss20.CHANNEL private const val isFeed = FEED + "|" + Rss20.CHANNEL
private const val isFeedItem = ENTRY + "|" + ac.mdiq.podcini.feed.parser.namespace.Rss20.ITEM private const val isFeedItem = ENTRY + "|" + Rss20.ITEM
} }
} }

View File

@ -10,14 +10,14 @@ class Itunes : Namespace() {
override fun handleElementStart(localName: String, state: ac.mdiq.podcini.feed.parser.HandlerState, override fun handleElementStart(localName: String, state: ac.mdiq.podcini.feed.parser.HandlerState,
attributes: Attributes): SyndElement { attributes: Attributes): SyndElement {
if (IMAGE == localName) { if (IMAGE == localName) {
val url = attributes.getValue(IMAGE_HREF) val url: String? = attributes.getValue(IMAGE_HREF)
if (state.currentItem != null) { if (state.currentItem != null) {
state.currentItem!!.imageUrl = url state.currentItem!!.imageUrl = url
} else { } else {
// this is the feed image // this is the feed image
// prefer to all other images // prefer to all other images
if (url.isNotEmpty()) { if (!url.isNullOrEmpty()) {
state.feed.imageUrl = url state.feed.imageUrl = url
} }
} }

View File

@ -15,9 +15,9 @@ import java.util.concurrent.TimeUnit
class Media : Namespace() { class Media : Namespace() {
override fun handleElementStart(localName: String, state: HandlerState, attributes: Attributes): SyndElement { override fun handleElementStart(localName: String, state: HandlerState, attributes: Attributes): SyndElement {
if (CONTENT == localName) { if (CONTENT == localName) {
val url = attributes.getValue(DOWNLOAD_URL) val url: String? = attributes.getValue(DOWNLOAD_URL)
val defaultStr = attributes.getValue(DEFAULT) val defaultStr: String? = attributes.getValue(DEFAULT)
val medium = attributes.getValue(MEDIUM) val medium: String? = attributes.getValue(MEDIUM)
var validTypeMedia = false var validTypeMedia = false
var validTypeImage = false var validTypeImage = false
val isDefault = "true" == defaultStr val isDefault = "true" == defaultStr
@ -44,7 +44,7 @@ class Media : Namespace() {
if (state.currentItem != null && (state.currentItem!!.media == null || isDefault) && url != null && validTypeMedia) { if (state.currentItem != null && (state.currentItem!!.media == null || isDefault) && url != null && validTypeMedia) {
var size: Long = 0 var size: Long = 0
val sizeStr = attributes.getValue(SIZE) val sizeStr: String? = attributes.getValue(SIZE)
if (!sizeStr.isNullOrEmpty()) { if (!sizeStr.isNullOrEmpty()) {
try { try {
size = sizeStr.toLong() size = sizeStr.toLong()
@ -53,7 +53,7 @@ class Media : Namespace() {
} }
} }
var durationMs = 0 var durationMs = 0
val durationStr = attributes.getValue(DURATION) val durationStr: String? = attributes.getValue(DURATION)
if (!durationStr.isNullOrEmpty()) { if (!durationStr.isNullOrEmpty()) {
try { try {
val duration = durationStr.toLong() val duration = durationStr.toLong()
@ -71,7 +71,7 @@ class Media : Namespace() {
state.currentItem!!.imageUrl = url state.currentItem!!.imageUrl = url
} }
} else if (IMAGE == localName) { } else if (IMAGE == localName) {
val url = attributes.getValue(IMAGE_URL) val url: String? = attributes.getValue(IMAGE_URL)
if (url != null) { if (url != null) {
if (state.currentItem != null) { if (state.currentItem != null) {
state.currentItem!!.imageUrl = url state.currentItem!!.imageUrl = url
@ -82,7 +82,7 @@ class Media : Namespace() {
} }
} }
} else if (DESCRIPTION == localName) { } else if (DESCRIPTION == localName) {
val type = attributes.getValue(DESCRIPTION_TYPE) val type: String? = attributes.getValue(DESCRIPTION_TYPE)
return AtomText(localName, this, type) return AtomText(localName, this, type)
} }
return SyndElement(localName, this) return SyndElement(localName, this)

View File

@ -9,13 +9,13 @@ class PodcastIndex : Namespace() {
override fun handleElementStart(localName: String, state: HandlerState, override fun handleElementStart(localName: String, state: HandlerState,
attributes: Attributes): SyndElement { attributes: Attributes): SyndElement {
if (FUNDING == localName) { if (FUNDING == localName) {
val href = attributes.getValue(URL) val href: String? = attributes.getValue(URL)
val funding = FeedFunding(href, "") val funding = FeedFunding(href, "")
state.currentFunding = funding state.currentFunding = funding
state.feed.addPayment(state.currentFunding!!) state.feed.addPayment(state.currentFunding!!)
} else if (CHAPTERS == localName) { } else if (CHAPTERS == localName) {
val href = attributes.getValue(URL) val href: String? = attributes.getValue(URL)
if (href.isNotEmpty()) { if (!href.isNullOrEmpty()) {
state.currentItem!!.podcastIndexChapterUrl = href state.currentItem!!.podcastIndexChapterUrl = href
} }
} }

View File

@ -22,14 +22,14 @@ class Rss20 : Namespace() {
state.items.add(state.currentItem!!) state.items.add(state.currentItem!!)
state.currentItem!!.feed = state.feed state.currentItem!!.feed = state.feed
} else if (ENCLOSURE == localName && ITEM == state.tagstack.peek()?.name) { } else if (ENCLOSURE == localName && ITEM == state.tagstack.peek()?.name) {
val url = attributes.getValue(ENC_URL) val url: String? = attributes.getValue(ENC_URL)
val mimeType = getMimeType(attributes.getValue(ENC_TYPE), url) val mimeType: String? = getMimeType(attributes.getValue(ENC_TYPE), url)
val validUrl = url.isNotEmpty() val validUrl = !url.isNullOrBlank()
if (state.currentItem?.media == null && isMediaFile(mimeType) && validUrl) { if (state.currentItem?.media == null && isMediaFile(mimeType) && validUrl) {
var size: Long = 0 var size: Long = 0
try { try {
size = attributes.getValue(ENC_LEN).toLong() size = attributes.getValue(ENC_LEN)?.toLong() ?: 0
if (size < 16384) { if (size < 16384) {
// less than 16kb is suspicious, check manually // less than 16kb is suspicious, check manually
size = 0 size = 0

View File

@ -12,13 +12,13 @@ class SimpleChapters : Namespace() {
if (currentItem != null) { if (currentItem != null) {
if (localName == CHAPTERS) { if (localName == CHAPTERS) {
currentItem.chapters = mutableListOf() currentItem.chapters = mutableListOf()
} else if (localName == CHAPTER && attributes.getValue(START).isNotEmpty()) { } else if (localName == CHAPTER && !attributes.getValue(START).isNullOrEmpty()) {
// if the chapter's START is empty, we don't need to do anything // if the chapter's START is empty, we don't need to do anything
try { try {
val start = parseTimeString(attributes.getValue(START)) val start= parseTimeString(attributes.getValue(START))
val title = attributes.getValue(TITLE) val title: String? = attributes.getValue(TITLE)
val link = attributes.getValue(HREF) val link: String? = attributes.getValue(HREF)
val imageUrl = attributes.getValue(IMAGE) val imageUrl: String? = attributes.getValue(IMAGE)
val chapter = Chapter(start, title, link, imageUrl) val chapter = Chapter(start, title, link, imageUrl)
currentItem.chapters?.add(chapter) currentItem.chapters?.add(chapter)
} catch (e: NumberFormatException) { } catch (e: NumberFormatException) {

View File

@ -13,6 +13,10 @@ buildscript {
} }
} }
plugins {
id 'com.google.devtools.ksp' version '1.9.22-1.0.17' apply false
}
allprojects { allprojects {
repositories { repositories {
google() google()
@ -21,48 +25,6 @@ allprojects {
} }
} }
project.ext {
// AndroidX
annotationVersion = "1.7.1"
appcompatVersion = "1.6.1"
coreVersion = "1.12.0"
fragmentVersion = "1.6.2"
mediaVersion = "1.7.0"
paletteVersion = "1.0.0"
preferenceVersion = "1.2.1"
recyclerViewVersion = "1.3.2"
viewPager2Version = "1.1.0-beta02"
workManagerVersion = "2.9.0"
googleMaterialVersion = "1.11.0"
// Third-party
commonslangVersion = "3.14.0"
commonsioVersion = "2.15.1"
jsoupVersion = "1.17.2"
glideVersion = "4.16.0"
okhttpVersion = "4.12.0"
okioVersion = "3.7.0"
eventbusVersion = "3.3.1"
rxAndroidVersion = "2.1.1"
rxJavaVersion = "2.2.21"
iconifyVersion = "2.2.2"
annimonStreamVersion = "1.2.2"
media3Version = "1.2.1"
// Google Play build
wearableSupportVersion = "2.6.0"
//Tests
awaitilityVersion = "4.2.0"
junitVersion = "4.13.2"
junit5Version = "5.10.1"
robolectricVersion = "4.11.1"
espressoVersion = "3.5.0"
runnerVersion = "1.5.2"
rulesVersion = "1.5.0"
testCoreVersion = "1.5.0"
}
apply plugin: "checkstyle" apply plugin: "checkstyle"
checkstyle { checkstyle {
toolVersion '10.3.1' toolVersion '10.3.1'

View File

@ -62,4 +62,10 @@
* removed info button in FeedItemList header * removed info button in FeedItemList header
* added items count in FeedItemList header * added items count in FeedItemList header
* fixed bug in FeedItemList when filtered list has no items * fixed bug in FeedItemList when filtered list has no items
* buildConfig is set in build.gradle instead of gradle.properties * buildConfig is set in build.gradle instead of gradle.properties
## 4.2.4
* fixed the "getValue() can not be null" bug
* enabled ksp for Kotlin builds
* cleaned up build.gradle files

View File

@ -1,109 +0,0 @@
android {
defaultConfig {
minSdk 21
compileSdk 34
targetSdk 34
kotlinOptions {
jvmTarget = '17'
}
vectorDrawables.useSupportLibrary false
vectorDrawables.generatedDensities = []
testApplicationId "ac.mdiq.podcini.tests"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard.cfg"
resValue "string", "app_name", "Podcini"
}
debug {
resValue "string", "app_name", "Podcini Debug"
}
}
packagingOptions {
resources {
excludes += ["META-INF/LICENSE.txt",
"META-INF/NOTICE.txt",
"META-INF/CHANGES",
"META-INF/README.md"]
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
testOptions {
animationsDisabled = true
unitTests {
includeAndroidResources = true
}
}
lint {
disable "GradleDependency"
checkDependencies true
warningsAsErrors true
abortOnError true
checkGeneratedSources = true
}
buildFeatures {
viewBinding true
}
}
tasks.withType(Test).configureEach {
testLogging {
exceptionFormat "full"
events "skipped", "passed", "failed"
showStandardStreams true
displayGranularity 2
}
}
gradle.projectsEvaluated {
tasks.withType(JavaCompile).tap {
configureEach {
options.compilerArgs << "-Xlint"
}
}
}
apply plugin: 'com.github.spotbugs'
spotbugs {
effort = 'max'
reportLevel = 'medium'
excludeFilter = rootProject.file('config/spotbugs/exclude.xml')
ignoreFailures = true // Handled by printing task
}
gradle.taskGraph.beforeTask { task ->
if (task.name.toLowerCase().contains('spotbugs')) {
task.doLast {
def reportFile = task.project.file("build/reports/spotbugs/playDebug.xml")
if (!reportFile.exists()) return
def slurped = new groovy.xml.XmlSlurper().parse(reportFile)
def foundErrors = false
slurped['BugInstance'].each { bug ->
logger.error "[SpotBugs] ${bug['LongMessage']} [${bug.@'type'}]"
bug['SourceLine'].each { line ->
logger.error "[SpotBugs] ${line['Message']}"
foundErrors = true
}
}
if (foundErrors) {
throw new TaskExecutionException(task,
new Exception("SpotBugs violations were found. See output above for details."))
}
}
}
}

View File

@ -0,0 +1,6 @@
Version 4.2.4 brings several changes:
* fixed the "getValue() can not be null" bug
* enabled ksp for Kotlin builds
* cleaned up build.gradle files

View File

@ -1,11 +0,0 @@
android {
flavorDimensions += ["market"]
productFlavors {
free {
dimension "market"
}
play {
dimension "market"
}
}
}