From 0b75b4c54277ef836c381a0a8d996346727d1752 Mon Sep 17 00:00:00 2001 From: Xilin Jia <6257601+XilinJia@users.noreply.github.com> Date: Wed, 13 Mar 2024 21:39:17 +0000 Subject: [PATCH] fixed getValue null bug and gradle cleanups --- .gitignore | 1 + app/build.gradle | 227 +++++++++++++----- .../podcini/util/service/download/HTTPBin.kt | 2 +- .../podcini/feed/parser/namespace/Atom.kt | 34 +-- .../podcini/feed/parser/namespace/Itunes.kt | 4 +- .../podcini/feed/parser/namespace/Media.kt | 14 +- .../feed/parser/namespace/PodcastIndex.kt | 6 +- .../podcini/feed/parser/namespace/Rss20.kt | 8 +- .../feed/parser/namespace/SimpleChapters.kt | 10 +- build.gradle | 46 +--- changelog.md | 8 +- common.gradle | 109 --------- .../android/en-US/changelogs/3020107.txt | 6 + playFlavor.gradle | 11 - 14 files changed, 227 insertions(+), 259 deletions(-) delete mode 100644 common.gradle create mode 100644 fastlane/metadata/android/en-US/changelogs/3020107.txt delete mode 100644 playFlavor.gradle diff --git a/.gitignore b/.gitignore index 7fa9d66d..a50a2934 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ contributers.py # View hierarchy captures captures # other +.directory *.odg# proguard libs diff --git a/app/build.gradle b/app/build.gradle index ba1ed1c7..40d70aa3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,14 +2,13 @@ plugins { id('com.android.application') id 'kotlin-android' id 'kotlin-kapt' + id 'com.google.devtools.ksp' id('com.github.triplet.play') version '3.8.3' apply false } -apply from: "../common.gradle" -apply from: "../playFlavor.gradle" android { namespace "ac.mdiq.podcini" - lint { + lintOptions { disable 'ObsoleteLintCustomCheck', 'CheckResult', 'UnusedAttribute', 'BatteryLife', 'InflateParams', 'RestrictedApi', 'TrustAllX509TrustManager', 'ExportedReceiver', 'AllowBackup', 'VectorDrawableCompat', 'StaticFieldLeak', 'UseCompoundDrawables', 'NestedWeights', 'Overdraw', 'UselessParent', 'TextFields', @@ -22,11 +21,25 @@ android { buildConfig true } 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" -> 1020395 - versionCode 3020106 - versionName "4.2.3" + versionCode 3020107 + versionName "4.2.4" def commit = "" try { @@ -65,33 +78,129 @@ android { keyPassword project.getProperties().getOrDefault("releaseKeyPassword", "password") } } + buildTypes { debug { applicationIdSuffix ".debug" + resValue "string", "app_name", "Podcini Debug" resValue "string", "provider_authority", "ac.mdiq.podcini.debug.provider" } release { + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard.cfg" + resValue "string", "app_name", "Podcini" resValue "string", "provider_authority", "ac.mdiq.podcini.provider" minifyEnabled true shrinkResources true 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" 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 { 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 { - implementation "androidx.core:core-ktx:$coreVersion" + implementation "androidx.core:core-ktx:1.12.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 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.fragment:fragment-ktx:$fragmentVersion" + implementation "androidx.fragment:fragment-ktx:1.6.2" implementation 'androidx.gridlayout:gridlayout:1.0.0' - implementation "androidx.media:media:$mediaVersion" - implementation "androidx.media3:media3-exoplayer:$media3Version" - implementation "androidx.media3:media3-ui:$media3Version" - implementation "androidx.media3:media3-datasource-okhttp:$media3Version" - implementation "androidx.media3:media3-common:$media3Version" - implementation "androidx.palette:palette-ktx:$paletteVersion" - implementation "androidx.preference:preference-ktx:$preferenceVersion" - implementation "androidx.recyclerview:recyclerview:$recyclerViewVersion" - implementation "androidx.viewpager2:viewpager2:$viewPager2Version" - implementation "androidx.work:work-runtime:$workManagerVersion" + implementation "androidx.media:media:1.7.0" + implementation "androidx.media3:media3-exoplayer:1.2.1" + implementation "androidx.media3:media3-ui:1.2.1" + implementation "androidx.media3:media3-datasource-okhttp:1.2.1" + implementation "androidx.media3:media3-common:1.2.1" + implementation "androidx.palette:palette-ktx:1.0.0" + implementation "androidx.preference:preference-ktx:1.2.1" + implementation "androidx.recyclerview:recyclerview:1.3.2" + implementation "androidx.viewpager2:viewpager2:1.1.0-beta02" + implementation "androidx.work:work-runtime:2.9.0" implementation "androidx.core:core-splashscreen: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 "commons-io:commons-io:$commonsioVersion" - implementation "org.jsoup:jsoup:$jsoupVersion" + implementation "org.apache.commons:commons-lang3:3.14.0" + implementation "commons-io:commons-io:2.15.1" + implementation "org.jsoup:jsoup:1.17.2" - implementation "com.github.bumptech.glide:glide:$glideVersion" - implementation "com.github.bumptech.glide:okhttp3-integration:$glideVersion@aar" - kapt "com.github.bumptech.glide:compiler:$glideVersion" + implementation "com.github.bumptech.glide:glide:4.16.0" + implementation "com.github.bumptech.glide:okhttp3-integration:4.16.0@aar" + ksp "com.github.bumptech.glide:ksp:4.16.0" - implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" - implementation "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion" - 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.squareup.okhttp3:okhttp:4.12.0" + implementation "com.squareup.okhttp3:okhttp-urlconnection:4.12.0" - implementation "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion" - implementation "com.joanzapata.iconify:android-iconify-material:$iconifyVersion" - implementation 'com.leinardi.android:speed-dial:3.2.0' + implementation "com.squareup.okio:okio:3.7.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.skydoves:balloon:1.5.3' + implementation 'com.github.skydoves:balloon:1.6.4' implementation 'com.github.xabaras:RecyclerViewSwipeDecorator:1.3' - implementation "com.annimon:stream:$annimonStreamVersion" + implementation "com.annimon:stream:1.2.2" // Non-free dependencies: - playImplementation 'com.google.android.play:core-ktx:1.8.0' - compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion" + playImplementation 'com.google.android.play:core-ktx:1.8.1' + compileOnly "com.google.android.wearable:wearable:2.9.0" +// this one can not be updated? androidTestImplementation 'com.nanohttpd:nanohttpd:2.1.1' - androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" - androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion" - androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion" - androidTestImplementation "androidx.test:runner:$runnerVersion" - androidTestImplementation "androidx.test:rules:$rulesVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1" + androidTestImplementation "androidx.test.espresso:espresso-contrib:3.5.1" + androidTestImplementation "androidx.test.espresso:espresso-intents:3.5.1" + androidTestImplementation "androidx.test:runner:1.5.2" + androidTestImplementation "androidx.test:rules:1.5.0" 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' // Non-free dependencies: - testImplementation "androidx.test:core:$testCoreVersion" - testImplementation "org.awaitility:awaitility:$awaitilityVersion" - testImplementation "junit:junit:$junitVersion" + testImplementation "androidx.test:core:1.5.0" + testImplementation "org.awaitility:awaitility:4.2.0" + testImplementation "junit:junit:4.13.2" 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' - 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' playApi 'androidx.mediarouter:mediarouter:1.6.0' - playApi "com.google.android.support:wearable:$wearableSupportVersion" - playApi 'com.google.android.gms:play-services-cast-framework:21.2.0' + playApi "com.google.android.support:wearable:2.9.0" + playApi 'com.google.android.gms:play-services-cast-framework:21.4.0' } kapt { diff --git a/app/src/androidTest/java/ac/test/podcini/util/service/download/HTTPBin.kt b/app/src/androidTest/java/ac/test/podcini/util/service/download/HTTPBin.kt index 1df7e462..41f79be9 100644 --- a/app/src/androidTest/java/ac/test/podcini/util/service/download/HTTPBin.kt +++ b/app/src/androidTest/java/ac/test/podcini/util/service/download/HTTPBin.kt @@ -1,8 +1,8 @@ package de.test.podcini.util.service.download +import ac.mdiq.podcini.BuildConfig import android.util.Base64 import android.util.Log -import ac.mdiq.podcini.BuildConfig import fi.iki.elonen.NanoHTTPD import fi.iki.elonen.NanoHTTPD.Response.IStatus import org.apache.commons.io.IOUtils diff --git a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Atom.kt b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Atom.kt index 83c88ad3..549fd5c5 100644 --- a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Atom.kt +++ b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Atom.kt @@ -19,17 +19,17 @@ class Atom : Namespace() { state.items.add(state.currentItem!!) state.currentItem!!.feed = state.feed } else if (localName.matches(isText.toRegex())) { - val type = attributes.getValue(TEXT_TYPE) + val type: String? = attributes.getValue(TEXT_TYPE) return AtomText(localName, this, type) } else if (LINK == localName) { - val href = attributes.getValue(LINK_HREF) - val rel = attributes.getValue(LINK_REL) + val href: String? = attributes.getValue(LINK_HREF) + val rel: String? = attributes.getValue(LINK_REL) val parent = state.tagstack.peek() if (parent.name.matches(isFeedItem.toRegex())) { if (rel == null || LINK_REL_ALTERNATE == rel) { state.currentItem!!.link = href } else if (LINK_REL_ENCLOSURE == rel) { - val strSize = attributes.getValue(LINK_LENGTH) + val strSize: String? = attributes.getValue(LINK_LENGTH) var size: Long = 0 try { if (strSize != null) { @@ -38,7 +38,7 @@ class Atom : Namespace() { } catch (e: NumberFormatException) { 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 if (isMediaFile(mimeType) && currItem != null && !currItem.hasMedia()) { @@ -49,7 +49,7 @@ class Atom : Namespace() { } } else if (parent.name.matches(isFeed.toRegex())) { 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 * feed-object has no link yet b) type of link is @@ -60,20 +60,20 @@ class Atom : Namespace() { state.feed.link = href } else if (LINK_TYPE_ATOM == type || LINK_TYPE_RSS == type) { // treat as podlove alternate feed - var title = attributes.getValue(LINK_TITLE) - if (title.isEmpty()) { - title = href + var title: String? = attributes.getValue(LINK_TITLE) + if (title.isNullOrEmpty()) { + title = href?:"" } - state.addAlternateFeedUrl(title!!, href) + if (!href.isNullOrEmpty()) state.addAlternateFeedUrl(title, href) } } 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) { - var title = attributes.getValue(LINK_TITLE) - if (title.isEmpty()) { - title = href + var title: String? = attributes.getValue(LINK_TITLE) + if (title.isNullOrEmpty()) { + title = href?:"" } - state.addAlternateFeedUrl(title!!, href) + if (!href.isNullOrEmpty()) state.addAlternateFeedUrl(title, href) } else if (LINK_TYPE_HTML == type || LINK_TYPE_XHTML == type) { //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 isFeed = FEED + "|" + ac.mdiq.podcini.feed.parser.namespace.Rss20.CHANNEL - private const val isFeedItem = ENTRY + "|" + ac.mdiq.podcini.feed.parser.namespace.Rss20.ITEM + private const val isFeed = FEED + "|" + Rss20.CHANNEL + private const val isFeedItem = ENTRY + "|" + Rss20.ITEM } } diff --git a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Itunes.kt b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Itunes.kt index 11d1030c..09e589dc 100644 --- a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Itunes.kt +++ b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Itunes.kt @@ -10,14 +10,14 @@ class Itunes : Namespace() { override fun handleElementStart(localName: String, state: ac.mdiq.podcini.feed.parser.HandlerState, attributes: Attributes): SyndElement { if (IMAGE == localName) { - val url = attributes.getValue(IMAGE_HREF) + val url: String? = attributes.getValue(IMAGE_HREF) if (state.currentItem != null) { state.currentItem!!.imageUrl = url } else { // this is the feed image // prefer to all other images - if (url.isNotEmpty()) { + if (!url.isNullOrEmpty()) { state.feed.imageUrl = url } } diff --git a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Media.kt b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Media.kt index 6dcecad9..f2ff2066 100644 --- a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Media.kt +++ b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Media.kt @@ -15,9 +15,9 @@ import java.util.concurrent.TimeUnit class Media : Namespace() { override fun handleElementStart(localName: String, state: HandlerState, attributes: Attributes): SyndElement { if (CONTENT == localName) { - val url = attributes.getValue(DOWNLOAD_URL) - val defaultStr = attributes.getValue(DEFAULT) - val medium = attributes.getValue(MEDIUM) + val url: String? = attributes.getValue(DOWNLOAD_URL) + val defaultStr: String? = attributes.getValue(DEFAULT) + val medium: String? = attributes.getValue(MEDIUM) var validTypeMedia = false var validTypeImage = false val isDefault = "true" == defaultStr @@ -44,7 +44,7 @@ class Media : Namespace() { if (state.currentItem != null && (state.currentItem!!.media == null || isDefault) && url != null && validTypeMedia) { var size: Long = 0 - val sizeStr = attributes.getValue(SIZE) + val sizeStr: String? = attributes.getValue(SIZE) if (!sizeStr.isNullOrEmpty()) { try { size = sizeStr.toLong() @@ -53,7 +53,7 @@ class Media : Namespace() { } } var durationMs = 0 - val durationStr = attributes.getValue(DURATION) + val durationStr: String? = attributes.getValue(DURATION) if (!durationStr.isNullOrEmpty()) { try { val duration = durationStr.toLong() @@ -71,7 +71,7 @@ class Media : Namespace() { state.currentItem!!.imageUrl = url } } else if (IMAGE == localName) { - val url = attributes.getValue(IMAGE_URL) + val url: String? = attributes.getValue(IMAGE_URL) if (url != null) { if (state.currentItem != null) { state.currentItem!!.imageUrl = url @@ -82,7 +82,7 @@ class Media : Namespace() { } } } else if (DESCRIPTION == localName) { - val type = attributes.getValue(DESCRIPTION_TYPE) + val type: String? = attributes.getValue(DESCRIPTION_TYPE) return AtomText(localName, this, type) } return SyndElement(localName, this) diff --git a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/PodcastIndex.kt b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/PodcastIndex.kt index 6fad4414..c83aa1e1 100644 --- a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/PodcastIndex.kt +++ b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/PodcastIndex.kt @@ -9,13 +9,13 @@ class PodcastIndex : Namespace() { override fun handleElementStart(localName: String, state: HandlerState, attributes: Attributes): SyndElement { if (FUNDING == localName) { - val href = attributes.getValue(URL) + val href: String? = attributes.getValue(URL) val funding = FeedFunding(href, "") state.currentFunding = funding state.feed.addPayment(state.currentFunding!!) } else if (CHAPTERS == localName) { - val href = attributes.getValue(URL) - if (href.isNotEmpty()) { + val href: String? = attributes.getValue(URL) + if (!href.isNullOrEmpty()) { state.currentItem!!.podcastIndexChapterUrl = href } } diff --git a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Rss20.kt b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Rss20.kt index c18c8464..6a2b5076 100644 --- a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Rss20.kt +++ b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Rss20.kt @@ -22,14 +22,14 @@ class Rss20 : Namespace() { state.items.add(state.currentItem!!) state.currentItem!!.feed = state.feed } else if (ENCLOSURE == localName && ITEM == state.tagstack.peek()?.name) { - val url = attributes.getValue(ENC_URL) - val mimeType = getMimeType(attributes.getValue(ENC_TYPE), url) + val url: String? = attributes.getValue(ENC_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) { var size: Long = 0 try { - size = attributes.getValue(ENC_LEN).toLong() + size = attributes.getValue(ENC_LEN)?.toLong() ?: 0 if (size < 16384) { // less than 16kb is suspicious, check manually size = 0 diff --git a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/SimpleChapters.kt b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/SimpleChapters.kt index d2812c29..b44dee29 100644 --- a/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/SimpleChapters.kt +++ b/app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/SimpleChapters.kt @@ -12,13 +12,13 @@ class SimpleChapters : Namespace() { if (currentItem != null) { if (localName == CHAPTERS) { 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 try { - val start = parseTimeString(attributes.getValue(START)) - val title = attributes.getValue(TITLE) - val link = attributes.getValue(HREF) - val imageUrl = attributes.getValue(IMAGE) + val start= parseTimeString(attributes.getValue(START)) + val title: String? = attributes.getValue(TITLE) + val link: String? = attributes.getValue(HREF) + val imageUrl: String? = attributes.getValue(IMAGE) val chapter = Chapter(start, title, link, imageUrl) currentItem.chapters?.add(chapter) } catch (e: NumberFormatException) { diff --git a/build.gradle b/build.gradle index c1ce5a19..657f240a 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,10 @@ buildscript { } } +plugins { + id 'com.google.devtools.ksp' version '1.9.22-1.0.17' apply false +} + allprojects { repositories { 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" checkstyle { toolVersion '10.3.1' diff --git a/changelog.md b/changelog.md index 97cb3e5c..3b670b44 100644 --- a/changelog.md +++ b/changelog.md @@ -62,4 +62,10 @@ * removed info button in FeedItemList header * added items count in FeedItemList header * fixed bug in FeedItemList when filtered list has no items -* buildConfig is set in build.gradle instead of gradle.properties \ No newline at end of file +* 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 \ No newline at end of file diff --git a/common.gradle b/common.gradle deleted file mode 100644 index d6a86f9c..00000000 --- a/common.gradle +++ /dev/null @@ -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.")) - } - } - } -} diff --git a/fastlane/metadata/android/en-US/changelogs/3020107.txt b/fastlane/metadata/android/en-US/changelogs/3020107.txt new file mode 100644 index 00000000..99ab91d9 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/3020107.txt @@ -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 diff --git a/playFlavor.gradle b/playFlavor.gradle deleted file mode 100644 index 9f2a3d8d..00000000 --- a/playFlavor.gradle +++ /dev/null @@ -1,11 +0,0 @@ -android { - flavorDimensions += ["market"] - productFlavors { - free { - dimension "market" - } - play { - dimension "market" - } - } -}