Skip to content

Commit

Permalink
5.0.0 commit
Browse files Browse the repository at this point in the history
  • Loading branch information
XilinJia committed May 11, 2024
1 parent eeea4bf commit 5fcb4c3
Show file tree
Hide file tree
Showing 154 changed files with 2,619 additions and 2,193 deletions.
4 changes: 3 additions & 1 deletion Licenses_and_permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ Apache License 2.0

[org.jsoup](https://jsoup.org/license) The MIT License

[com.github.bumptech.glide](https://github.com/bumptech/glide/blob/master/LICENSE) Various
[io.coil-kt](https://github.com/coil-kt/coil/blob/main/LICENSE.txt) Apache License 2.0

[net.dankito.readability4j](https://github.com/dankito/Readability4J/blob/master/LICENSE) Apache License 2.0

[com.squareup.okhttp3](https://github.com/square/okhttp/blob/master/LICENSE.txt) Apache License 2.0

Expand Down
44 changes: 22 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,31 @@
# Podcini

<img width="100" src="https://raw.githubusercontent.com/xilinjia/podcini/main/images/icon 256x256.png" align="left" style="margin-right:15px"/>
Podcini is an open source podcast manager/player project.
Podcini is an open source podcast instrument, attuned to Puccini <img src="./images/Puccini.jpg" height="50" />, adorned with pasticcini <img src="./images/pasticcini.jpg" height="50" /> and aromatized with porcini <img src="./images/porcini.jpg" height="50" />.

This project is a fork of [AntennaPod](<https://github.com/AntennaPod/AntennaPod>) as of Feb 5 2024.
This project is based on a fork of [AntennaPod](<https://github.com/AntennaPod/AntennaPod>) as of Feb 5 2024.

Compared to AntennaPod this project:

1. Migrated the media player to `androidx.media3`,
2. Added `AudioOffloadMode` support, which is supposed to be kind to device battery,
3. Relies on the most recent dependencies,
4. Is __purely__ Kotlin based,
4. Targets Android 14,
5. Aims to improve efficiency and provide more user-friendly features
1. Migrated all media routines to `androidx.media3`,
2. Plays in `AudioOffloadMode`, kind to device battery,
3. Is purely `Kotlin` based and mono-modular,
4. Targets Android 14 with updated dependencies,
5. Outfits with Viewbinding and modern image library Coil,
6. Boasts new UI's including streamlined drawer, subscriptions view and player controller
7. Offers Readability for RSS contents and TTS integration,
8. Features `instant sync` across devices without a server.

The project aims to improve efficiency and provide more useful and user-friendly features.

~Even so, the database remains backward compatible, and AntennaPod's db can be easily imported.~ Since version 4.10.0 and/or AntennaPod 3.3.2, AntennaPod's DB can not be directly imported

[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/ac.mdiq.podcini/)

Or download the latest APK from the [Releases Section](https://github.com/XilinJia/Podcini/releases/latest).

## Version 4

Some drastic changes are made in the project since version 4.0:
- A whole new interface of the Subscriptions page showing only the feeds with tags as filters, no longer having tags as folders in the page,
- Home, Echo and Inbox pages are removed from the project,
- Subscriptions is now the default page,
- Feed list are no longer shown in the drawer,
- Access to statistics is in the drawer.
- `OnlineFeedView` activity is stripped down to only receive externally shared feeds,
- Viewbindings are enabled for most views,
- Project became mono-modular.

~Even so, the database remains backward compatible, and AntennaPod's db can be easily imported.~ Since version 4.10.0 and/or AntennaPod 3.3.2, AntennaPod's DB can not be directly imported

## Notable new features & enhancements

### Player
Expand Down Expand Up @@ -67,6 +59,7 @@ Some drastic changes are made in the project since version 4.0:

### Podcast/Episode list

* A whole new interface of the Subscriptions page showing only the feeds with tags as filters, no longer having tags as folders in the page,
* New and efficient ways of click and long-click operations on lists:
* click on title area opens the podcast/episode
* long-press on title area automatically enters in selection mode
Expand Down Expand Up @@ -94,8 +87,15 @@ Some drastic changes are made in the project since version 4.0:
* Online feed info display is handled in similar ways as any local feed, and offers options to subscribe or view episodes
* Online feed episodes can be freely played (streamed) without a subscription
* externally shared feed opens in the new online feed view fragment
* OnlineFeedView` activity is stripped down to only receive externally shared feeds
* Youtube channels are accepted from external share or paste of address in podcast search view, and can be subscribed as a normal podcast, though video play is handled externally

### Instant (or Wifi) sync

* Ability to sync between devices on the same wifi network without a server (experimental release, back up before use)
* It syncs the play states (position and played) of episodes that exist in both devices (ensure to refresh first) and that have been played (completed or not)
* So far, every sync is a full sync, no subscription feeds sync, and no media files sync

### Security

* Disabled `usesCleartextTraffic`, so that all content transmission is more private and secure
Expand Down
17 changes: 11 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ android {

buildFeatures {
viewBinding true
buildConfig true
}
}

Expand Down Expand Up @@ -158,8 +159,8 @@ android {
// Version code schema (not used):
// "1.2.3-beta4" -> 1020304
// "1.2.3" -> 1020395
versionCode 3020139
versionName "4.10.1"
versionCode 3020140
versionName "5.0.0"

def commit = ""
try {
Expand Down Expand Up @@ -197,10 +198,12 @@ android {
resValue "string", "provider_authority", "ac.mdiq.podcini.debug.provider"
}
release {
resValue "string", "app_name", "Podcini"
resValue "string", "provider_authority", "ac.mdiq.podcini.provider"
minifyEnabled true
shrinkResources true
signingConfig signingConfigs.releaseConfig
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard.cfg"
}
}
applicationVariants.configureEach { variant ->
Expand All @@ -219,6 +222,7 @@ android {
dependencies {
implementation "androidx.core:core-ktx:1.12.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'com.android.volley:volley:1.2.1'

constraints {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version") {
Expand Down Expand Up @@ -256,13 +260,14 @@ dependencies {
implementation 'commons-io:commons-io:2.16.1'
implementation "org.jsoup:jsoup:1.17.2"

implementation "com.github.bumptech.glide:glide:4.16.0"
implementation "com.github.bumptech.glide:okhttp3-integration:4.16.0@aar"
kapt "com.github.bumptech.glide:ksp:4.16.0"
implementation "io.coil-kt:coil:2.6.0"

// implementation "com.github.bumptech.glide:glide:4.16.0"
// implementation "com.github.bumptech.glide:okhttp3-integration:4.16.0@aar"
// kapt "com.github.bumptech.glide:ksp:4.16.0"

implementation "com.squareup.okhttp3:okhttp:4.12.0"
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.12.0"

implementation 'com.squareup.okio:okio:3.9.0'

implementation "org.greenrobot:eventbus:3.3.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import ac.mdiq.podcini.storage.DBReader.getQueue
import ac.mdiq.podcini.storage.DBReader.getQueueIDList
import ac.mdiq.podcini.storage.DBWriter.clearQueue
import ac.mdiq.podcini.playback.PlaybackController
import ac.mdiq.podcini.playback.base.MediaPlayerBase
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter.Companion.unfiltered
import ac.mdiq.podcini.storage.model.feed.SortOrder
import ac.mdiq.podcini.playback.base.PlayerStatus
Expand Down Expand Up @@ -87,7 +88,7 @@ class PlaybackTest {
setupPlaybackController()
playFromQueue(0)
Awaitility.await().atMost(5, TimeUnit.SECONDS)
.until { controller!!.status == PlayerStatus.INITIALIZED }
.until { MediaPlayerBase.status == PlayerStatus.INITIALIZED }
}

@Test
Expand Down Expand Up @@ -148,11 +149,11 @@ class PlaybackTest {
// let playback run a bit then pause
Awaitility.await()
.atMost(1000, TimeUnit.MILLISECONDS)
.until { PlayerStatus.PLAYING == controller!!.status }
.until { PlayerStatus.PLAYING == MediaPlayerBase.status }
pauseEpisode()
Awaitility.await()
.atMost(1000, TimeUnit.MILLISECONDS)
.until { PlayerStatus.PAUSED == controller!!.status }
.until { PlayerStatus.PAUSED == MediaPlayerBase.status }

Assert.assertThat("Ensure even with smart mark as play, after pause, the item remains in the queue.",
getQueue(), Matchers.hasItems(feedItem))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package de.test.podcini.service.playback
import ac.mdiq.podcini.storage.model.playback.MediaType
import ac.mdiq.podcini.storage.model.playback.Playable
import ac.mdiq.podcini.playback.base.MediaPlayerBase.PSMPCallback
import ac.mdiq.podcini.playback.base.MediaPlayerBase.PSMPInfo
import ac.mdiq.podcini.playback.base.MediaPlayerBase.MediaPlayerInfo

class CancelablePSMPCallback(private val originalCallback: PSMPCallback) : PSMPCallback {
private var isCancelled = false
Expand All @@ -12,7 +12,7 @@ class CancelablePSMPCallback(private val originalCallback: PSMPCallback) : PSMPC
isCancelled = true
}

override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
if (isCancelled) {
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package de.test.podcini.service.playback
import ac.mdiq.podcini.storage.model.playback.MediaType
import ac.mdiq.podcini.storage.model.playback.Playable
import ac.mdiq.podcini.playback.base.MediaPlayerBase.PSMPCallback
import ac.mdiq.podcini.playback.base.MediaPlayerBase.PSMPInfo
import ac.mdiq.podcini.playback.base.MediaPlayerBase.MediaPlayerInfo

open class DefaultPSMPCallback : PSMPCallback {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
}

override fun shouldStop() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import ac.mdiq.podcini.playback.service.LocalMediaPlayer
import ac.mdiq.podcini.storage.model.feed.*
import ac.mdiq.podcini.storage.model.playback.Playable
import ac.mdiq.podcini.playback.base.MediaPlayerBase
import ac.mdiq.podcini.playback.base.MediaPlayerBase.PSMPInfo
import ac.mdiq.podcini.playback.base.MediaPlayerBase.MediaPlayerInfo
import ac.mdiq.podcini.playback.base.PlayerStatus
import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.deleteDatabase
import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.getInstance
Expand Down Expand Up @@ -80,7 +80,7 @@ class MediaPlayerBaseTest {
Assert.assertEquals(0, httpServer!!.serveFile(dest).toLong())
}

private fun checkPSMPInfo(info: PSMPInfo?) {
private fun checkPSMPInfo(info: MediaPlayerInfo?) {
try {
when (info!!.playerStatus) {
PlayerStatus.PLAYING, PlayerStatus.PAUSED, PlayerStatus.PREPARED, PlayerStatus.PREPARING, PlayerStatus.INITIALIZED, PlayerStatus.INITIALIZING, PlayerStatus.SEEKING -> Assert.assertNotNull(
Expand Down Expand Up @@ -126,7 +126,7 @@ class MediaPlayerBaseTest {
val c = InstrumentationRegistry.getInstrumentation().targetContext
val countDownLatch = CountDownLatch(2)
val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
try {
checkPSMPInfo(newInfo)
check(newInfo!!.playerStatus != PlayerStatus.ERROR) { "MediaPlayer error" }
Expand Down Expand Up @@ -168,7 +168,7 @@ class MediaPlayerBaseTest {
val c = InstrumentationRegistry.getInstrumentation().targetContext
val countDownLatch = CountDownLatch(2)
val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
try {
checkPSMPInfo(newInfo)
check(newInfo!!.playerStatus != PlayerStatus.ERROR) { "MediaPlayer error" }
Expand Down Expand Up @@ -211,7 +211,7 @@ class MediaPlayerBaseTest {
val c = InstrumentationRegistry.getInstrumentation().targetContext
val countDownLatch = CountDownLatch(4)
val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
try {
checkPSMPInfo(newInfo)
check(newInfo!!.playerStatus != PlayerStatus.ERROR) { "MediaPlayer error" }
Expand Down Expand Up @@ -257,7 +257,7 @@ class MediaPlayerBaseTest {
val c = InstrumentationRegistry.getInstrumentation().targetContext
val countDownLatch = CountDownLatch(5)
val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
try {
checkPSMPInfo(newInfo)
check(newInfo!!.playerStatus != PlayerStatus.ERROR) { "MediaPlayer error" }
Expand Down Expand Up @@ -305,7 +305,7 @@ class MediaPlayerBaseTest {
val c = InstrumentationRegistry.getInstrumentation().targetContext
val countDownLatch = CountDownLatch(2)
val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
try {
checkPSMPInfo(newInfo)
check(newInfo!!.playerStatus != PlayerStatus.ERROR) { "MediaPlayer error" }
Expand Down Expand Up @@ -346,7 +346,7 @@ class MediaPlayerBaseTest {
val c = InstrumentationRegistry.getInstrumentation().targetContext
val countDownLatch = CountDownLatch(2)
val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
try {
checkPSMPInfo(newInfo)
check(newInfo!!.playerStatus != PlayerStatus.ERROR) { "MediaPlayer error" }
Expand Down Expand Up @@ -387,7 +387,7 @@ class MediaPlayerBaseTest {
val c = InstrumentationRegistry.getInstrumentation().targetContext
val countDownLatch = CountDownLatch(4)
val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
try {
checkPSMPInfo(newInfo)
check(newInfo!!.playerStatus != PlayerStatus.ERROR) { "MediaPlayer error" }
Expand Down Expand Up @@ -432,7 +432,7 @@ class MediaPlayerBaseTest {
val c = InstrumentationRegistry.getInstrumentation().targetContext
val countDownLatch = CountDownLatch(5)
val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
try {
checkPSMPInfo(newInfo)
check(newInfo!!.playerStatus != PlayerStatus.ERROR) { "MediaPlayer error" }
Expand Down Expand Up @@ -486,7 +486,7 @@ class MediaPlayerBaseTest {
val countDownLatch = CountDownLatch(latchCount)

val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
checkPSMPInfo(newInfo)
when {
newInfo!!.playerStatus == PlayerStatus.ERROR -> {
Expand Down Expand Up @@ -607,7 +607,7 @@ class MediaPlayerBaseTest {
val countDownLatch = CountDownLatch(latchCount)

val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
checkPSMPInfo(newInfo)
when {
newInfo!!.playerStatus == PlayerStatus.ERROR -> {
Expand Down Expand Up @@ -666,7 +666,7 @@ class MediaPlayerBaseTest {
val latchCount = 1
val countDownLatch = CountDownLatch(latchCount)
val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
checkPSMPInfo(newInfo)
if (newInfo!!.playerStatus == PlayerStatus.ERROR) {
if (assertionError == null) assertionError = UnexpectedStateChange(newInfo.playerStatus)
Expand Down Expand Up @@ -739,7 +739,7 @@ class MediaPlayerBaseTest {
val latchCount = 2
val countDownLatch = CountDownLatch(latchCount)
val callback = CancelablePSMPCallback(object : DefaultPSMPCallback() {
override fun statusChanged(newInfo: PSMPInfo?) {
override fun statusChanged(newInfo: MediaPlayerInfo?) {
checkPSMPInfo(newInfo)
if (newInfo!!.playerStatus == PlayerStatus.ERROR) {
if (assertionError == null) assertionError = UnexpectedStateChange(newInfo.playerStatus)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package de.test.podcini.util.service.download

import ac.mdiq.podcini.BuildConfig
import ac.mdiq.podcini.util.Logd
import android.util.Base64
import android.util.Log
import fi.iki.elonen.NanoHTTPD
Expand Down Expand Up @@ -62,7 +62,7 @@ class HTTPBin : NanoHTTPD(0) {
}

override fun serve(session: IHTTPSession): Response {
if (BuildConfig.DEBUG) Log.d(TAG, "Requested url: " + session.uri)
Logd(TAG, "Requested url: " + session.uri)

val segments = session.uri.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (segments.size < 3) {
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/java/ac/mdiq/podcini/feed/parser/FeedHandler.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package ac.mdiq.podcini.feed.parser

import ac.mdiq.podcini.storage.model.feed.Feed
import ac.mdiq.podcini.feed.parser.util.TypeGetter
import android.util.Log
import ac.mdiq.podcini.storage.model.feed.Feed
import ac.mdiq.podcini.util.Logd
import org.apache.commons.io.input.XmlStreamReader
import org.xml.sax.InputSource
import org.xml.sax.SAXException
Expand Down Expand Up @@ -30,7 +30,7 @@ class FeedHandler {

val inputStreamReader: Reader = XmlStreamReader(File(feed.file_url!!))
val inputSource = InputSource(inputStreamReader)
Log.d("FeedHandler", "starting saxParser.parse")
Logd("FeedHandler", "starting saxParser.parse")
saxParser.parse(inputSource, handler)
inputStreamReader.close()
}
Expand Down
10 changes: 4 additions & 6 deletions app/src/main/java/ac/mdiq/podcini/feed/parser/HandlerState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ import java.util.*
* Contains all relevant information to describe the current state of a
* SyndHandler.
*/
class HandlerState(
/**
* Feed that the Handler is currently processing.
*/
@JvmField var feed: Feed
) {
/**
* Feed that the Handler is currently processing.
*/
class HandlerState(@JvmField var feed: Feed) {
/**
* Contains links to related feeds, e.g. feeds with enclosures in other formats. The key of the map is the
* URL of the feed, the value is the title
Expand Down
Loading

0 comments on commit 5fcb4c3

Please sign in to comment.