5.2.0 commit
This commit is contained in:
parent
a8838161b4
commit
6d22cf55b9
|
@ -159,8 +159,8 @@ android {
|
|||
// Version code schema (not used):
|
||||
// "1.2.3-beta4" -> 1020304
|
||||
// "1.2.3" -> 1020395
|
||||
versionCode 3020142
|
||||
versionName "5.1.0"
|
||||
versionCode 3020143
|
||||
versionName "5.2.0"
|
||||
|
||||
def commit = ""
|
||||
try {
|
||||
|
@ -262,10 +262,6 @@ dependencies {
|
|||
|
||||
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'
|
||||
|
|
|
@ -9,6 +9,7 @@ import ac.mdiq.podcini.storage.model.download.DownloadError
|
|||
import ac.mdiq.podcini.storage.model.feed.FeedFile
|
||||
import ac.mdiq.podcini.net.download.serviceinterface.DownloadRequest
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.init
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import de.test.podcini.util.service.download.HTTPBin
|
||||
import org.junit.After
|
||||
import org.junit.Assert
|
||||
|
@ -54,7 +55,7 @@ class HttpDownloaderTest {
|
|||
val fileUrl = File(destDir, title).absolutePath
|
||||
val file = File(fileUrl)
|
||||
if (deleteExisting) {
|
||||
Log.d(TAG, "Deleting file: " + file.delete())
|
||||
Logd(TAG, "Deleting file: " + file.delete())
|
||||
}
|
||||
feedfile.setFile_url(fileUrl)
|
||||
return feedfile
|
||||
|
|
|
@ -2,6 +2,7 @@ package ac.mdiq.podcini.feed
|
|||
|
||||
import android.util.Log
|
||||
import ac.mdiq.podcini.storage.model.feed.Chapter
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import kotlin.math.abs
|
||||
|
||||
object ChapterMerger {
|
||||
|
@ -11,7 +12,7 @@ object ChapterMerger {
|
|||
* This method might modify the input data.
|
||||
*/
|
||||
fun merge(chapters1: List<Chapter>?, chapters2: List<Chapter>?): List<Chapter>? {
|
||||
Log.d(TAG, "Merging chapters")
|
||||
Logd(TAG, "Merging chapters")
|
||||
when {
|
||||
chapters1 == null -> return chapters2
|
||||
chapters2 == null -> return chapters1
|
||||
|
|
|
@ -25,6 +25,7 @@ import ac.mdiq.podcini.feed.parser.media.id3.ID3ReaderException
|
|||
import ac.mdiq.podcini.feed.parser.media.id3.Id3MetadataReader
|
||||
import ac.mdiq.podcini.feed.parser.media.vorbis.VorbisCommentMetadataReader
|
||||
import ac.mdiq.podcini.feed.parser.media.vorbis.VorbisCommentReaderException
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import org.apache.commons.io.input.CountingInputStream
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.IOException
|
||||
|
@ -186,8 +187,7 @@ object LocalFeedUpdater {
|
|||
item.setDescriptionIfLonger(reader.comment)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "Unable to parse ID3 of " + file.uri + ": " + e.message)
|
||||
|
||||
Logd(TAG, "Unable to parse ID3 of " + file.uri + ": " + e.message)
|
||||
try {
|
||||
context.contentResolver.openInputStream(file.uri).use { inputStream ->
|
||||
val reader = VorbisCommentMetadataReader(inputStream)
|
||||
|
@ -195,12 +195,12 @@ object LocalFeedUpdater {
|
|||
item.setDescriptionIfLonger(reader.description)
|
||||
}
|
||||
} catch (e2: IOException) {
|
||||
Log.d(TAG, "Unable to parse vorbis comments of " + file.uri + ": " + e2.message)
|
||||
Logd(TAG, "Unable to parse vorbis comments of " + file.uri + ": " + e2.message)
|
||||
} catch (e2: VorbisCommentReaderException) {
|
||||
Log.d(TAG, "Unable to parse vorbis comments of " + file.uri + ": " + e2.message)
|
||||
Logd(TAG, "Unable to parse vorbis comments of " + file.uri + ": " + e2.message)
|
||||
}
|
||||
} catch (e: ID3ReaderException) {
|
||||
Log.d(TAG, "Unable to parse ID3 of " + file.uri + ": " + e.message)
|
||||
Logd(TAG, "Unable to parse ID3 of " + file.uri + ": " + e.message)
|
||||
|
||||
try {
|
||||
context.contentResolver.openInputStream(file.uri).use { inputStream ->
|
||||
|
@ -209,9 +209,9 @@ object LocalFeedUpdater {
|
|||
item.setDescriptionIfLonger(reader.description)
|
||||
}
|
||||
} catch (e2: IOException) {
|
||||
Log.d(TAG, "Unable to parse vorbis comments of " + file.uri + ": " + e2.message)
|
||||
Logd(TAG, "Unable to parse vorbis comments of " + file.uri + ": " + e2.message)
|
||||
} catch (e2: VorbisCommentReaderException) {
|
||||
Log.d(TAG, "Unable to parse vorbis comments of " + file.uri + ": " + e2.message)
|
||||
Logd(TAG, "Unable to parse vorbis comments of " + file.uri + ": " + e2.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.util.Log
|
|||
import ac.mdiq.podcini.storage.model.feed.Chapter
|
||||
import ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage.Companion.makeUrl
|
||||
import ac.mdiq.podcini.feed.parser.media.id3.model.FrameHeader
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import org.apache.commons.io.input.CountingInputStream
|
||||
import java.io.IOException
|
||||
import java.net.URLDecoder
|
||||
|
@ -18,9 +19,9 @@ class ChapterReader(input: CountingInputStream?) : ID3Reader(input!!) {
|
|||
@Throws(IOException::class, ID3ReaderException::class)
|
||||
override fun readFrame(frameHeader: FrameHeader) {
|
||||
if (FRAME_ID_CHAPTER == frameHeader.id) {
|
||||
Log.d(TAG, "Handling frame: $frameHeader")
|
||||
Logd(TAG, "Handling frame: $frameHeader")
|
||||
val chapter = readChapter(frameHeader)
|
||||
Log.d(TAG, "Chapter done: $chapter")
|
||||
Logd(TAG, "Chapter done: $chapter")
|
||||
chapters.add(chapter)
|
||||
} else {
|
||||
super.readFrame(frameHeader)
|
||||
|
@ -48,12 +49,12 @@ class ChapterReader(input: CountingInputStream?) : ID3Reader(input!!) {
|
|||
|
||||
@Throws(IOException::class, ID3ReaderException::class)
|
||||
fun readChapterSubFrame(frameHeader: FrameHeader, chapter: Chapter) {
|
||||
Log.d(TAG, "Handling subframe: $frameHeader")
|
||||
Logd(TAG, "Handling subframe: $frameHeader")
|
||||
val frameStartPosition = position
|
||||
when (frameHeader.id) {
|
||||
FRAME_ID_TITLE -> {
|
||||
chapter.title = readEncodingAndString(frameHeader.size)
|
||||
Log.d(TAG, "Found title: " + chapter.title)
|
||||
Logd(TAG, "Found title: " + chapter.title)
|
||||
}
|
||||
FRAME_ID_LINK -> {
|
||||
readEncodingAndString(frameHeader.size) // skip description
|
||||
|
@ -61,7 +62,7 @@ class ChapterReader(input: CountingInputStream?) : ID3Reader(input!!) {
|
|||
try {
|
||||
val decodedLink = URLDecoder.decode(url, "ISO-8859-1")
|
||||
chapter.link = decodedLink
|
||||
Log.d(TAG, "Found link: " + chapter.link)
|
||||
Logd(TAG, "Found link: " + chapter.link)
|
||||
} catch (iae: IllegalArgumentException) {
|
||||
Log.w(TAG, "Bad URL found in ID3 data")
|
||||
}
|
||||
|
@ -71,10 +72,10 @@ class ChapterReader(input: CountingInputStream?) : ID3Reader(input!!) {
|
|||
val mime = readIsoStringNullTerminated(frameHeader.size)
|
||||
val type = readByte()
|
||||
val description = readEncodedString(encoding.toInt(), frameHeader.size)
|
||||
Log.d(TAG, "Found apic: $mime,$description")
|
||||
Logd(TAG, "Found apic: $mime,$description")
|
||||
if (MIME_IMAGE_URL == mime) {
|
||||
val link = readIsoStringNullTerminated(frameHeader.size)
|
||||
Log.d(TAG, "Link: $link")
|
||||
Logd(TAG, "Link: $link")
|
||||
if (chapter.imageUrl.isNullOrEmpty() || type.toInt() == IMAGE_TYPE_COVER) chapter.imageUrl = link
|
||||
} else {
|
||||
val alreadyConsumed = position - frameStartPosition
|
||||
|
@ -82,7 +83,7 @@ class ChapterReader(input: CountingInputStream?) : ID3Reader(input!!) {
|
|||
if (chapter.imageUrl.isNullOrEmpty() || type.toInt() == IMAGE_TYPE_COVER) chapter.imageUrl = makeUrl(position, rawImageDataLength)
|
||||
}
|
||||
}
|
||||
else -> Log.d(TAG, "Unknown chapter sub-frame.")
|
||||
else -> Logd(TAG, "Unknown chapter sub-frame.")
|
||||
}
|
||||
// Skip garbage to fill frame completely
|
||||
// This also asserts that we are not reading too many bytes from this frame.
|
||||
|
|
|
@ -3,6 +3,7 @@ package ac.mdiq.podcini.feed.parser.media.id3
|
|||
import android.util.Log
|
||||
import ac.mdiq.podcini.feed.parser.media.id3.model.FrameHeader
|
||||
import ac.mdiq.podcini.feed.parser.media.id3.model.TagHeader
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.apache.commons.io.input.CountingInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
@ -25,7 +26,7 @@ open class ID3Reader(private val inputStream: CountingInputStream) {
|
|||
while (position < tagContentStartPosition + tagHeader!!.size) {
|
||||
val frameHeader = readFrameHeader()
|
||||
if (frameHeader.id[0] < '0' || frameHeader.id[0] > 'z') {
|
||||
Log.d(TAG, "Stopping because of invalid frame: $frameHeader")
|
||||
Logd(TAG, "Stopping because of invalid frame: $frameHeader")
|
||||
return
|
||||
}
|
||||
readFrame(frameHeader)
|
||||
|
@ -34,7 +35,7 @@ open class ID3Reader(private val inputStream: CountingInputStream) {
|
|||
|
||||
@Throws(IOException::class, ID3ReaderException::class)
|
||||
protected open fun readFrame(frameHeader: FrameHeader) {
|
||||
Log.d(TAG, "Skipping frame: " + frameHeader.id + ", size: " + frameHeader.size)
|
||||
Logd(TAG, "Skipping frame: " + frameHeader.id + ", size: " + frameHeader.size)
|
||||
skipBytes(frameHeader.size)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ac.mdiq.podcini.feed.parser.media.vorbis
|
||||
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.util.Log
|
||||
import org.apache.commons.io.EndianUtils
|
||||
import org.apache.commons.io.IOUtils
|
||||
|
@ -17,12 +18,12 @@ abstract class VorbisCommentReader internal constructor(private val input: Input
|
|||
findOggPage()
|
||||
findCommentHeader()
|
||||
val commentHeader = readCommentHeader()
|
||||
Log.d(TAG, commentHeader.toString())
|
||||
Logd(TAG, commentHeader.toString())
|
||||
for (i in 0 until commentHeader.userCommentLength) {
|
||||
readUserComment()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "Vorbis parser: " + e.message)
|
||||
Logd(TAG, "Vorbis parser: " + e.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +55,7 @@ abstract class VorbisCommentReader internal constructor(private val input: Input
|
|||
}
|
||||
val key = readContentVectorKey(vectorLength)!!.lowercase()
|
||||
val shouldReadValue = handles(key)
|
||||
Log.d(TAG, "key=$key, length=$vectorLength, handles=$shouldReadValue")
|
||||
Logd(TAG, "key=$key, length=$vectorLength, handles=$shouldReadValue")
|
||||
if (shouldReadValue) {
|
||||
val value = readUtf8String(vectorLength - key.length - 1)
|
||||
onContentVectorValue(key, value)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ac.mdiq.podcini.feed.parser.util
|
||||
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.util.Log
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.text.ParsePosition
|
||||
|
@ -98,7 +99,7 @@ object DateUtils {
|
|||
// if date string starts with a weekday, try parsing date string without it
|
||||
if (date.matches("^\\w+, .*$".toRegex())) return parse(date.substring(date.indexOf(',') + 1))
|
||||
|
||||
Log.d(TAG, "Could not parse date string \"$input\" [$date]")
|
||||
Logd(TAG, "Could not parse date string \"$input\" [$date]")
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import ac.mdiq.podcini.storage.model.feed.FeedPreferences
|
|||
import ac.mdiq.podcini.storage.model.playback.MediaType
|
||||
import ac.mdiq.podcini.storage.model.playback.Playable
|
||||
import ac.mdiq.podcini.preferences.UserPreferences
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
|
||||
/**
|
||||
* Utility class to use the appropriate playback speed based on [PlaybackPreferences]
|
||||
|
@ -32,8 +33,8 @@ object PlaybackSpeedUtils {
|
|||
val feed = item.feed
|
||||
if (feed?.preferences != null) {
|
||||
playbackSpeed = feed.preferences!!.feedPlaybackSpeed
|
||||
Log.d(TAG, "using feed speed $playbackSpeed")
|
||||
} else Log.d(TAG, "Can not get feed specific playback speed: $feed")
|
||||
Logd(TAG, "using feed speed $playbackSpeed")
|
||||
} else Logd(TAG, "Can not get feed specific playback speed: $feed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ac.mdiq.podcini.net.common
|
||||
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
|
@ -29,23 +30,23 @@ object UrlChecker {
|
|||
val lowerCaseUrl = url.lowercase() // protocol names are case insensitive
|
||||
when {
|
||||
lowerCaseUrl.startsWith("feed://") -> {
|
||||
Log.d(TAG, "Replacing feed:// with http://")
|
||||
Logd(TAG, "Replacing feed:// with http://")
|
||||
return prepareUrl(url.substring("feed://".length))
|
||||
}
|
||||
lowerCaseUrl.startsWith("pcast://") -> {
|
||||
Log.d(TAG, "Removing pcast://")
|
||||
Logd(TAG, "Removing pcast://")
|
||||
return prepareUrl(url.substring("pcast://".length))
|
||||
}
|
||||
lowerCaseUrl.startsWith("pcast:") -> {
|
||||
Log.d(TAG, "Removing pcast:")
|
||||
Logd(TAG, "Removing pcast:")
|
||||
return prepareUrl(url.substring("pcast:".length))
|
||||
}
|
||||
lowerCaseUrl.startsWith("itpc") -> {
|
||||
Log.d(TAG, "Replacing itpc:// with http://")
|
||||
Logd(TAG, "Replacing itpc:// with http://")
|
||||
return prepareUrl(url.substring("itpc://".length))
|
||||
}
|
||||
lowerCaseUrl.startsWith(AP_SUBSCRIBE) -> {
|
||||
Log.d(TAG, "Removing podcini-subscribe://")
|
||||
Logd(TAG, "Removing podcini-subscribe://")
|
||||
return prepareUrl(url.substring(AP_SUBSCRIBE.length))
|
||||
}
|
||||
// lowerCaseUrl.contains(AP_SUBSCRIBE_DEEPLINK) -> {
|
||||
|
@ -58,7 +59,7 @@ object UrlChecker {
|
|||
// }
|
||||
// }
|
||||
!(lowerCaseUrl.startsWith("http://") || lowerCaseUrl.startsWith("https://")) -> {
|
||||
Log.d(TAG, "Adding http:// at the beginning of the URL")
|
||||
Logd(TAG, "Adding http:// at the beginning of the URL")
|
||||
return "http://$url"
|
||||
}
|
||||
else -> return url
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ac.mdiq.podcini.net.discovery
|
||||
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import io.reactivex.Single
|
||||
|
@ -27,7 +28,7 @@ class CombinedSearcher : PodcastSearcher {
|
|||
singleResults[i] = e
|
||||
latch.countDown()
|
||||
}, { throwable: Throwable? ->
|
||||
Log.d(TAG, Log.getStackTraceString(throwable))
|
||||
Logd(TAG, Log.getStackTraceString(throwable))
|
||||
latch.countDown()
|
||||
}
|
||||
))
|
||||
|
|
|
@ -12,8 +12,7 @@ class FyydPodcastSearcher : PodcastSearcher {
|
|||
|
||||
override fun search(query: String): Single<List<PodcastSearchResult?>?> {
|
||||
return Single.create { subscriber: SingleEmitter<List<PodcastSearchResult?>?> ->
|
||||
val response = client.searchPodcasts(
|
||||
query, 10)
|
||||
val response = client.searchPodcasts(query, 10)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.blockingGet()
|
||||
val searchResults = ArrayList<PodcastSearchResult?>()
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.content.Context
|
|||
import android.util.Log
|
||||
import ac.mdiq.podcini.net.download.service.PodciniHttpClient.getHttpClient
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
@ -36,7 +37,7 @@ class ItunesTopListLoader(private val context: Context) {
|
|||
@Throws(IOException::class)
|
||||
private fun getTopListFeed(client: OkHttpClient?, country: String): String {
|
||||
val url = "https://itunes.apple.com/%s/rss/toppodcasts/limit=$NUM_LOADED/explicit=true/json"
|
||||
Log.d(TAG, "Feed URL " + String.format(url, country))
|
||||
Logd(TAG, "Feed URL " + String.format(url, country))
|
||||
val httpReq: Request.Builder = Request.Builder()
|
||||
.cacheControl(CacheControl.Builder().maxStale(1, TimeUnit.DAYS).build())
|
||||
.url(String.format(url, country))
|
||||
|
|
|
@ -26,6 +26,14 @@ object PodcastSearcherRegistry {
|
|||
return Single.just(url)
|
||||
}
|
||||
|
||||
// fun lookupUrlCo(url: String): String {
|
||||
// for (searchProviderInfo in searchProviders) {
|
||||
// if (searchProviderInfo.searcher.javaClass != CombinedSearcher::class.java && searchProviderInfo.searcher.urlNeedsLookup(url))
|
||||
// return searchProviderInfo.searcher.lookupUrlCo(url)
|
||||
// }
|
||||
// return url
|
||||
// }
|
||||
|
||||
fun urlNeedsLookup(url: String): Boolean {
|
||||
for (searchProviderInfo in searchProviders) {
|
||||
if (searchProviderInfo.searcher.javaClass != CombinedSearcher::class.java && searchProviderInfo.searcher.urlNeedsLookup(url)) return true
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ac.mdiq.podcini.net.download
|
||||
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.ConnectivityManager.OnNetworkActiveListener
|
||||
|
@ -17,7 +18,7 @@ class ConnectionStateMonitor
|
|||
.build()
|
||||
|
||||
@UnstableApi override fun onNetworkActive() {
|
||||
Log.d(TAG, "ConnectionStateMonitor::onNetworkActive network connection changed")
|
||||
Logd(TAG, "ConnectionStateMonitor::onNetworkActive network connection changed")
|
||||
NetworkConnectionChangeHandler.networkChangedDetected()
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import ac.mdiq.podcini.util.NetworkUtils.networkAvailable
|
|||
import ac.mdiq.podcini.util.event.MessageEvent
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.preferences.UserPreferences
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
@ -68,7 +69,7 @@ object FeedUpdateManager {
|
|||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun runOnceOrAsk(context: Context, feed: Feed? = null) {
|
||||
Log.d(TAG, "Run auto update immediately in background.")
|
||||
Logd(TAG, "Run auto update immediately in background.")
|
||||
when {
|
||||
feed != null && feed.isLocalFeed -> runOnce(context, feed)
|
||||
!networkAvailable() -> EventBus.getDefault().post(MessageEvent(context.getString(R.string.download_error_no_connection)))
|
||||
|
|
|
@ -7,6 +7,7 @@ import ac.mdiq.podcini.storage.DBTasks
|
|||
import ac.mdiq.podcini.util.NetworkUtils.isAutoDownloadAllowed
|
||||
import ac.mdiq.podcini.util.NetworkUtils.isNetworkRestricted
|
||||
import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
|
||||
@UnstableApi
|
||||
object NetworkConnectionChangeHandler {
|
||||
|
@ -21,7 +22,7 @@ object NetworkConnectionChangeHandler {
|
|||
@JvmStatic
|
||||
fun networkChangedDetected() {
|
||||
if (isAutoDownloadAllowed) {
|
||||
Log.d(TAG, "auto-dl network available, starting auto-download")
|
||||
Logd(TAG, "auto-dl network available, starting auto-download")
|
||||
DBTasks.autodownloadUndownloadedItems(context)
|
||||
} else { // if new network is Wi-Fi, finish ongoing downloads,
|
||||
// otherwise cancel all downloads
|
||||
|
|
|
@ -6,6 +6,7 @@ import ac.mdiq.podcini.net.download.service.HttpCredentialEncoder.encode
|
|||
import ac.mdiq.podcini.storage.DBReader
|
||||
import ac.mdiq.podcini.util.URIUtil
|
||||
import ac.mdiq.podcini.net.download.serviceinterface.DownloadRequest
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Interceptor.Chain
|
||||
import okhttp3.Request
|
||||
|
@ -47,24 +48,24 @@ class BasicAuthorizationInterceptor : Interceptor {
|
|||
} else userInfo = DBReader.getImageAuthentication(request.url.toString())
|
||||
|
||||
if (userInfo.isEmpty()) {
|
||||
Log.d(TAG, "no credentials for '" + request.url + "'")
|
||||
Logd(TAG, "no credentials for '" + request.url + "'")
|
||||
return response
|
||||
}
|
||||
|
||||
if (!userInfo.contains(":")) {
|
||||
Log.d(TAG, "Invalid credentials for '" + request.url + "'")
|
||||
Logd(TAG, "Invalid credentials for '" + request.url + "'")
|
||||
return response
|
||||
}
|
||||
val username = userInfo.substring(0, userInfo.indexOf(':'))
|
||||
val password = userInfo.substring(userInfo.indexOf(':') + 1)
|
||||
|
||||
Log.d(TAG, "Authorization failed, re-trying with ISO-8859-1 encoded credentials")
|
||||
Logd(TAG, "Authorization failed, re-trying with ISO-8859-1 encoded credentials")
|
||||
newRequest.header(HEADER_AUTHORIZATION, encode(username, password, "ISO-8859-1"))
|
||||
response = chain.proceed(newRequest.build())
|
||||
|
||||
if (response.code != HttpURLConnection.HTTP_UNAUTHORIZED) return response
|
||||
|
||||
Log.d(TAG, "Authorization failed, re-trying with UTF-8 encoded credentials")
|
||||
Logd(TAG, "Authorization failed, re-trying with UTF-8 encoded credentials")
|
||||
newRequest.header(HEADER_AUTHORIZATION, encode(username, password, "UTF-8"))
|
||||
return chain.proceed(newRequest.build())
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import ac.mdiq.podcini.ui.utils.NotificationUtils
|
|||
import ac.mdiq.podcini.storage.model.download.DownloadError
|
||||
import ac.mdiq.podcini.storage.model.download.DownloadResult
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.os.Build
|
||||
import java.util.*
|
||||
|
||||
|
@ -54,7 +55,7 @@ class FeedUpdateWorker(context: Context, params: WorkerParameters) : Worker(cont
|
|||
toUpdate.shuffle() // If the worker gets cancelled early, every feed has a chance to be updated
|
||||
} else {
|
||||
val feed = DBReader.getFeed(feedId) ?: return Result.success()
|
||||
Log.d(TAG, "doWork feed.download_url: ${feed.download_url}")
|
||||
Logd(TAG, "doWork feed.download_url: ${feed.download_url}")
|
||||
if (!feed.isLocalFeed) allAreLocal = false
|
||||
toUpdate = ArrayList()
|
||||
toUpdate.add(feed) // Needs to be updatable, so no singletonList
|
||||
|
@ -63,7 +64,7 @@ class FeedUpdateWorker(context: Context, params: WorkerParameters) : Worker(cont
|
|||
|
||||
if (!inputData.getBoolean(FeedUpdateManager.EXTRA_EVEN_ON_MOBILE, false) && !allAreLocal) {
|
||||
if (!NetworkUtils.networkAvailable() || !NetworkUtils.isFeedRefreshAllowed) {
|
||||
Log.d(TAG, "Blocking automatic update")
|
||||
Logd(TAG, "Blocking automatic update")
|
||||
return Result.retry()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package ac.mdiq.podcini.net.download.service
|
|||
import android.util.Log
|
||||
import ac.mdiq.podcini.storage.model.download.ProxyConfig
|
||||
import ac.mdiq.podcini.net.ssl.SslClientSetup
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import okhttp3.*
|
||||
import okhttp3.Credentials.basic
|
||||
import okhttp3.OkHttpClient.Builder
|
||||
|
@ -50,7 +51,7 @@ object PodciniHttpClient {
|
|||
*/
|
||||
@JvmStatic
|
||||
fun newBuilder(): Builder {
|
||||
Log.d(TAG, "Creating new instance of HTTP client")
|
||||
Logd(TAG, "Creating new instance of HTTP client")
|
||||
|
||||
System.setProperty("http.maxConnections", MAX_CONNECTIONS.toString())
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import ac.mdiq.podcini.storage.model.feed.FeedPreferences
|
|||
import ac.mdiq.podcini.storage.model.feed.VolumeAdaptionSetting
|
||||
import ac.mdiq.podcini.net.download.serviceinterface.DownloadRequest
|
||||
import ac.mdiq.podcini.feed.parser.UnsupportedFeedtypeException
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import org.xml.sax.SAXException
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
@ -44,7 +45,7 @@ class FeedParserTask(private val request: DownloadRequest) : Callable<FeedHandle
|
|||
var result: FeedHandlerResult? = null
|
||||
try {
|
||||
result = feedHandler.parseFeed(feed)
|
||||
Log.d(TAG, feed.title + " parsed")
|
||||
Logd(TAG, feed.title + " parsed")
|
||||
checkFeedData(feed)
|
||||
// TODO: what the shit is this??
|
||||
if (feed.imageUrl.isNullOrEmpty()) feed.imageUrl = Feed.PREFIX_GENERATIVE_COVER + feed.download_url
|
||||
|
@ -78,7 +79,7 @@ class FeedParserTask(private val request: DownloadRequest) : Callable<FeedHandle
|
|||
val feedFile = File(request.destination?:"junk")
|
||||
if (feedFile.exists()) {
|
||||
val deleted = feedFile.delete()
|
||||
Log.d(TAG, "Deletion of file '" + feedFile.absolutePath + "' " + (if (deleted) "successful" else "FAILED"))
|
||||
Logd(TAG, "Deletion of file '" + feedFile.absolutePath + "' " + (if (deleted) "successful" else "FAILED"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import ac.mdiq.podcini.storage.DBWriter.removeQueueItem
|
|||
import ac.mdiq.podcini.storage.model.feed.*
|
||||
import ac.mdiq.podcini.ui.utils.NotificationUtils
|
||||
import ac.mdiq.podcini.util.FeedItemUtil.hasAlmostEnded
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.LongList
|
||||
import ac.mdiq.podcini.util.event.FeedUpdateRunningEvent
|
||||
import ac.mdiq.podcini.util.event.MessageEvent
|
||||
|
@ -52,7 +53,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
|||
protected val synchronizationQueueStorage = SynchronizationQueueStorage(context)
|
||||
|
||||
@UnstableApi override fun doWork(): Result {
|
||||
Log.d(TAG, "doWork() called")
|
||||
Logd(TAG, "doWork() called")
|
||||
val activeSyncProvider = getActiveSyncProvider() ?: return Result.failure()
|
||||
|
||||
SynchronizationSettings.updateLastSynchronizationAttempt()
|
||||
|
@ -94,7 +95,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
|||
|
||||
@UnstableApi @Throws(SyncServiceException::class)
|
||||
private fun syncSubscriptions(syncServiceImpl: ISyncService) {
|
||||
Log.d(TAG, "syncSubscriptions called")
|
||||
Logd(TAG, "syncSubscriptions called")
|
||||
val lastSync = SynchronizationSettings.lastSubscriptionSynchronizationTimestamp
|
||||
EventBus.getDefault().postSticky(SyncServiceEvent(R.string.sync_status_subscriptions))
|
||||
val localSubscriptions: List<String> = getFeedListDownloadUrls()
|
||||
|
@ -104,11 +105,11 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
|||
val queuedRemovedFeeds: MutableList<String> = synchronizationQueueStorage.queuedRemovedFeeds
|
||||
var queuedAddedFeeds: List<String> = synchronizationQueueStorage.queuedAddedFeeds
|
||||
|
||||
Log.d(TAG, "Downloaded subscription changes: $subscriptionChanges")
|
||||
Logd(TAG, "Downloaded subscription changes: $subscriptionChanges")
|
||||
if (subscriptionChanges != null) {
|
||||
for (downloadUrl in subscriptionChanges.added) {
|
||||
if (!downloadUrl.startsWith("http")) { // Also matches https
|
||||
Log.d(TAG, "Skipping url: $downloadUrl")
|
||||
Logd(TAG, "Skipping url: $downloadUrl")
|
||||
continue
|
||||
}
|
||||
if (!containsUrl(localSubscriptions, downloadUrl) && !queuedRemovedFeeds.contains(downloadUrl)) {
|
||||
|
@ -125,7 +126,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
|||
}
|
||||
|
||||
if (lastSync == 0L) {
|
||||
Log.d(TAG, "First sync. Adding all local subscriptions.")
|
||||
Logd(TAG, "First sync. Adding all local subscriptions.")
|
||||
queuedAddedFeeds = localSubscriptions.toMutableList()
|
||||
queuedAddedFeeds.removeAll(subscriptionChanges.added)
|
||||
queuedRemovedFeeds.removeAll(subscriptionChanges.removed)
|
||||
|
@ -133,8 +134,8 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
|||
}
|
||||
|
||||
if (queuedAddedFeeds.isNotEmpty() || queuedRemovedFeeds.size > 0) {
|
||||
Log.d(TAG, "Added: " + StringUtils.join(queuedAddedFeeds, ", "))
|
||||
Log.d(TAG, "Removed: " + StringUtils.join(queuedRemovedFeeds, ", "))
|
||||
Logd(TAG, "Added: " + StringUtils.join(queuedAddedFeeds, ", "))
|
||||
Logd(TAG, "Removed: " + StringUtils.join(queuedRemovedFeeds, ", "))
|
||||
|
||||
LockingAsyncExecutor.lock.lock()
|
||||
try {
|
||||
|
@ -149,7 +150,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
|||
}
|
||||
|
||||
private fun waitForDownloadServiceCompleted() {
|
||||
Log.d(TAG, "waitForDownloadServiceCompleted called")
|
||||
Logd(TAG, "waitForDownloadServiceCompleted called")
|
||||
EventBus.getDefault().postSticky(SyncServiceEvent(R.string.sync_status_wait_for_downloads))
|
||||
try {
|
||||
while (true) {
|
||||
|
@ -179,7 +180,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
|||
if (lastSync == 0L) {
|
||||
EventBus.getDefault().postSticky(SyncServiceEvent(R.string.sync_status_upload_played))
|
||||
val readItems = getEpisodes(0, Int.MAX_VALUE, FeedItemFilter(FeedItemFilter.PLAYED), SortOrder.DATE_NEW_OLD)
|
||||
Log.d(TAG, "First sync. Upload state for all " + readItems.size + " played episodes")
|
||||
Logd(TAG, "First sync. Upload state for all " + readItems.size + " played episodes")
|
||||
for (item in readItems) {
|
||||
val media = item.media ?: continue
|
||||
val played = EpisodeAction.Builder(item, EpisodeAction.PLAY)
|
||||
|
@ -194,10 +195,10 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
|||
if (queuedEpisodeActions.isNotEmpty()) {
|
||||
LockingAsyncExecutor.lock.lock()
|
||||
try {
|
||||
Log.d(TAG, "Uploading ${queuedEpisodeActions.size} actions: ${StringUtils.join(queuedEpisodeActions, ", ")}")
|
||||
Logd(TAG, "Uploading ${queuedEpisodeActions.size} actions: ${StringUtils.join(queuedEpisodeActions, ", ")}")
|
||||
val postResponse = syncServiceImpl.uploadEpisodeActions(queuedEpisodeActions)
|
||||
newTimeStamp = postResponse?.timestamp?:0L
|
||||
Log.d(TAG, "Upload episode response: $postResponse")
|
||||
Logd(TAG, "Upload episode response: $postResponse")
|
||||
synchronizationQueueStorage.clearEpisodeActionQueue()
|
||||
} finally {
|
||||
LockingAsyncExecutor.lock.unlock()
|
||||
|
@ -208,7 +209,7 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
|||
|
||||
@UnstableApi @Throws(SyncServiceException::class)
|
||||
private fun syncEpisodeActions(syncServiceImpl: ISyncService) {
|
||||
Log.d(TAG, "syncEpisodeActions called")
|
||||
Logd(TAG, "syncEpisodeActions called")
|
||||
var (lastSync, newTimeStamp) = getEpisodeActions(syncServiceImpl)
|
||||
|
||||
// upload local actions
|
||||
|
@ -230,18 +231,18 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
|||
var idRemove = 0L
|
||||
feedItem.media!!.setPosition(action.position * 1000)
|
||||
if (hasAlmostEnded(feedItem.media!!)) {
|
||||
Log.d(TAG, "Marking as played: $action")
|
||||
Logd(TAG, "Marking as played: $action")
|
||||
feedItem.setPlayed(true)
|
||||
feedItem.media!!.setPosition(0)
|
||||
idRemove = feedItem.id
|
||||
} else Log.d(TAG, "Setting position: $action")
|
||||
} else Logd(TAG, "Setting position: $action")
|
||||
|
||||
return Pair(idRemove, feedItem)
|
||||
}
|
||||
|
||||
@UnstableApi @Synchronized
|
||||
fun processEpisodeActions(remoteActions: List<EpisodeAction>) {
|
||||
Log.d(TAG, "Processing " + remoteActions.size + " actions")
|
||||
Logd(TAG, "Processing " + remoteActions.size + " actions")
|
||||
if (remoteActions.isEmpty()) return
|
||||
|
||||
val playActionsToUpdate = getRemoteActionsOverridingLocalActions(remoteActions, synchronizationQueueStorage.queuedEpisodeActions)
|
||||
|
@ -264,11 +265,11 @@ open class SyncService(context: Context, params: WorkerParameters) : Worker(cont
|
|||
}
|
||||
|
||||
protected fun updateErrorNotification(exception: Exception) {
|
||||
Log.d(TAG, "Posting sync error notification")
|
||||
Logd(TAG, "Posting sync error notification")
|
||||
val description = ("${applicationContext.getString(R.string.gpodnetsync_error_descr)}${exception.message}")
|
||||
|
||||
if (!gpodnetNotificationsEnabled()) {
|
||||
Log.d(TAG, "Skipping sync error notification because of user setting")
|
||||
Logd(TAG, "Skipping sync error notification because of user setting")
|
||||
return
|
||||
}
|
||||
if (EventBus.getDefault().hasSubscriberForEvent(MessageEvent::class.java)) {
|
||||
|
|
|
@ -10,6 +10,7 @@ import ac.mdiq.podcini.net.sync.gpoddernet.model.GpodnetEpisodeActionPostRespons
|
|||
import ac.mdiq.podcini.net.sync.gpoddernet.model.GpodnetPodcast
|
||||
import ac.mdiq.podcini.net.sync.gpoddernet.model.GpodnetUploadChangesResponse
|
||||
import ac.mdiq.podcini.net.sync.model.*
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.util.Log
|
||||
import okhttp3.*
|
||||
import okhttp3.Credentials.basic
|
||||
|
@ -278,7 +279,7 @@ class GpodnetService(private val httpClient: OkHttpClient, baseHosturl: String?,
|
|||
@Throws(SyncServiceException::class)
|
||||
private fun uploadEpisodeActionsPartial(episodeActions: List<EpisodeAction?>?, from: Int, to: Int): UploadChangesResponse {
|
||||
try {
|
||||
Log.d(TAG, "Uploading partial actions " + from + " to " + to + " of " + episodeActions!!.size)
|
||||
Logd(TAG, "Uploading partial actions " + from + " to " + to + " of " + episodeActions!!.size)
|
||||
val url = URI(baseScheme, null, baseHost, basePort,
|
||||
String.format("/api/2/episodes/%s.json", username), null, null).toURL()
|
||||
|
||||
|
@ -424,7 +425,7 @@ class GpodnetService(private val httpClient: OkHttpClient, baseHosturl: String?,
|
|||
} else {
|
||||
if (BuildConfig.DEBUG) {
|
||||
try {
|
||||
Log.d(TAG, response.body!!.string())
|
||||
Logd(TAG, response.body!!.string())
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import ac.mdiq.podcini.storage.model.feed.FeedItem
|
|||
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
||||
import ac.mdiq.podcini.storage.model.feed.SortOrder
|
||||
import ac.mdiq.podcini.util.FeedItemUtil.hasAlmostEnded
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.SyncServiceEvent
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
|
@ -38,7 +39,7 @@ import kotlin.math.min
|
|||
var loginFail = false
|
||||
|
||||
override fun doWork(): Result {
|
||||
Log.d(TAG, "doWork() called")
|
||||
Logd(TAG, "doWork() called")
|
||||
|
||||
SynchronizationSettings.updateLastSynchronizationAttempt()
|
||||
setCurrentlyActive(true)
|
||||
|
@ -107,7 +108,7 @@ import kotlin.math.min
|
|||
private var socket: Socket? = null
|
||||
|
||||
@OptIn(UnstableApi::class) override fun login() {
|
||||
Log.d(TAG, "serverIp: $hostIp serverPort: $hostPort $isGuest")
|
||||
Logd(TAG, "serverIp: $hostIp serverPort: $hostPort $isGuest")
|
||||
EventBus.getDefault().post(SyncServiceEvent(R.string.sync_status_in_progress, "2"))
|
||||
if (!isPortInUse(hostPort)) {
|
||||
if (isGuest) {
|
||||
|
@ -134,7 +135,7 @@ import kotlin.math.min
|
|||
try {
|
||||
socket = serverSocket!!.accept()
|
||||
while (true) {
|
||||
Log.d(TAG, "waiting for guest message")
|
||||
Logd(TAG, "waiting for guest message")
|
||||
try {
|
||||
receiveFromPeer()
|
||||
sendToPeer("Hello", "Hello, Client")
|
||||
|
@ -191,30 +192,24 @@ import kotlin.math.min
|
|||
val messageData = parts[1]
|
||||
// Process the message based on the type
|
||||
when (messageType) {
|
||||
"Hello" -> Log.d(TAG, "Received Hello message: $messageData")
|
||||
"Hello" -> Logd(TAG, "Received Hello message: $messageData")
|
||||
"EpisodeActions" -> {
|
||||
val remoteActions = mutableListOf<EpisodeAction>()
|
||||
val jsonArray = JSONArray(messageData)
|
||||
for (i in 0 until jsonArray.length()) {
|
||||
val jsonAction = jsonArray.getJSONObject(i)
|
||||
|
||||
// TODO: this conversion shouldn't be needed, check about the uploader
|
||||
// val timeStr = jsonAction.getString("timestamp")
|
||||
// val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US)
|
||||
// val date = format.parse(timeStr)
|
||||
// jsonAction.put("timestamp", date?.time?:0L)
|
||||
|
||||
Log.d(TAG, "Received EpisodeActions message: $i $jsonAction")
|
||||
Logd(TAG, "Received EpisodeActions message: $i $jsonAction")
|
||||
val action = readFromJsonObject(jsonAction)
|
||||
if (action != null) remoteActions.add(action)
|
||||
}
|
||||
processEpisodeActions(remoteActions)
|
||||
}
|
||||
"AllSent" -> {
|
||||
Log.d(TAG, "Received AllSent message: $messageData")
|
||||
Logd(TAG, "Received AllSent message: $messageData")
|
||||
return true
|
||||
}
|
||||
else -> Log.d(TAG, "Received unknown message: $messageData")
|
||||
else -> Logd(TAG, "Received unknown message: $messageData")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -223,19 +218,19 @@ import kotlin.math.min
|
|||
|
||||
@Throws(SyncServiceException::class)
|
||||
override fun getSubscriptionChanges(lastSync: Long): SubscriptionChanges? {
|
||||
Log.d(TAG, "getSubscriptionChanges does nothing")
|
||||
Logd(TAG, "getSubscriptionChanges does nothing")
|
||||
return null
|
||||
}
|
||||
|
||||
@Throws(SyncServiceException::class)
|
||||
override fun uploadSubscriptionChanges(added: List<String>, removed: List<String>): UploadChangesResponse? {
|
||||
Log.d(TAG, "uploadSubscriptionChanges does nothing")
|
||||
Logd(TAG, "uploadSubscriptionChanges does nothing")
|
||||
return null
|
||||
}
|
||||
|
||||
@Throws(SyncServiceException::class)
|
||||
override fun getEpisodeActionChanges(timestamp: Long): EpisodeActionChanges? {
|
||||
Log.d(TAG, "getEpisodeActionChanges does nothing")
|
||||
Logd(TAG, "getEpisodeActionChanges does nothing")
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -243,7 +238,7 @@ import kotlin.math.min
|
|||
var newTimeStamp = newTimeStamp_
|
||||
EventBus.getDefault().postSticky(SyncServiceEvent(R.string.sync_status_episodes_upload))
|
||||
val queuedEpisodeActions: MutableList<EpisodeAction> = synchronizationQueueStorage.queuedEpisodeActions
|
||||
Log.d(TAG, "pushEpisodeActions queuedEpisodeActions: ${queuedEpisodeActions.size}")
|
||||
Logd(TAG, "pushEpisodeActions queuedEpisodeActions: ${queuedEpisodeActions.size}")
|
||||
|
||||
if (lastSync == 0L) {
|
||||
EventBus.getDefault().postSticky(SyncServiceEvent(R.string.sync_status_upload_played))
|
||||
|
@ -253,7 +248,7 @@ import kotlin.math.min
|
|||
val comItems = mutableSetOf<FeedItem>()
|
||||
comItems.addAll(pausedItems)
|
||||
comItems.addAll(readItems)
|
||||
Log.d(TAG, "First sync. Upload state for all " + comItems.size + " played episodes")
|
||||
Logd(TAG, "First sync. Upload state for all " + comItems.size + " played episodes")
|
||||
for (item in comItems) {
|
||||
val media = item.media ?: continue
|
||||
val played = EpisodeAction.Builder(item, EpisodeAction.PLAY)
|
||||
|
@ -268,10 +263,10 @@ import kotlin.math.min
|
|||
if (queuedEpisodeActions.isNotEmpty()) {
|
||||
LockingAsyncExecutor.lock.lock()
|
||||
try {
|
||||
Log.d(TAG, "Uploading ${queuedEpisodeActions.size} actions: ${StringUtils.join(queuedEpisodeActions, ", ")}")
|
||||
Logd(TAG, "Uploading ${queuedEpisodeActions.size} actions: ${StringUtils.join(queuedEpisodeActions, ", ")}")
|
||||
val postResponse = uploadEpisodeActions(queuedEpisodeActions)
|
||||
newTimeStamp = postResponse.timestamp
|
||||
Log.d(TAG, "Upload episode response: $postResponse")
|
||||
Logd(TAG, "Upload episode response: $postResponse")
|
||||
synchronizationQueueStorage.clearEpisodeActionQueue()
|
||||
} finally {
|
||||
LockingAsyncExecutor.lock.unlock()
|
||||
|
@ -301,7 +296,7 @@ import kotlin.math.min
|
|||
val episodeAction = queuedEpisodeActions[i]
|
||||
val obj = episodeAction.writeToJsonObject()
|
||||
if (obj != null) {
|
||||
Log.d(TAG, "sending EpisodeAction: $obj")
|
||||
Logd(TAG, "sending EpisodeAction: $obj")
|
||||
list.put(obj)
|
||||
}
|
||||
}
|
||||
|
@ -325,18 +320,18 @@ import kotlin.math.min
|
|||
}
|
||||
feedItem.media = getFeedMedia(feedItem.media!!.id)
|
||||
var idRemove = 0L
|
||||
Log.d(TAG, "processEpisodeAction ${feedItem.media!!.getLastPlayedTime()} ${(action.timestamp?.time?:0L)} ${action.position} ${feedItem.title}")
|
||||
Logd(TAG, "processEpisodeAction ${feedItem.media!!.getLastPlayedTime()} ${(action.timestamp?.time?:0L)} ${action.position} ${feedItem.title}")
|
||||
if (feedItem.media!!.getLastPlayedTime() < (action.timestamp?.time?:0L)) {
|
||||
feedItem.media!!.setPosition(action.position * 1000)
|
||||
feedItem.media!!.setLastPlayedTime(action.timestamp!!.time)
|
||||
if (hasAlmostEnded(feedItem.media!!)) {
|
||||
Log.d(TAG, "Marking as played")
|
||||
Logd(TAG, "Marking as played")
|
||||
feedItem.setPlayed(true)
|
||||
feedItem.media!!.setPosition(0)
|
||||
idRemove = feedItem.id
|
||||
} else Log.d(TAG, "Setting position")
|
||||
} else Logd(TAG, "Setting position")
|
||||
persistFeedMediaPlaybackInfo(feedItem.media)
|
||||
} else Log.d(TAG, "local is newer, no change")
|
||||
} else Logd(TAG, "local is newer, no change")
|
||||
|
||||
return Pair(idRemove, feedItem)
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
|||
private var initialized = false
|
||||
private var eventsRegistered = false
|
||||
|
||||
private var loadedFeedMedia: Long = -1
|
||||
private var loadedFeedMediaId: Long = -1
|
||||
|
||||
val position: Int
|
||||
get() = playbackService?.currentPosition ?: getMedia()?.getPosition() ?: Playable.INVALID_TIME
|
||||
|
@ -114,7 +114,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
|||
// ignore
|
||||
}
|
||||
unbind()
|
||||
media = null
|
||||
// media = null
|
||||
released = true
|
||||
|
||||
if (eventsRegistered) {
|
||||
|
@ -235,8 +235,9 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
|||
}
|
||||
|
||||
private fun checkMediaInfoLoaded() {
|
||||
if (!mediaInfoLoaded || loadedFeedMedia != PlaybackPreferences.currentlyPlayingFeedMediaId) {
|
||||
loadedFeedMedia = PlaybackPreferences.currentlyPlayingFeedMediaId
|
||||
if (!mediaInfoLoaded || loadedFeedMediaId != PlaybackPreferences.currentlyPlayingFeedMediaId) {
|
||||
loadedFeedMediaId = PlaybackPreferences.currentlyPlayingFeedMediaId
|
||||
Logd(TAG, "checkMediaInfoLoaded: $loadedFeedMediaId")
|
||||
loadMediaInfo()
|
||||
}
|
||||
mediaInfoLoaded = true
|
||||
|
|
|
@ -48,12 +48,11 @@ import androidx.media3.extractor.DefaultExtractorsFactory
|
|||
import androidx.media3.extractor.mp3.Mp3Extractor
|
||||
import androidx.media3.ui.DefaultTrackNameProvider
|
||||
import androidx.media3.ui.TrackNameProvider
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.lang.Runnable
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
@ -83,8 +82,8 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
|
|||
private var isShutDown = false
|
||||
private var seekLatch: CountDownLatch? = null
|
||||
|
||||
private val bufferUpdateInterval = 5L
|
||||
private val bufferingUpdateDisposable: Disposable
|
||||
private val bufferUpdateInterval = 5000L
|
||||
// private val bufferingUpdateDisposable: Disposable
|
||||
private var mediaSource: MediaSource? = null
|
||||
private var playbackParameters: PlaybackParameters
|
||||
|
||||
|
@ -125,7 +124,7 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
|
|||
}
|
||||
|
||||
private fun release() {
|
||||
bufferingUpdateDisposable.dispose()
|
||||
// bufferingUpdateDisposable.dispose()
|
||||
|
||||
// exoplayerListener = null
|
||||
exoPlayer?.stop()
|
||||
|
@ -670,12 +669,21 @@ class LocalMediaPlayer(context: Context, callback: MediaPlayerCallback) : MediaP
|
|||
createStaticPlayer(context)
|
||||
}
|
||||
playbackParameters = exoPlayer!!.playbackParameters
|
||||
bufferingUpdateDisposable = Observable.interval(bufferUpdateInterval, TimeUnit.SECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
// bufferingUpdateDisposable = Observable.interval(bufferUpdateInterval, TimeUnit.SECONDS)
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe {
|
||||
// bufferingUpdateListener?.accept(exoPlayer!!.bufferedPercentage)
|
||||
// }
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
scope.launch {
|
||||
while (true) {
|
||||
delay(bufferUpdateInterval)
|
||||
withContext(Dispatchers.Main) {
|
||||
bufferingUpdateListener?.accept(exoPlayer!!.bufferedPercentage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun endPlayback(hasEnded: Boolean, wasSkipped: Boolean, shouldContinue: Boolean, toStoppedState: Boolean) {
|
||||
releaseWifiLockIfNecessary()
|
||||
|
|
|
@ -4,8 +4,8 @@ import ac.mdiq.podcini.R
|
|||
import ac.mdiq.podcini.net.sync.queue.SynchronizationQueueSink
|
||||
import ac.mdiq.podcini.playback.PlaybackServiceStarter
|
||||
import ac.mdiq.podcini.playback.base.MediaPlayerBase
|
||||
import ac.mdiq.podcini.playback.base.MediaPlayerBase.MediaPlayerInfo
|
||||
import ac.mdiq.podcini.playback.base.MediaPlayerBase.MediaPlayerCallback
|
||||
import ac.mdiq.podcini.playback.base.MediaPlayerBase.MediaPlayerInfo
|
||||
import ac.mdiq.podcini.playback.base.PlayerStatus
|
||||
import ac.mdiq.podcini.playback.cast.CastPsmp
|
||||
import ac.mdiq.podcini.playback.cast.CastStateListener
|
||||
|
@ -91,10 +91,10 @@ import androidx.media3.session.SessionResult
|
|||
import androidx.work.impl.utils.futures.SettableFuture
|
||||
import com.google.common.util.concurrent.Futures
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
@ -108,7 +108,8 @@ import kotlin.math.max
|
|||
@UnstableApi
|
||||
class PlaybackService : MediaSessionService() {
|
||||
private var mediaPlayer: MediaPlayerBase? = null
|
||||
private var positionEventTimer: Disposable? = null
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
private lateinit var customMediaNotificationProvider: CustomMediaNotificationProvider
|
||||
private val notificationCustomButtons = NotificationCustomButton.entries.map { command -> command.commandButton }
|
||||
|
@ -261,7 +262,6 @@ class PlaybackService : MediaSessionService() {
|
|||
currentMediaType = MediaType.UNKNOWN
|
||||
castStateListener.destroy()
|
||||
|
||||
// cancelPositionObserver()
|
||||
LocalMediaPlayer.cleanup()
|
||||
mediaSession?.run {
|
||||
player.release()
|
||||
|
@ -367,139 +367,6 @@ class PlaybackService : MediaSessionService() {
|
|||
return mediaSession
|
||||
}
|
||||
|
||||
// private fun loadQueueForMediaSession() {
|
||||
// Single.create { emitter: SingleEmitter<List<MediaSessionCompat.QueueItem>?> ->
|
||||
// val queueItems: MutableList<MediaSessionCompat.QueueItem> = ArrayList()
|
||||
// for (feedItem in DBReader.getQueue()) {
|
||||
// if (feedItem.media != null) {
|
||||
// val mediaDescription = feedItem.media!!.mediaItem.description
|
||||
// queueItems.add(MediaSessionCompat.QueueItem(mediaDescription, feedItem.id))
|
||||
// }
|
||||
// }
|
||||
// emitter.onSuccess(queueItems)
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({
|
||||
//// mediaSession?.setQueue(queueItems)
|
||||
// },
|
||||
// { obj: Throwable -> obj.printStackTrace() })
|
||||
// }
|
||||
|
||||
// private fun createBrowsableMediaItem(@StringRes title: Int, @DrawableRes icon: Int, numEpisodes: Int): MediaItem {
|
||||
// val uri = Uri.Builder()
|
||||
// .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||
// .authority(resources.getResourcePackageName(icon))
|
||||
// .appendPath(resources.getResourceTypeName(icon))
|
||||
// .appendPath(resources.getResourceEntryName(icon))
|
||||
// .build()
|
||||
//
|
||||
// val description = MediaDescription.Builder()
|
||||
// .setIconUri(uri)
|
||||
// .setMediaId(resources.getString(title))
|
||||
// .setTitle(resources.getString(title))
|
||||
// .setSubtitle(resources.getQuantityString(R.plurals.num_episodes, numEpisodes, numEpisodes))
|
||||
// .build()
|
||||
// return MediaItem(description, MediaItem.FLAG_BROWSABLE)
|
||||
// }
|
||||
|
||||
// private fun createBrowsableMediaItemForFeed(feed: Feed): MediaItem {
|
||||
// val builder = MediaDescription.Builder()
|
||||
// .setMediaId("FeedId:" + feed.id)
|
||||
// .setTitle(feed.title)
|
||||
// .setDescription(feed.description)
|
||||
// .setSubtitle(feed.getCustomTitle())
|
||||
// if (feed.imageUrl != null) {
|
||||
// builder.setIconUri(Uri.parse(feed.imageUrl))
|
||||
// }
|
||||
// if (feed.link != null) {
|
||||
// builder.setMediaUri(Uri.parse(feed.link))
|
||||
// }
|
||||
// val description = builder.build()
|
||||
// return MediaItem(description, MediaItem.FLAG_BROWSABLE)
|
||||
// }
|
||||
|
||||
// override fun onLoadChildren(parentId: String, result: Result<List<MediaItem>>) {
|
||||
// Log.d(TAG, "OnLoadChildren: parentMediaId=$parentId")
|
||||
// result.detach()
|
||||
//
|
||||
// Completable.create { emitter: CompletableEmitter ->
|
||||
// result.sendResult(loadChildrenSynchronous(parentId))
|
||||
// emitter.onComplete()
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// {}, { e: Throwable ->
|
||||
// e.printStackTrace()
|
||||
// result.sendResult(null)
|
||||
// })
|
||||
// }
|
||||
|
||||
// private fun loadChildrenSynchronous(parentId: String): List<MediaBrowserCompat.MediaItem>? {
|
||||
// val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = ArrayList()
|
||||
// if (parentId == resources.getString(R.string.app_name)) {
|
||||
// val currentlyPlaying = currentPlayerStatus.toLong()
|
||||
// if (currentlyPlaying == PlaybackPreferences.PLAYER_STATUS_PLAYING.toLong()
|
||||
// || currentlyPlaying == PlaybackPreferences.PLAYER_STATUS_PAUSED.toLong()) {
|
||||
// mediaItems.add(createBrowsableMediaItem(R.string.current_playing_episode, R.drawable.ic_play_48dp, 1))
|
||||
// }
|
||||
// mediaItems.add(createBrowsableMediaItem(R.string.queue_label, R.drawable.ic_playlist_play_black,
|
||||
// DBReader.getTotalEpisodeCount(FeedItemFilter(FeedItemFilter.QUEUED))))
|
||||
// mediaItems.add(createBrowsableMediaItem(R.string.downloads_label, R.drawable.ic_download_black,
|
||||
// DBReader.getTotalEpisodeCount(FeedItemFilter(FeedItemFilter.DOWNLOADED))))
|
||||
// mediaItems.add(createBrowsableMediaItem(R.string.episodes_label, R.drawable.ic_feed_black,
|
||||
// DBReader.getTotalEpisodeCount(FeedItemFilter(FeedItemFilter.UNPLAYED))))
|
||||
// val feeds = DBReader.getFeedList()
|
||||
// for (feed in feeds) {
|
||||
// mediaItems.add(createBrowsableMediaItemForFeed(feed))
|
||||
// }
|
||||
// return mediaItems
|
||||
// }
|
||||
//
|
||||
// val feedItems: List<FeedItem?>
|
||||
// when {
|
||||
// parentId == resources.getString(R.string.queue_label) -> {
|
||||
// feedItems = DBReader.getQueue()
|
||||
// }
|
||||
// parentId == resources.getString(R.string.downloads_label) -> {
|
||||
// feedItems = DBReader.getEpisodes(0, MAX_ANDROID_AUTO_EPISODES_PER_FEED,
|
||||
// FeedItemFilter(FeedItemFilter.DOWNLOADED), downloadsSortedOrder)
|
||||
// }
|
||||
// parentId == resources.getString(R.string.episodes_label) -> {
|
||||
// feedItems = DBReader.getEpisodes(0, MAX_ANDROID_AUTO_EPISODES_PER_FEED,
|
||||
// FeedItemFilter(FeedItemFilter.UNPLAYED), allEpisodesSortOrder)
|
||||
// }
|
||||
// parentId.startsWith("FeedId:") -> {
|
||||
// val feedId = parentId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1].toLong()
|
||||
// val feed = DBReader.getFeed(feedId)
|
||||
// feedItems = if (feed != null) DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), feed.sortOrder) else listOf()
|
||||
// }
|
||||
// parentId == getString(R.string.current_playing_episode) -> {
|
||||
// val playable = createInstanceFromPreferences(this)
|
||||
// if (playable is FeedMedia) {
|
||||
// feedItems = listOf(playable.item)
|
||||
// } else {
|
||||
// return null
|
||||
// }
|
||||
// }
|
||||
// else -> {
|
||||
// Log.e(TAG, "Parent ID not found: $parentId")
|
||||
// return null
|
||||
// }
|
||||
// }
|
||||
// var count = 0
|
||||
// for (feedItem in feedItems) {
|
||||
// if (feedItem?.media != null) {
|
||||
// mediaItems.add(feedItem.media!!.mediaItem)
|
||||
// if (++count >= MAX_ANDROID_AUTO_EPISODES_PER_FEED) {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return mediaItems
|
||||
// }
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
Logd(TAG, "Received onBind event")
|
||||
return if (intent?.action != null && TextUtils.equals(intent.action, SERVICE_INTERFACE)) {
|
||||
|
@ -544,23 +411,37 @@ class PlaybackService : MediaSessionService() {
|
|||
val allowStreamAlways = intent.getBooleanExtra(PlaybackServiceConstants.EXTRA_ALLOW_STREAM_ALWAYS, false)
|
||||
sendNotificationBroadcast(PlaybackServiceConstants.NOTIFICATION_TYPE_RELOAD, 0)
|
||||
if (allowStreamAlways) isAllowMobileStreaming = true
|
||||
Observable.fromCallable {
|
||||
if (playable is FeedMedia) return@fromCallable DBReader.getFeedMedia(playable.id)
|
||||
else return@fromCallable playable
|
||||
|
||||
// Observable.fromCallable {
|
||||
// if (playable is FeedMedia) return@fromCallable DBReader.getFeedMedia(playable.id)
|
||||
// else return@fromCallable playable
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { loadedPlayable: Playable? -> startPlaying(loadedPlayable, allowStreamThisTime) },
|
||||
// { error: Throwable ->
|
||||
// Logd(TAG, "Playable was not found. Stopping service.")
|
||||
// error.printStackTrace()
|
||||
// })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
val loadedPlayable = withContext(Dispatchers.IO) {
|
||||
if (playable is FeedMedia) DBReader.getFeedMedia(playable.id)
|
||||
else playable
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ loadedPlayable: Playable? -> startPlaying(loadedPlayable, allowStreamThisTime) },
|
||||
{ error: Throwable ->
|
||||
withContext(Dispatchers.Main) {
|
||||
startPlaying(loadedPlayable, allowStreamThisTime)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Logd(TAG, "Playable was not found. Stopping service.")
|
||||
error.printStackTrace()
|
||||
})
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
else -> {
|
||||
// mediaSession?.controller?.transportControls?.sendCustomAction(customAction, null)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -724,15 +605,28 @@ class PlaybackService : MediaSessionService() {
|
|||
}
|
||||
|
||||
private fun startPlayingFromPreferences() {
|
||||
Observable.fromCallable { createInstanceFromPreferences(applicationContext) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ playable: Playable? -> startPlaying(playable, false) },
|
||||
{ error: Throwable ->
|
||||
// Observable.fromCallable { createInstanceFromPreferences(applicationContext) }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { playable: Playable? -> startPlaying(playable, false) },
|
||||
// { error: Throwable ->
|
||||
// Logd(TAG, "Playable was not loaded from preferences. Stopping service.")
|
||||
// error.printStackTrace()
|
||||
// })
|
||||
scope.launch {
|
||||
try {
|
||||
val playable = withContext(Dispatchers.IO) {
|
||||
createInstanceFromPreferences(applicationContext)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
startPlaying(playable, false)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Logd(TAG, "Playable was not loaded from preferences. Stopping service.")
|
||||
error.printStackTrace()
|
||||
})
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startPlaying(playable: Playable?, allowStreamThisTime: Boolean) {
|
||||
|
@ -750,7 +644,6 @@ class PlaybackService : MediaSessionService() {
|
|||
|
||||
mediaPlayer?.playMediaObject(playable, stream, startWhenPrepared = true, true)
|
||||
recreateMediaSessionIfNeeded()
|
||||
// updateNotificationAndMediaSession(playable)
|
||||
addPlayableToQueue(playable)
|
||||
// EventBus.getDefault().post(PlaybackServiceEvent(PlaybackServiceEvent.Action.SERVICE_RESTARTED))
|
||||
}
|
||||
|
@ -767,10 +660,8 @@ class PlaybackService : MediaSessionService() {
|
|||
fun notifyVideoSurfaceAbandoned() {
|
||||
mediaPlayer?.pause(abandonFocus = true, reinit = false)
|
||||
mediaPlayer?.resetVideoSurface()
|
||||
// updateNotificationAndMediaSession(playable)
|
||||
}
|
||||
|
||||
// TODO: positionEventTimer also monitors position, should be combined?
|
||||
private val taskManagerCallback: PSTMCallback = object : PSTMCallback {
|
||||
override fun positionSaverTick() {
|
||||
if (currentPosition != previousPosition) {
|
||||
|
@ -789,7 +680,6 @@ class PlaybackService : MediaSessionService() {
|
|||
|
||||
override fun onChapterLoaded(media: Playable?) {
|
||||
sendNotificationBroadcast(PlaybackServiceConstants.NOTIFICATION_TYPE_RELOAD, 0)
|
||||
// updateMediaSession(MediaPlayerBase.status)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -797,29 +687,24 @@ class PlaybackService : MediaSessionService() {
|
|||
override fun statusChanged(newInfo: MediaPlayerInfo?) {
|
||||
currentMediaType = mediaPlayer?.getCurrentMediaType() ?: MediaType.UNKNOWN
|
||||
Logd(TAG, "statusChanged called ${newInfo?.playerStatus}")
|
||||
// updateMediaSession(newInfo?.playerStatus)
|
||||
if (newInfo != null) {
|
||||
when (newInfo.playerStatus) {
|
||||
PlayerStatus.INITIALIZED -> {
|
||||
PlayerStatus.INITIALIZED ->
|
||||
if (mediaPlayer != null) writeMediaPlaying(mediaPlayer!!.playerInfo.playable, mediaPlayer!!.playerInfo.playerStatus, currentitem)
|
||||
// updateNotificationAndMediaSession(newInfo.playable)
|
||||
}
|
||||
PlayerStatus.PREPARED -> {
|
||||
if (mediaPlayer != null) writeMediaPlaying(mediaPlayer!!.playerInfo.playable, mediaPlayer!!.playerInfo.playerStatus, currentitem)
|
||||
taskManager.startChapterLoader(newInfo.playable!!)
|
||||
}
|
||||
PlayerStatus.PAUSED -> {
|
||||
// updateNotificationAndMediaSession(newInfo.playable)
|
||||
// cancelPositionObserver()
|
||||
if (mediaPlayer != null) writePlayerStatus(MediaPlayerBase.status)
|
||||
}
|
||||
PlayerStatus.STOPPED -> {}
|
||||
PlayerStatus.PLAYING -> {
|
||||
if (mediaPlayer != null) writePlayerStatus(MediaPlayerBase.status)
|
||||
if (mediaPlayer != null) {
|
||||
writePlayerStatus(MediaPlayerBase.status)
|
||||
}
|
||||
saveCurrentPosition(true, null, Playable.INVALID_TIME)
|
||||
recreateMediaSessionIfNeeded()
|
||||
// updateNotificationAndMediaSession(newInfo.playable)
|
||||
// setupPositionObserver()
|
||||
// set sleep timer if auto-enabled
|
||||
var autoEnableByTime = true
|
||||
val fromSetting = autoEnableFrom()
|
||||
|
@ -857,7 +742,6 @@ class PlaybackService : MediaSessionService() {
|
|||
override fun onMediaChanged(reloadUI: Boolean) {
|
||||
Logd(TAG, "reloadUI callback reached")
|
||||
if (reloadUI) sendNotificationBroadcast(PlaybackServiceConstants.NOTIFICATION_TYPE_RELOAD, 0)
|
||||
// updateNotificationAndMediaSession(this@PlaybackService.playable)
|
||||
}
|
||||
|
||||
override fun onPostPlayback(media: Playable?, ended: Boolean, skipped: Boolean, playingNext: Boolean) {
|
||||
|
@ -874,7 +758,6 @@ class PlaybackService : MediaSessionService() {
|
|||
|
||||
override fun onPlaybackPause(playable: Playable?, position: Int) {
|
||||
taskManager.cancelPositionSaver()
|
||||
// cancelPositionObserver()
|
||||
saveCurrentPosition(position == Playable.INVALID_TIME || playable == null, playable, position)
|
||||
taskManager.cancelWidgetUpdater()
|
||||
if (playable != null) {
|
||||
|
@ -917,7 +800,6 @@ class PlaybackService : MediaSessionService() {
|
|||
// Playable is being streamed and does not have a duration specified in the feed
|
||||
playable.setDuration(mediaPlayer!!.getDuration())
|
||||
DBWriter.persistFeedMedia(playable as FeedMedia)
|
||||
// updateNotificationAndMediaSession(playable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -942,7 +824,7 @@ class PlaybackService : MediaSessionService() {
|
|||
}
|
||||
|
||||
private fun getNextInQueue(currentMedia: Playable?): Playable? {
|
||||
Logd(TAG, "getNextInQueue currentMedia: ${currentMedia?.getEpisodeTitle()}")
|
||||
Logd(TAG, "*** expensive call getNextInQueue currentMedia: ${currentMedia?.getEpisodeTitle()}")
|
||||
if (currentMedia !is FeedMedia) {
|
||||
Logd(TAG, "getNextInQueue(), but playable not an instance of FeedMedia, so not proceeding")
|
||||
writeNoMediaPlaying()
|
||||
|
@ -966,7 +848,6 @@ class PlaybackService : MediaSessionService() {
|
|||
if (!isFollowQueue) {
|
||||
Logd(TAG, "getNextInQueue(), but follow queue is not enabled.")
|
||||
writeMediaPlaying(nextItem.media, PlayerStatus.STOPPED, currentitem)
|
||||
// updateNotificationAndMediaSession(nextItem.media)
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -975,6 +856,7 @@ class PlaybackService : MediaSessionService() {
|
|||
writeNoMediaPlaying()
|
||||
return null
|
||||
}
|
||||
EventBus.getDefault().post(StartPlayEvent(nextItem))
|
||||
return nextItem.media
|
||||
}
|
||||
|
||||
|
@ -986,7 +868,6 @@ class PlaybackService : MediaSessionService() {
|
|||
clearCurrentlyPlayingTemporaryPlaybackSpeed()
|
||||
if (stopPlaying) {
|
||||
taskManager.cancelPositionSaver()
|
||||
// cancelPositionObserver()
|
||||
}
|
||||
if (mediaType == null) {
|
||||
sendNotificationBroadcast(PlaybackServiceConstants.NOTIFICATION_TYPE_PLAYBACK_END, 0)
|
||||
|
@ -1070,7 +951,6 @@ class PlaybackService : MediaSessionService() {
|
|||
DBWriter.deleteFeedMediaOfItem(this@PlaybackService, media.id)
|
||||
Logd(TAG, "Episode Deleted")
|
||||
}
|
||||
// notifyChildrenChanged(getString(R.string.queue_label))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1119,76 +999,6 @@ class PlaybackService : MediaSessionService() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the Media Session for the corresponding status.
|
||||
*/
|
||||
// private fun updateMediaSession(playerStatus: PlayerStatus?) {
|
||||
// val sessionState = PlaybackStateCompat.Builder()
|
||||
// val state = if (playerStatus != null) {
|
||||
// when (playerStatus) {
|
||||
// PlayerStatus.PLAYING -> PlaybackStateCompat.STATE_PLAYING
|
||||
// PlayerStatus.FALLBACK -> PlaybackStateCompat.STATE_PLAYING
|
||||
// PlayerStatus.PREPARED, PlayerStatus.PAUSED -> PlaybackStateCompat.STATE_PAUSED
|
||||
// PlayerStatus.STOPPED -> PlaybackStateCompat.STATE_STOPPED
|
||||
// PlayerStatus.SEEKING -> PlaybackStateCompat.STATE_FAST_FORWARDING
|
||||
// PlayerStatus.PREPARING, PlayerStatus.INITIALIZING -> PlaybackStateCompat.STATE_CONNECTING
|
||||
// PlayerStatus.ERROR -> PlaybackStateCompat.STATE_ERROR
|
||||
// PlayerStatus.INITIALIZED, PlayerStatus.INDETERMINATE -> PlaybackStateCompat.STATE_NONE
|
||||
// }
|
||||
// } else {
|
||||
// PlaybackStateCompat.STATE_NONE
|
||||
// }
|
||||
//
|
||||
// sessionState.setState(state, currentPosition.toLong(), currentPlaybackSpeed)
|
||||
// val capabilities = (PlaybackStateCompat.ACTION_PLAY
|
||||
// or PlaybackStateCompat.ACTION_PLAY_PAUSE
|
||||
// or PlaybackStateCompat.ACTION_REWIND
|
||||
// or PlaybackStateCompat.ACTION_PAUSE
|
||||
// or PlaybackStateCompat.ACTION_FAST_FORWARD
|
||||
// or PlaybackStateCompat.ACTION_SEEK_TO
|
||||
// or PlaybackStateCompat.ACTION_SET_PLAYBACK_SPEED)
|
||||
//
|
||||
// sessionState.setActions(capabilities)
|
||||
|
||||
// On Android Auto, custom actions are added in the following order around the play button, if no default
|
||||
// actions are present: Near left, near right, far left, far right, additional actions panel
|
||||
// val rewindBuilder = PlaybackStateCompat.CustomAction.Builder(CUSTOM_ACTION_REWIND, getString(R.string.rewind_label), R.drawable.ic_notification_fast_rewind)
|
||||
// WearMediaSession.addWearExtrasToAction(rewindBuilder)
|
||||
//// sessionState.addCustomAction(rewindBuilder.build())
|
||||
|
||||
// val fastForwardBuilder = PlaybackStateCompat.CustomAction.Builder(CUSTOM_ACTION_FAST_FORWARD, getString(R.string.fast_forward_label), R.drawable.ic_notification_fast_forward)
|
||||
// WearMediaSession.addWearExtrasToAction(fastForwardBuilder)
|
||||
// sessionState.addCustomAction(fastForwardBuilder.build())
|
||||
|
||||
// if (showPlaybackSpeedOnFullNotification())
|
||||
// sessionState.addCustomAction(PlaybackStateCompat.CustomAction.Builder(CUSTOM_ACTION_CHANGE_PLAYBACK_SPEED,
|
||||
// getString(R.string.playback_speed), R.drawable.ic_notification_playback_speed).build())
|
||||
|
||||
// if (showNextChapterOnFullNotification()) {
|
||||
// if (!playable?.getChapters().isNullOrEmpty())
|
||||
// sessionState.addCustomAction(PlaybackStateCompat.CustomAction.Builder(CUSTOM_ACTION_NEXT_CHAPTER,
|
||||
// getString(R.string.next_chapter), R.drawable.ic_notification_next_chapter).build())
|
||||
// }
|
||||
|
||||
// if (showSkipOnFullNotification())
|
||||
// sessionState.addCustomAction(PlaybackStateCompat.CustomAction.Builder(CUSTOM_ACTION_SKIP_TO_NEXT,
|
||||
// getString(R.string.skip_episode_label), R.drawable.ic_notification_skip).build())
|
||||
|
||||
|
||||
// if (mediaSession != null) {
|
||||
// WearMediaSession.mediaSessionSetExtraForWear(mediaSession!!)
|
||||
//// mediaSession!!.setPlaybackState(sessionState.build())
|
||||
// }
|
||||
// }
|
||||
|
||||
// private fun updateMediaSessionMetadata(p: Playable?) {
|
||||
// if (p == null || mediaSession == null) return
|
||||
//
|
||||
// // TODO: what's this?
|
||||
// mediaSession!!.setSessionActivity(
|
||||
// PendingIntent.getActivity(this, R.id.pending_intent_player_activity, getPlayerActivityIntent(this), FLAG_IMMUTABLE))
|
||||
// }
|
||||
|
||||
/**
|
||||
* Persists the current position and last played time of the media file.
|
||||
*
|
||||
|
@ -1485,30 +1295,10 @@ class PlaybackService : MediaSessionService() {
|
|||
mediaPlayer?.setAudioTrack(track)
|
||||
}
|
||||
|
||||
// private fun setupPositionObserver() {
|
||||
// positionEventTimer?.dispose()
|
||||
//
|
||||
// Log.d(TAG, "Setting up position observer")
|
||||
// positionEventTimer = Observable.interval(POSITION_EVENT_INTERVAL, TimeUnit.SECONDS)
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
//// .takeWhile { currentPosition != previousPosition }
|
||||
// .subscribe {
|
||||
// Log.d(TAG, "positionEventTimer currentPosition: $currentPosition, currentPlaybackSpeed: $currentPlaybackSpeed")
|
||||
// EventBus.getDefault().post(PlaybackPositionEvent(currentPosition, duration))
|
||||
// previousPosition = currentPosition
|
||||
// skipEndingIfNecessary()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun cancelPositionObserver() {
|
||||
// positionEventTimer?.dispose()
|
||||
// }
|
||||
|
||||
private fun addPlayableToQueue(playable: Playable?) {
|
||||
if (playable is FeedMedia) {
|
||||
val itemId = playable.item?.id ?: return
|
||||
DBWriter.addQueueItem(this, false, true, itemId)
|
||||
// notifyChildrenChanged(getString(R.string.queue_label))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +1,25 @@
|
|||
package ac.mdiq.podcini.playback.service
|
||||
|
||||
import ac.mdiq.podcini.preferences.SleepTimerPreferences
|
||||
import ac.mdiq.podcini.storage.model.playback.Playable
|
||||
import ac.mdiq.podcini.ui.widget.WidgetUpdater
|
||||
import ac.mdiq.podcini.ui.widget.WidgetUpdater.WidgetState
|
||||
import ac.mdiq.podcini.util.ChapterUtils
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.playback.SleepTimerUpdatedEvent
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.Vibrator
|
||||
import android.util.Log
|
||||
import ac.mdiq.podcini.preferences.SleepTimerPreferences
|
||||
import ac.mdiq.podcini.util.ChapterUtils
|
||||
import ac.mdiq.podcini.ui.widget.WidgetUpdater
|
||||
import ac.mdiq.podcini.ui.widget.WidgetUpdater.WidgetState
|
||||
import ac.mdiq.podcini.util.event.playback.SleepTimerUpdatedEvent
|
||||
import ac.mdiq.podcini.storage.model.playback.Playable
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.CompletableEmitter
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.util.concurrent.ScheduledFuture
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.concurrent.Volatile
|
||||
|
||||
/**
|
||||
* Manages the background tasks of PlaybackSerivce, i.e.
|
||||
|
@ -38,11 +37,40 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
private var widgetUpdaterFuture: ScheduledFuture<*>? = null
|
||||
private var sleepTimerFuture: ScheduledFuture<*>? = null
|
||||
|
||||
@Volatile
|
||||
private var chapterLoaderFuture: Disposable? = null
|
||||
// @Volatile
|
||||
// private var chapterLoaderFuture: Disposable? = null
|
||||
|
||||
private var sleepTimer: SleepTimer? = null
|
||||
|
||||
/**
|
||||
* Returns true if the sleep timer is currently active.
|
||||
*/
|
||||
@get:Synchronized
|
||||
val isSleepTimerActive: Boolean
|
||||
get() = (sleepTimer != null && sleepTimerFuture != null && !sleepTimerFuture!!.isCancelled
|
||||
&& !sleepTimerFuture!!.isDone) && sleepTimer!!.getWaitingTime() > 0
|
||||
|
||||
/**
|
||||
* Returns the current sleep timer time or 0 if the sleep timer is not active.
|
||||
*/
|
||||
@get:Synchronized
|
||||
val sleepTimerTimeLeft: Long
|
||||
get() = if (isSleepTimerActive) sleepTimer!!.getWaitingTime() else 0
|
||||
|
||||
/**
|
||||
* Returns true if the widget updater is currently running.
|
||||
*/
|
||||
@get:Synchronized
|
||||
val isWidgetUpdaterActive: Boolean
|
||||
get() = widgetUpdaterFuture != null && !widgetUpdaterFuture!!.isCancelled && !widgetUpdaterFuture!!.isDone
|
||||
|
||||
/**
|
||||
* Returns true if the position saver is currently running.
|
||||
*/
|
||||
@get:Synchronized
|
||||
val isPositionSaverActive: Boolean
|
||||
get() = positionSaverFuture != null && !positionSaverFuture!!.isCancelled && !positionSaverFuture!!.isDone
|
||||
|
||||
/**
|
||||
* Sets up a new PSTM. This method will also start the queue loader task.
|
||||
*
|
||||
|
@ -67,17 +95,10 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
positionSaver = useMainThreadIfNecessary(positionSaver)
|
||||
positionSaverFuture = schedExecutor.scheduleWithFixedDelay(positionSaver, POSITION_SAVER_WAITING_INTERVAL.toLong(),
|
||||
POSITION_SAVER_WAITING_INTERVAL.toLong(), TimeUnit.MILLISECONDS)
|
||||
Log.d(TAG, "Started PositionSaver")
|
||||
} else Log.d(TAG, "Call to startPositionSaver was ignored.")
|
||||
Logd(TAG, "Started PositionSaver")
|
||||
} else Logd(TAG, "Call to startPositionSaver was ignored.")
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
val isPositionSaverActive: Boolean
|
||||
/**
|
||||
* Returns true if the position saver is currently running.
|
||||
*/
|
||||
get() = positionSaverFuture != null && !positionSaverFuture!!.isCancelled && !positionSaverFuture!!.isDone
|
||||
|
||||
/**
|
||||
* Cancels the position saver. If the position saver is not running, nothing will happen.
|
||||
*/
|
||||
|
@ -85,7 +106,7 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
fun cancelPositionSaver() {
|
||||
if (isPositionSaverActive) {
|
||||
positionSaverFuture!!.cancel(false)
|
||||
Log.d(TAG, "Cancelled PositionSaver")
|
||||
Logd(TAG, "Cancelled PositionSaver")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,8 +120,8 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
widgetUpdater = useMainThreadIfNecessary(widgetUpdater)
|
||||
widgetUpdaterFuture = schedExecutor.scheduleWithFixedDelay(widgetUpdater, WIDGET_UPDATER_NOTIFICATION_INTERVAL.toLong(),
|
||||
WIDGET_UPDATER_NOTIFICATION_INTERVAL.toLong(), TimeUnit.MILLISECONDS)
|
||||
Log.d(TAG, "Started WidgetUpdater")
|
||||
} else Log.d(TAG, "Call to startWidgetUpdater was ignored.")
|
||||
Logd(TAG, "Started WidgetUpdater")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,7 +131,7 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
fun requestWidgetUpdate() {
|
||||
val state = callback.requestWidgetState()
|
||||
if (!schedExecutor.isShutdown) schedExecutor.execute { WidgetUpdater.updateWidget(context, state) }
|
||||
else Log.d(TAG, "Call to requestWidgetUpdate was ignored.")
|
||||
else Logd(TAG, "Call to requestWidgetUpdate was ignored.")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,28 +145,20 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
fun setSleepTimer(waitingTime: Long) {
|
||||
require(waitingTime > 0) { "Waiting time <= 0" }
|
||||
|
||||
Log.d(TAG, "Setting sleep timer to $waitingTime milliseconds")
|
||||
Logd(TAG, "Setting sleep timer to $waitingTime milliseconds")
|
||||
if (isSleepTimerActive) sleepTimerFuture!!.cancel(true)
|
||||
sleepTimer = SleepTimer(waitingTime)
|
||||
sleepTimerFuture = schedExecutor.schedule(sleepTimer, 0, TimeUnit.MILLISECONDS)
|
||||
EventBus.getDefault().post(SleepTimerUpdatedEvent.justEnabled(waitingTime))
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
val isSleepTimerActive: Boolean
|
||||
/**
|
||||
* Returns true if the sleep timer is currently active.
|
||||
*/
|
||||
get() = (sleepTimer != null && sleepTimerFuture != null && !sleepTimerFuture!!.isCancelled
|
||||
&& !sleepTimerFuture!!.isDone) && sleepTimer!!.getWaitingTime() > 0
|
||||
|
||||
/**
|
||||
* Disables the sleep timer. If the sleep timer is not active, nothing will happen.
|
||||
*/
|
||||
@Synchronized
|
||||
fun disableSleepTimer() {
|
||||
if (isSleepTimerActive) {
|
||||
Log.d(TAG, "Disabling sleep timer")
|
||||
Logd(TAG, "Disabling sleep timer")
|
||||
sleepTimer!!.cancel()
|
||||
}
|
||||
}
|
||||
|
@ -156,25 +169,11 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
@Synchronized
|
||||
fun restartSleepTimer() {
|
||||
if (isSleepTimerActive) {
|
||||
Log.d(TAG, "Restarting sleep timer")
|
||||
Logd(TAG, "Restarting sleep timer")
|
||||
sleepTimer!!.restart()
|
||||
}
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
val sleepTimerTimeLeft: Long
|
||||
/**
|
||||
* Returns the current sleep timer time or 0 if the sleep timer is not active.
|
||||
*/
|
||||
get() = if (isSleepTimerActive) sleepTimer!!.getWaitingTime() else 0
|
||||
|
||||
@get:Synchronized
|
||||
val isWidgetUpdaterActive: Boolean
|
||||
/**
|
||||
* Returns true if the widget updater is currently running.
|
||||
*/
|
||||
get() = widgetUpdaterFuture != null && !widgetUpdaterFuture!!.isCancelled && !widgetUpdaterFuture!!.isDone
|
||||
|
||||
/**
|
||||
* Cancels the widget updater. If the widget updater is not running, nothing will happen.
|
||||
*/
|
||||
|
@ -182,7 +181,7 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
fun cancelWidgetUpdater() {
|
||||
if (isWidgetUpdaterActive) {
|
||||
widgetUpdaterFuture!!.cancel(false)
|
||||
Log.d(TAG, "Cancelled WidgetUpdater")
|
||||
Logd(TAG, "Cancelled WidgetUpdater")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,23 +192,34 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
*/
|
||||
@Synchronized
|
||||
fun startChapterLoader(media: Playable) {
|
||||
chapterLoaderFuture?.dispose()
|
||||
chapterLoaderFuture = null
|
||||
// chapterLoaderFuture?.dispose()
|
||||
// chapterLoaderFuture = null
|
||||
|
||||
if (!media.chaptersLoaded()) {
|
||||
chapterLoaderFuture = Completable.create { emitter: CompletableEmitter ->
|
||||
ChapterUtils.loadChapters(media, context, false)
|
||||
emitter.onComplete()
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ callback.onChapterLoaded(media) },
|
||||
{ throwable: Throwable? ->
|
||||
Log.d(TAG, "Error loading chapters: " + Log.getStackTraceString(throwable))
|
||||
})
|
||||
}
|
||||
}
|
||||
// chapterLoaderFuture = Completable.create { emitter: CompletableEmitter ->
|
||||
// ChapterUtils.loadChapters(media, context, false)
|
||||
// emitter.onComplete()
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ callback.onChapterLoaded(media) },
|
||||
// { throwable: Throwable? ->
|
||||
// Logd(TAG, "Error loading chapters: " + Log.getStackTraceString(throwable))
|
||||
// })
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
ChapterUtils.loadChapters(media, context, false)
|
||||
withContext(Dispatchers.Main) {
|
||||
callback.onChapterLoaded(media)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.d(TAG, "Error loading chapters: ${Log.getStackTraceString(e)}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all tasks. The PSTM will be in the initial state after execution of this method.
|
||||
|
@ -220,8 +230,8 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
cancelWidgetUpdater()
|
||||
disableSleepTimer()
|
||||
|
||||
chapterLoaderFuture?.dispose()
|
||||
chapterLoaderFuture = null
|
||||
// chapterLoaderFuture?.dispose()
|
||||
// chapterLoaderFuture = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -251,14 +261,14 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
private var shakeListener: ShakeListener? = null
|
||||
|
||||
override fun run() {
|
||||
Log.d(TAG, "Starting")
|
||||
Logd(TAG, "Starting SleepTimer")
|
||||
var lastTick = System.currentTimeMillis()
|
||||
EventBus.getDefault().post(SleepTimerUpdatedEvent.updated(timeLeft))
|
||||
while (timeLeft > 0) {
|
||||
try {
|
||||
Thread.sleep(UPDATE_INTERVAL)
|
||||
} catch (e: InterruptedException) {
|
||||
Log.d(TAG, "Thread was interrupted while waiting")
|
||||
Logd(TAG, "Thread was interrupted while waiting")
|
||||
e.printStackTrace()
|
||||
break
|
||||
}
|
||||
|
@ -269,7 +279,7 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
|
||||
EventBus.getDefault().post(SleepTimerUpdatedEvent.updated(timeLeft))
|
||||
if (timeLeft < NOTIFICATION_THRESHOLD) {
|
||||
Log.d(TAG, "Sleep timer is about to expire")
|
||||
Logd(TAG, "Sleep timer is about to expire")
|
||||
if (SleepTimerPreferences.vibrate() && !hasVibrated) {
|
||||
val v = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
if (v != null) {
|
||||
|
@ -280,7 +290,7 @@ class PlaybackServiceTaskManager(private val context: Context, private val callb
|
|||
if (shakeListener == null && SleepTimerPreferences.shakeToReset()) shakeListener = ShakeListener(context, this)
|
||||
}
|
||||
if (timeLeft <= 0) {
|
||||
Log.d(TAG, "Sleep timer expired")
|
||||
Logd(TAG, "Sleep timer expired")
|
||||
shakeListener?.pause()
|
||||
shakeListener = null
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import androidx.annotation.RequiresApi
|
|||
import androidx.media3.common.util.UnstableApi
|
||||
import ac.mdiq.podcini.preferences.PlaybackPreferences
|
||||
import ac.mdiq.podcini.receiver.MediaButtonReceiver
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
|
||||
@UnstableApi
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
|
@ -43,7 +44,7 @@ class QuickSettingsTileService : TileService() {
|
|||
|
||||
fun updateTile() {
|
||||
val qsTile = qsTile
|
||||
if (qsTile == null) Log.d(TAG, "Ignored call to update QS tile: getQsTile() returned null.")
|
||||
if (qsTile == null) Logd(TAG, "Ignored call to update QS tile: getQsTile() returned null.")
|
||||
else {
|
||||
val isPlaying = (PlaybackService.isRunning && PlaybackPreferences.currentPlayerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING)
|
||||
qsTile.state = if (isPlaying) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.hardware.SensorEventListener
|
|||
import android.hardware.SensorManager
|
||||
import android.util.Log
|
||||
import ac.mdiq.podcini.playback.service.PlaybackServiceTaskManager.SleepTimer
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import kotlin.math.sqrt
|
||||
|
||||
internal class ShakeListener(private val mContext: Context, private val mSleepTimer: SleepTimer) : SensorEventListener {
|
||||
|
@ -42,7 +43,7 @@ internal class ShakeListener(private val mContext: Context, private val mSleepTi
|
|||
|
||||
val gForce = sqrt((gX * gX + gY * gY + gZ * gZ).toDouble())
|
||||
if (gForce > 2.25) {
|
||||
Log.d(TAG, "Detected shake $gForce")
|
||||
Logd(TAG, "Detected shake $gForce")
|
||||
mSleepTimer.restart()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
|||
import ac.mdiq.podcini.storage.model.feed.FeedPreferences
|
||||
import ac.mdiq.podcini.storage.model.playback.MediaType
|
||||
import ac.mdiq.podcini.storage.model.playback.Playable
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.PlayerStatusEvent
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
@ -133,7 +134,7 @@ class PlaybackPreferences private constructor() : OnSharedPreferenceChangeListen
|
|||
|
||||
@JvmStatic
|
||||
fun writeMediaPlaying(playable: Playable?, playerStatus: PlayerStatus, item: FeedItem? = null) {
|
||||
Log.d(TAG, "Writing playback preferences")
|
||||
Logd(TAG, "Writing playback preferences ${playable?.getIdentifier()}")
|
||||
val editor = prefs.edit()
|
||||
|
||||
if (playable == null) {
|
||||
|
@ -159,7 +160,7 @@ class PlaybackPreferences private constructor() : OnSharedPreferenceChangeListen
|
|||
|
||||
@JvmStatic
|
||||
fun writePlayerStatus(playerStatus: PlayerStatus) {
|
||||
Log.d(TAG, "Writing player status playback preferences")
|
||||
Logd(TAG, "Writing player status playback preferences")
|
||||
|
||||
val editor = prefs.edit()
|
||||
editor.putInt(PREF_CURRENT_PLAYER_STATUS, getCurrentPlayerStatusAsInt(playerStatus))
|
||||
|
@ -191,7 +192,7 @@ class PlaybackPreferences private constructor() : OnSharedPreferenceChangeListen
|
|||
@JvmStatic
|
||||
fun createInstanceFromPreferences(context: Context): Playable? {
|
||||
val currentlyPlayingMedia = currentlyPlayingMediaType
|
||||
Log.d(TAG, "currentlyPlayingMedia: $currentlyPlayingMedia")
|
||||
Logd(TAG, "currentlyPlayingMedia: $currentlyPlayingMedia")
|
||||
if (currentlyPlayingMedia != NO_MEDIA_PLAYING) {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context.applicationContext)
|
||||
return createInstanceFromPreferences(currentlyPlayingMedia.toInt(), prefs)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ac.mdiq.podcini.preferences
|
||||
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
|
@ -30,7 +31,7 @@ object SleepTimerPreferences {
|
|||
*/
|
||||
@JvmStatic
|
||||
fun init(context: Context) {
|
||||
Log.d(TAG, "Creating new instance of SleepTimerPreferences")
|
||||
Logd(TAG, "Creating new instance of SleepTimerPreferences")
|
||||
prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import ac.mdiq.podcini.storage.model.feed.FeedPreferences.NewEpisodesAction
|
|||
import ac.mdiq.podcini.storage.model.feed.SortOrder
|
||||
import ac.mdiq.podcini.storage.model.feed.SubscriptionsFilter
|
||||
import ac.mdiq.podcini.storage.model.playback.MediaType
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
|
@ -145,7 +146,7 @@ object UserPreferences {
|
|||
*/
|
||||
@JvmStatic
|
||||
fun init(context: Context) {
|
||||
Log.d(TAG, "Creating new instance of UserPreferences")
|
||||
Logd(TAG, "Creating new instance of UserPreferences")
|
||||
|
||||
UserPreferences.context = context.applicationContext
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
@ -697,11 +698,11 @@ object UserPreferences {
|
|||
fun getDataFolder(type: String?): File? {
|
||||
var dataFolder = getTypeDir(prefs.getString(PREF_DATA_FOLDER, null), type)
|
||||
if (dataFolder == null || !dataFolder.canWrite()) {
|
||||
Log.d(TAG, "User data folder not writable or not set. Trying default.")
|
||||
Logd(TAG, "User data folder not writable or not set. Trying default.")
|
||||
dataFolder = context.getExternalFilesDir(type)
|
||||
}
|
||||
if (dataFolder == null || !dataFolder.canWrite()) {
|
||||
Log.d(TAG, "Default data folder not available or not writable. Falling back to internal memory.")
|
||||
Logd(TAG, "Default data folder not available or not writable. Falling back to internal memory.")
|
||||
dataFolder = getTypeDir(context.filesDir.absolutePath, type)
|
||||
}
|
||||
return dataFolder
|
||||
|
@ -727,7 +728,7 @@ object UserPreferences {
|
|||
|
||||
@JvmStatic
|
||||
fun setDataFolder(dir: String) {
|
||||
Log.d(TAG, "setDataFolder(dir: $dir)")
|
||||
Logd(TAG, "setDataFolder(dir: $dir)")
|
||||
prefs.edit().putString(PREF_DATA_FOLDER, dir).apply()
|
||||
}
|
||||
|
||||
|
@ -743,7 +744,7 @@ object UserPreferences {
|
|||
Log.e(TAG, "Could not create .nomedia file")
|
||||
e.printStackTrace()
|
||||
}
|
||||
Log.d(TAG, ".nomedia file created")
|
||||
Logd(TAG, ".nomedia file created")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import ac.mdiq.podcini.preferences.UserPreferences.autodownloadSelectedNetworks
|
|||
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownloadWifiFilter
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.setAutodownloadSelectedNetworks
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import java.util.*
|
||||
|
||||
class AutoDownloadPreferencesFragment : PreferenceFragmentCompat() {
|
||||
|
@ -95,7 +96,7 @@ class AutoDownloadPreferencesFragment : PreferenceFragmentCompat() {
|
|||
val key = preference.getKey()
|
||||
val prefValuesList: MutableList<String?> = ArrayList(listOf(*autodownloadSelectedNetworks))
|
||||
val newValue = preference.isChecked
|
||||
Log.d(TAG, "Selected network $key. New state: $newValue")
|
||||
Logd(TAG, "Selected network $key. New state: $newValue")
|
||||
|
||||
val index = prefValuesList.indexOf(key)
|
||||
when {
|
||||
|
|
|
@ -6,6 +6,7 @@ import ac.mdiq.podcini.net.sync.SynchronizationCredentials
|
|||
import ac.mdiq.podcini.net.sync.SynchronizationSettings.setWifiSyncEnabled
|
||||
import ac.mdiq.podcini.net.sync.wifi.WifiSyncService.Companion.hostPort
|
||||
import ac.mdiq.podcini.net.sync.wifi.WifiSyncService.Companion.startInstantSync
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.SyncServiceEvent
|
||||
import android.app.Dialog
|
||||
import android.content.Context.WIFI_SERVICE
|
||||
|
@ -76,7 +77,7 @@ import java.util.*
|
|||
if (d != null) {
|
||||
val confirmButton = d.getButton(Dialog.BUTTON_POSITIVE) as Button
|
||||
confirmButton.setOnClickListener {
|
||||
Log.d(TAG, "confirm button pressed")
|
||||
Logd(TAG, "confirm button pressed")
|
||||
if (isGuest == null) {
|
||||
Toast.makeText(requireContext(), R.string.host_or_guest, Toast.LENGTH_LONG).show()
|
||||
return@setOnClickListener
|
||||
|
@ -107,7 +108,7 @@ import java.util.*
|
|||
binding!!.progressBar.progress = event.message.toInt()
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "Sync result unknow ${event.messageResId}")
|
||||
Logd(TAG, "Sync result unknow ${event.messageResId}")
|
||||
// Toast.makeText(context, "Sync result unknow ${event.messageResId}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,12 @@ import android.util.Log
|
|||
import androidx.media3.common.util.UnstableApi
|
||||
import ac.mdiq.podcini.util.config.ClientConfigurator
|
||||
import ac.mdiq.podcini.net.download.NetworkConnectionChangeHandler.networkChangedDetected
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
|
||||
class ConnectivityActionReceiver : BroadcastReceiver() {
|
||||
@UnstableApi override fun onReceive(context: Context, intent: Intent) {
|
||||
if (TextUtils.equals(intent.action, ConnectivityManager.CONNECTIVITY_ACTION)) {
|
||||
Log.d(TAG, "Received intent")
|
||||
Logd(TAG, "Received intent")
|
||||
|
||||
ClientConfigurator.initialize(context)
|
||||
networkChangedDetected()
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.util.Log
|
|||
import androidx.media3.common.util.UnstableApi
|
||||
import ac.mdiq.podcini.util.config.ClientConfigurator
|
||||
import ac.mdiq.podcini.net.download.FeedUpdateManager
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
|
||||
/**
|
||||
* Refreshes all feeds when it receives an intent
|
||||
|
@ -14,7 +15,7 @@ import ac.mdiq.podcini.net.download.FeedUpdateManager
|
|||
class FeedUpdateReceiver : BroadcastReceiver() {
|
||||
@UnstableApi
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
Log.d(TAG, "Received intent")
|
||||
Logd(TAG, "Received intent")
|
||||
ClientConfigurator.initialize(context)
|
||||
|
||||
FeedUpdateManager.runOnce(context)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ac.mdiq.podcini.receiver
|
||||
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
|
@ -17,7 +18,7 @@ import ac.mdiq.podcini.util.config.ClientConfigurator
|
|||
class MediaButtonReceiver : BroadcastReceiver() {
|
||||
@UnstableApi
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
Log.d(TAG, "Received intent")
|
||||
Logd(TAG, "Received intent")
|
||||
if (intent.extras == null) return
|
||||
|
||||
val event = intent.extras!![Intent.EXTRA_KEY_EVENT] as? KeyEvent
|
||||
|
|
|
@ -9,19 +9,20 @@ import androidx.work.ExistingWorkPolicy
|
|||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import ac.mdiq.podcini.ui.widget.WidgetUpdaterWorker
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class PlayerWidget : AppWidgetProvider() {
|
||||
override fun onEnabled(context: Context) {
|
||||
super.onEnabled(context)
|
||||
Log.d(TAG, "Widget enabled")
|
||||
Logd(TAG, "Widget enabled")
|
||||
setEnabled(context, true)
|
||||
WidgetUpdaterWorker.enqueueWork(context)
|
||||
scheduleWorkaround(context)
|
||||
}
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
Log.d(TAG, "onUpdate() called with: context = [$context], appWidgetManager = [$appWidgetManager], appWidgetIds = [${appWidgetIds.contentToString()}]")
|
||||
Logd(TAG, "onUpdate() called with: context = [$context], appWidgetManager = [$appWidgetManager], appWidgetIds = [${appWidgetIds.contentToString()}]")
|
||||
WidgetUpdaterWorker.enqueueWork(context)
|
||||
|
||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
|
@ -33,12 +34,12 @@ class PlayerWidget : AppWidgetProvider() {
|
|||
|
||||
override fun onDisabled(context: Context) {
|
||||
super.onDisabled(context)
|
||||
Log.d(TAG, "Widget disabled")
|
||||
Logd(TAG, "Widget disabled")
|
||||
setEnabled(context, false)
|
||||
}
|
||||
|
||||
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
||||
Log.d(TAG, "OnDeleted")
|
||||
Logd(TAG, "OnDeleted")
|
||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
for (appWidgetId in appWidgetIds) {
|
||||
prefs.edit().remove(KEY_WIDGET_COLOR + appWidgetId).apply()
|
||||
|
|
|
@ -9,6 +9,7 @@ import ac.mdiq.podcini.util.config.ClientConfigurator
|
|||
import ac.mdiq.podcini.storage.DBTasks
|
||||
import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownloadOnBattery
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
|
||||
// modified from http://developer.android.com/training/monitoring-device-state/battery-monitoring.html
|
||||
// and ConnectivityActionReceiver.java
|
||||
|
@ -19,11 +20,11 @@ class PowerConnectionReceiver : BroadcastReceiver() {
|
|||
@UnstableApi override fun onReceive(context: Context, intent: Intent) {
|
||||
val action = intent.action
|
||||
|
||||
Log.d(TAG, "charging intent: $action")
|
||||
Logd(TAG, "charging intent: $action")
|
||||
|
||||
ClientConfigurator.initialize(context)
|
||||
if (Intent.ACTION_POWER_CONNECTED == action) {
|
||||
Log.d(TAG, "charging, starting auto-download")
|
||||
Logd(TAG, "charging, starting auto-download")
|
||||
// we're plugged in, this is a great time to auto-download if everything else is
|
||||
// right. So, even if the user allows auto-dl on battery, let's still start
|
||||
// downloading now. They shouldn't mind.
|
||||
|
@ -33,9 +34,9 @@ class PowerConnectionReceiver : BroadcastReceiver() {
|
|||
} else {
|
||||
// if we're not supposed to be auto-downloading when we're not charging, stop it
|
||||
if (!isEnableAutodownloadOnBattery) {
|
||||
Log.d(TAG, "not charging anymore, canceling auto-download")
|
||||
Logd(TAG, "not charging anymore, canceling auto-download")
|
||||
DownloadServiceInterface.get()?.cancelAll(context)
|
||||
} else Log.d(TAG, "not charging anymore, but the user allows auto-download when on battery so we'll keep going")
|
||||
} else Logd(TAG, "not charging anymore, but the user allows auto-download when on battery so we'll keep going")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import ac.mdiq.podcini.util.config.ClientConfigurator
|
|||
import ac.mdiq.podcini.storage.DBTasks
|
||||
import ac.mdiq.podcini.net.download.FeedUpdateManager.runOnce
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
|
||||
/**
|
||||
* Receives intents from Podcini Single Purpose apps
|
||||
|
@ -20,7 +21,7 @@ class SPAReceiver : BroadcastReceiver() {
|
|||
@UnstableApi override fun onReceive(context: Context, intent: Intent) {
|
||||
if (!TextUtils.equals(intent.action, ACTION_SP_APPS_QUERY_FEEDS_REPSONSE)) return
|
||||
|
||||
Log.d(TAG, "Received SP_APPS_QUERY_RESPONSE")
|
||||
Logd(TAG, "Received SP_APPS_QUERY_RESPONSE")
|
||||
if (!intent.hasExtra(ACTION_SP_APPS_QUERY_FEEDS_REPSONSE_FEEDS_EXTRA)) {
|
||||
Log.e(TAG, "Received invalid SP_APPS_QUERY_RESPONSE: Contains no extra")
|
||||
return
|
||||
|
@ -30,7 +31,7 @@ class SPAReceiver : BroadcastReceiver() {
|
|||
Log.e(TAG, "Received invalid SP_APPS_QUERY_REPSONSE: extra was null")
|
||||
return
|
||||
}
|
||||
Log.d(TAG, "Received feeds list: " + feedUrls.contentToString())
|
||||
Logd(TAG, "Received feeds list: " + feedUrls.contentToString())
|
||||
ClientConfigurator.initialize(context)
|
||||
for (url in feedUrls) {
|
||||
val feed = Feed(url, null, "Unknown podcast")
|
||||
|
|
|
@ -18,6 +18,7 @@ import ac.mdiq.podcini.preferences.UserPreferences
|
|||
import ac.mdiq.podcini.preferences.UserPreferences.episodeCacheSize
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownloadOnBattery
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
|
||||
/**
|
||||
* Implements the automatic download algorithm used by Podcini. This class assumes that
|
||||
|
@ -42,17 +43,17 @@ open class AutomaticDownloadAlgorithm {
|
|||
|
||||
// true if we should auto download based on power status
|
||||
val powerShouldAutoDl = (deviceCharging(context) || isEnableAutodownloadOnBattery)
|
||||
Log.d(TAG, "prepare autoDownloadUndownloadedItems $networkShouldAutoDl $powerShouldAutoDl")
|
||||
Logd(TAG, "prepare autoDownloadUndownloadedItems $networkShouldAutoDl $powerShouldAutoDl")
|
||||
|
||||
// we should only auto download if both network AND power are happy
|
||||
if (networkShouldAutoDl && powerShouldAutoDl) {
|
||||
Log.d(TAG, "Performing auto-dl of undownloaded episodes")
|
||||
Logd(TAG, "Performing auto-dl of undownloaded episodes")
|
||||
|
||||
val candidates: MutableList<FeedItem>
|
||||
val queue = getQueue()
|
||||
|
||||
val newItems = getEpisodes(0, Int.MAX_VALUE, FeedItemFilter(FeedItemFilter.NEW), SortOrder.DATE_NEW_OLD)
|
||||
Log.d(TAG, "newItems: ${newItems.size}")
|
||||
Logd(TAG, "newItems: ${newItems.size}")
|
||||
candidates = ArrayList(queue.size + newItems.size)
|
||||
candidates.addAll(queue)
|
||||
for (newItem in newItems) {
|
||||
|
@ -80,14 +81,14 @@ open class AutomaticDownloadAlgorithm {
|
|||
|
||||
val itemsToDownload: List<FeedItem> = candidates.subList(0, episodeSpaceLeft)
|
||||
if (itemsToDownload.isNotEmpty()) {
|
||||
Log.d(TAG, "Enqueueing " + itemsToDownload.size + " items for download")
|
||||
Logd(TAG, "Enqueueing " + itemsToDownload.size + " items for download")
|
||||
|
||||
for (episode in itemsToDownload) {
|
||||
DownloadServiceInterface.get()?.download(context, episode)
|
||||
}
|
||||
}
|
||||
}
|
||||
else Log.d(TAG, "not auto downloaded networkShouldAutoDl: $networkShouldAutoDl powerShouldAutoDl $powerShouldAutoDl")
|
||||
else Logd(TAG, "not auto downloaded networkShouldAutoDl: $networkShouldAutoDl powerShouldAutoDl $powerShouldAutoDl")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ import ac.mdiq.podcini.util.comparator.DownloadResultComparator
|
|||
import ac.mdiq.podcini.util.comparator.PlaybackCompletionDateComparator
|
||||
import android.database.Cursor
|
||||
import android.util.Log
|
||||
import androidx.collection.ArrayMap
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
|
@ -30,8 +29,9 @@ import kotlin.math.min
|
|||
*/
|
||||
object DBReader {
|
||||
private const val TAG = "DBReader"
|
||||
private var feeds: MutableList<Feed> = mutableListOf()
|
||||
private var tags: MutableList<String> = mutableListOf()
|
||||
private val feeds: MutableList<Feed> = mutableListOf()
|
||||
private val feedIndex: MutableMap<Long, Feed> = mutableMapOf()
|
||||
private val tags: MutableList<String> = mutableListOf()
|
||||
private val feedListLock = Any()
|
||||
|
||||
/**
|
||||
|
@ -61,11 +61,12 @@ object DBReader {
|
|||
fun updateFeedList(adapter: PodDBAdapter) {
|
||||
synchronized(feedListLock) {
|
||||
adapter.allFeedsCursor.use { cursor ->
|
||||
// feeds = ArrayList(cursor.count)
|
||||
feeds.clear()
|
||||
feedIndex.clear()
|
||||
while (cursor.moveToNext()) {
|
||||
val feed = extractFeedFromCursorRow(cursor)
|
||||
feeds.add(feed)
|
||||
feedIndex[feed.id] = feed
|
||||
}
|
||||
buildTags()
|
||||
}
|
||||
|
@ -110,21 +111,18 @@ object DBReader {
|
|||
* @param items the FeedItems who should have other data loaded
|
||||
*/
|
||||
fun loadAdditionalFeedItemListData(items: List<FeedItem>) {
|
||||
loadTagsOfFeedItemList(items)
|
||||
|
||||
synchronized(feedListLock) {
|
||||
loadFeedDataOfFeedItemList(items)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadTagsOfFeedItemList(items: List<FeedItem>) {
|
||||
val favoriteIds = getFavoriteIDList()
|
||||
val queueIds = getQueueIDList()
|
||||
Logd(TAG, "loadAdditionalFeedItemListData called with items.size: ${items.size}")
|
||||
val favoriteIds = getFavoriteIDSet()
|
||||
val queueIds = getQueueIDSet()
|
||||
|
||||
for (item in items) {
|
||||
if (favoriteIds.contains(item.id)) item.addTag(FeedItem.TAG_FAVORITE)
|
||||
if (queueIds.contains(item.id)) item.addTag(FeedItem.TAG_QUEUE)
|
||||
}
|
||||
|
||||
synchronized(feedListLock) {
|
||||
loadFeedDataOfFeedItemList(items)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,11 +134,12 @@ object DBReader {
|
|||
*/
|
||||
private fun loadFeedDataOfFeedItemList(items: List<FeedItem>) {
|
||||
Logd(TAG, "loadFeedDataOfFeedItemList called")
|
||||
val feedIndex: MutableMap<Long, Feed> = ArrayMap(feeds.size)
|
||||
val feedsCopy = ArrayList(feeds)
|
||||
for (feed in feedsCopy) {
|
||||
feedIndex[feed.id] = feed
|
||||
}
|
||||
// feedIndex is now a static member
|
||||
// val feedIndex: MutableMap<Long, Feed> = ArrayMap(feeds.size)
|
||||
// val feedsCopy = ArrayList(feeds)
|
||||
// for (feed in feedsCopy) {
|
||||
// feedIndex[feed.id] = feed
|
||||
// }
|
||||
for (item in items) {
|
||||
var feed = feedIndex[item.feedId]
|
||||
if (feed == null) {
|
||||
|
@ -168,9 +167,9 @@ object DBReader {
|
|||
return getFeedItemList(feed, filter, SortOrder.DATE_NEW_OLD)
|
||||
}
|
||||
|
||||
fun getFeedItemList(feed: Feed?, filter: FeedItemFilter?, sortOrder: SortOrder?): List<FeedItem> {
|
||||
// Log.d(TAG, "getFeedItemList() called with: feed = [$feed]")
|
||||
|
||||
fun getFeedItemList(feed: Feed?, filter_: FeedItemFilter?, sortOrder: SortOrder?): List<FeedItem> {
|
||||
val filter = filter_ ?: unfiltered()
|
||||
Logd(TAG, "getFeedItemList() called with: ${feed?.title}")
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
try {
|
||||
|
@ -240,8 +239,6 @@ object DBReader {
|
|||
@JvmStatic
|
||||
fun getQueueIDList(): LongList {
|
||||
Logd(TAG, "getQueueIDList() called")
|
||||
// printStackTrace()
|
||||
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
try {
|
||||
|
@ -251,21 +248,39 @@ object DBReader {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getQueueIDList(adapter: PodDBAdapter?): LongList {
|
||||
adapter?.queueIDCursor?.use { cursor ->
|
||||
private fun getQueueIDList(adapter: PodDBAdapter): LongList {
|
||||
adapter.queueIDCursor.use { cursor ->
|
||||
val queueIds = LongList(cursor.count)
|
||||
while (cursor.moveToNext()) {
|
||||
queueIds.add(cursor.getLong(0))
|
||||
}
|
||||
return queueIds
|
||||
}
|
||||
return LongList()
|
||||
// return LongList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getQueueIDSet(): HashSet<Long> {
|
||||
Logd(TAG, "getQueueIDSet() called")
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
try {
|
||||
adapter.queueIDCursor.use { cursor ->
|
||||
val queueIds = HashSet<Long>(cursor.count)
|
||||
while (cursor.moveToNext()) {
|
||||
queueIds.add(cursor.getLong(0))
|
||||
}
|
||||
return queueIds
|
||||
}
|
||||
// return HashSet()
|
||||
} finally {
|
||||
adapter.close()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getQueue(): List<FeedItem> {
|
||||
Logd(TAG, "getQueue() called")
|
||||
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
try {
|
||||
|
@ -275,14 +290,13 @@ object DBReader {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getFavoriteIDList(): LongList {
|
||||
Logd(TAG, "getFavoriteIDList() called")
|
||||
|
||||
private fun getFavoriteIDSet(): HashSet<Long> {
|
||||
Logd(TAG, "getFavoriteIDSet() called")
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
try {
|
||||
adapter.getFavoritesIdsCursor(0, Int.MAX_VALUE).use { cursor ->
|
||||
val favoriteIDs = LongList(cursor.count)
|
||||
val favoriteIDs = HashSet<Long>(cursor.count)
|
||||
while (cursor.moveToNext()) {
|
||||
favoriteIDs.add(cursor.getLong(0))
|
||||
}
|
||||
|
@ -300,7 +314,8 @@ object DBReader {
|
|||
* @param filter The filter describing which episodes to filter out.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getEpisodes(offset: Int, limit: Int, filter: FeedItemFilter?, sortOrder: SortOrder?): List<FeedItem> {
|
||||
fun getEpisodes(offset: Int, limit: Int, filter_: FeedItemFilter?, sortOrder: SortOrder?): List<FeedItem> {
|
||||
val filter = filter_ ?: unfiltered()
|
||||
Logd(TAG, "getEpisodes called with: offset=$offset, limit=$limit")
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
|
@ -316,7 +331,8 @@ object DBReader {
|
|||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getTotalEpisodeCount(filter: FeedItemFilter?): Int {
|
||||
fun getTotalEpisodeCount(filter_: FeedItemFilter?): Int {
|
||||
val filter = filter_ ?: unfiltered()
|
||||
Logd(TAG, "getTotalEpisodeCount called")
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
|
@ -481,11 +497,11 @@ object DBReader {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getFeedItem(itemId: Long, adapter: PodDBAdapter?): FeedItem? {
|
||||
private fun getFeedItem(itemId: Long, adapter: PodDBAdapter): FeedItem? {
|
||||
Logd(TAG, "Loading feeditem with id $itemId")
|
||||
|
||||
var item: FeedItem? = null
|
||||
adapter?.getFeedItemCursor(itemId.toString())?.use { cursor ->
|
||||
adapter.getFeedItemCursor(itemId.toString()).use { cursor ->
|
||||
if (cursor.moveToNext()) {
|
||||
val list = extractItemlistFromCursor(adapter, cursor)
|
||||
if (list.isNotEmpty()) {
|
||||
|
@ -495,7 +511,7 @@ object DBReader {
|
|||
}
|
||||
return item
|
||||
}
|
||||
return null
|
||||
// return null
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -525,12 +541,13 @@ object DBReader {
|
|||
* @return The FeedItem next in queue or null if the FeedItem could not be found.
|
||||
*/
|
||||
fun getNextInQueue(item: FeedItem): FeedItem? {
|
||||
Logd(TAG, "getNextInQueue() called with: itemId = [${item.id}]")
|
||||
Logd(TAG, "*** expensive call getNextInQueue() with: itemId = [${item.id}]")
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
try {
|
||||
var nextItem: FeedItem? = null
|
||||
try {
|
||||
// TODO: these calls are expensive
|
||||
adapter.getNextInQueue(item).use { cursor ->
|
||||
val list = extractItemlistFromCursor(adapter, cursor)
|
||||
if (list.isNotEmpty()) {
|
||||
|
@ -571,14 +588,14 @@ object DBReader {
|
|||
* @return The FeedItem or null if the FeedItem could not be found.
|
||||
* Does NOT load additional attributes like feed or queue state.
|
||||
*/
|
||||
private fun getFeedItemByGuidOrEpisodeUrl(guid: String?, episodeUrl: String, adapter: PodDBAdapter?): FeedItem? {
|
||||
adapter?.getFeedItemCursor(guid, episodeUrl)?.use { cursor ->
|
||||
private fun getFeedItemByGuidOrEpisodeUrl(guid: String?, episodeUrl: String, adapter: PodDBAdapter): FeedItem? {
|
||||
adapter.getFeedItemCursor(guid, episodeUrl).use { cursor ->
|
||||
if (!cursor.moveToNext()) return null
|
||||
val list = extractItemlistFromCursor(adapter, cursor)
|
||||
if (list.isNotEmpty()) return list[0]
|
||||
return null
|
||||
}
|
||||
return null
|
||||
// return null
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -598,9 +615,9 @@ object DBReader {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getImageAuthentication(imageUrl: String, adapter: PodDBAdapter?): String {
|
||||
var credentials = ""
|
||||
adapter?.getImageAuthenticationCursor(imageUrl)?.use { cursor ->
|
||||
private fun getImageAuthentication(imageUrl: String, adapter: PodDBAdapter): String {
|
||||
var credentials: String
|
||||
adapter.getImageAuthenticationCursor(imageUrl).use { cursor ->
|
||||
if (cursor.moveToFirst()) {
|
||||
val username = cursor.getString(0)
|
||||
val password = cursor.getString(1)
|
||||
|
@ -646,10 +663,10 @@ object DBReader {
|
|||
if (cursor.moveToFirst()) {
|
||||
val indexDescription = cursor.getColumnIndex(PodDBAdapter.KEY_DESCRIPTION)
|
||||
val description = cursor.getString(indexDescription)
|
||||
item.setDescriptionIfLonger(description)
|
||||
item.setDescription(description)
|
||||
val indexTranscript = cursor.getColumnIndex(PodDBAdapter.KEY_TRANSCRIPT)
|
||||
val transcript = cursor.getString(indexTranscript)
|
||||
item.setTranscriptIfLonger(transcript)
|
||||
item.setTranscript(transcript)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
@ -681,8 +698,8 @@ object DBReader {
|
|||
}
|
||||
}
|
||||
|
||||
private fun loadChaptersOfFeedItem(adapter: PodDBAdapter?, item: FeedItem): List<Chapter>? {
|
||||
adapter?.getSimpleChaptersOfFeedItemCursor(item)?.use { cursor ->
|
||||
private fun loadChaptersOfFeedItem(adapter: PodDBAdapter, item: FeedItem): List<Chapter>? {
|
||||
adapter.getSimpleChaptersOfFeedItemCursor(item).use { cursor ->
|
||||
val chaptersCount = cursor.count
|
||||
if (chaptersCount == 0) {
|
||||
item.chapters = null
|
||||
|
@ -694,7 +711,7 @@ object DBReader {
|
|||
}
|
||||
return chapters
|
||||
}
|
||||
return null
|
||||
// return null
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -827,7 +844,7 @@ object DBReader {
|
|||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
|
||||
val feedCounters: Map<Long, Int> = adapter.getFeedCounters(feedCounterSetting)
|
||||
val feedCounters: Map<Long, Int> = adapter.getFeedEpisodesCounters(feedCounterSetting)
|
||||
// getFeedList(adapter)
|
||||
|
||||
// TODO:
|
||||
|
@ -901,16 +918,19 @@ object DBReader {
|
|||
}
|
||||
|
||||
val queueSize = adapter.queueSize
|
||||
// getting all items, not only new ones
|
||||
val numNewItems = getTotalEpisodeCount(FeedItemFilter(FeedItemFilter.NEW))
|
||||
val numDownloadedItems = getTotalEpisodeCount(FeedItemFilter(FeedItemFilter.DOWNLOADED))
|
||||
|
||||
val numItems = getTotalEpisodeCount(unfiltered())
|
||||
val numFeeds = feeds.size
|
||||
val items: MutableList<FeedDrawerItem> = ArrayList()
|
||||
for (feed in feeds) {
|
||||
val counter = if (feedCounters.containsKey(feed.id)) feedCounters[feed.id]!! else 0
|
||||
val drawerItem = FeedDrawerItem(feed, feed.id, counter)
|
||||
items.add(drawerItem)
|
||||
}
|
||||
val result = NavDrawerData(items, queueSize, numNewItems, numDownloadedItems, feedCounters, EpisodeCleanupAlgorithmFactory.build().getReclaimableItems())
|
||||
val result = NavDrawerData(items, queueSize, numNewItems, numDownloadedItems, feedCounters,
|
||||
EpisodeCleanupAlgorithmFactory.build().getReclaimableItems(), numItems, numFeeds)
|
||||
adapter.close()
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import ac.mdiq.podcini.storage.model.download.DownloadResult
|
|||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.comparator.FeedItemPubdateComparator
|
||||
import ac.mdiq.podcini.util.event.FeedItemEvent.Companion.updated
|
||||
import ac.mdiq.podcini.util.event.FeedListUpdateEvent
|
||||
|
@ -105,7 +106,7 @@ import java.util.concurrent.*
|
|||
*/
|
||||
@UnstableApi @JvmStatic
|
||||
fun autodownloadUndownloadedItems(context: Context): Future<*> {
|
||||
Log.d(TAG, "autodownloadUndownloadedItems")
|
||||
Logd(TAG, "autodownloadUndownloadedItems")
|
||||
return autodownloadExec.submit(downloadAlgorithm.autoDownloadUndownloadedItems(context))
|
||||
}
|
||||
|
||||
|
@ -190,7 +191,7 @@ import java.util.concurrent.*
|
|||
@JvmStatic
|
||||
@Synchronized
|
||||
fun updateFeed(context: Context, newFeed: Feed, removeUnlistedItems: Boolean): Feed? {
|
||||
Log.d(TAG, "updateFeed called")
|
||||
Logd(TAG, "updateFeed called")
|
||||
var resultFeed: Feed?
|
||||
val unlistedItems: MutableList<FeedItem> = ArrayList()
|
||||
|
||||
|
@ -200,24 +201,24 @@ import java.util.concurrent.*
|
|||
// Look up feed in the feedslist
|
||||
val savedFeed = searchFeedByIdentifyingValueOrID(newFeed)
|
||||
if (savedFeed == null) {
|
||||
Log.d(TAG, "Found no existing Feed with title " + newFeed.title + ". Adding as new one.")
|
||||
Logd(TAG, "Found no existing Feed with title " + newFeed.title + ". Adding as new one.")
|
||||
|
||||
resultFeed = newFeed
|
||||
} else {
|
||||
Log.d(TAG, "Feed with title " + newFeed.title + " already exists. Syncing new with existing one.")
|
||||
Logd(TAG, "Feed with title " + newFeed.title + " already exists. Syncing new with existing one.")
|
||||
newFeed.items.sortWith(FeedItemPubdateComparator())
|
||||
|
||||
if (newFeed.pageNr == savedFeed.pageNr) {
|
||||
if (savedFeed.compareWithOther(newFeed)) {
|
||||
Log.d(TAG, "Feed has updated attribute values. Updating old feed's attributes")
|
||||
Logd(TAG, "Feed has updated attribute values. Updating old feed's attributes")
|
||||
savedFeed.updateFromOther(newFeed)
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "New feed has a higher page number.")
|
||||
Logd(TAG, "New feed has a higher page number.")
|
||||
savedFeed.nextPageLink = newFeed.nextPageLink
|
||||
}
|
||||
if (savedFeed.preferences != null && savedFeed.preferences!!.compareWithOther(newFeed.preferences)) {
|
||||
Log.d(TAG, "Feed has updated preferences. Updating old feed's preferences")
|
||||
Logd(TAG, "Feed has updated preferences. Updating old feed's preferences")
|
||||
savedFeed.preferences!!.updateFromOther(newFeed.preferences)
|
||||
}
|
||||
|
||||
|
@ -249,7 +250,7 @@ import java.util.concurrent.*
|
|||
if (!newFeed.isLocalFeed && oldItem == null) {
|
||||
oldItem = searchFeedItemGuessDuplicate(savedFeed.items, item)
|
||||
if (oldItem != null) {
|
||||
Log.d(TAG, "Repaired duplicate: $oldItem, $item")
|
||||
Logd(TAG, "Repaired duplicate: $oldItem, $item")
|
||||
DBWriter.addDownloadStatus(DownloadResult(savedFeed,
|
||||
item.title?:"", DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
|
||||
"""
|
||||
|
@ -279,7 +280,7 @@ import java.util.concurrent.*
|
|||
if (oldItem != null) {
|
||||
oldItem.updateFromOther(item)
|
||||
} else {
|
||||
Log.d(TAG, "Found new item: " + item.title)
|
||||
Logd(TAG, "Found new item: " + item.title)
|
||||
item.feed = savedFeed
|
||||
|
||||
if (idx >= savedFeed.items.size) savedFeed.items.add(item)
|
||||
|
@ -287,7 +288,7 @@ import java.util.concurrent.*
|
|||
|
||||
val pubDate = item.getPubDate()
|
||||
if (pubDate == null || priorMostRecentDate == null || priorMostRecentDate.before(pubDate) || priorMostRecentDate == pubDate) {
|
||||
Log.d(TAG, "Marking item published on $pubDate new, prior most recent date = $priorMostRecentDate")
|
||||
Logd(TAG, "Marking item published on $pubDate new, prior most recent date = $priorMostRecentDate")
|
||||
item.setNew()
|
||||
}
|
||||
}
|
||||
|
@ -357,7 +358,7 @@ import java.util.concurrent.*
|
|||
*/
|
||||
@JvmStatic
|
||||
fun searchFeedItems(feedID: Long, query: String): FutureTask<List<FeedItem>> {
|
||||
Log.d(TAG, "searchFeedItems called")
|
||||
Logd(TAG, "searchFeedItems called")
|
||||
return FutureTask(object : QueryTask<List<FeedItem>>() {
|
||||
override fun execute(adapter: PodDBAdapter?) {
|
||||
val searchResult = adapter?.searchItems(feedID, query)
|
||||
|
|
|
@ -43,6 +43,7 @@ import ac.mdiq.podcini.preferences.UserPreferences.enqueueLocation
|
|||
import ac.mdiq.podcini.preferences.UserPreferences.isQueueKeepSorted
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.queueKeepSortedOrder
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.shouldDeleteRemoveFromQueue
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.showStackTrace
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.io.File
|
||||
|
@ -98,7 +99,7 @@ import java.util.concurrent.TimeUnit
|
|||
*/
|
||||
@JvmStatic
|
||||
fun deleteFeedMediaOfItem(context: Context, mediaId: Long): Future<*> {
|
||||
Log.d(TAG, "deleteFeedMediaOfItem called")
|
||||
Logd(TAG, "deleteFeedMediaOfItem called")
|
||||
return runOnDbThread {
|
||||
val media = getFeedMedia(mediaId)
|
||||
if (media != null) {
|
||||
|
@ -208,7 +209,7 @@ import java.util.concurrent.TimeUnit
|
|||
* Deleting media also removes the download log entries.
|
||||
*/
|
||||
fun deleteFeedItems(context: Context, items: List<FeedItem>): Future<*> {
|
||||
Log.d(TAG, "deleteFeedItems called")
|
||||
Logd(TAG, "deleteFeedItems called")
|
||||
return runOnDbThread { deleteFeedItemsSynchronous(context, items) }
|
||||
}
|
||||
|
||||
|
@ -295,7 +296,7 @@ import java.util.concurrent.TimeUnit
|
|||
fun addItemToPlaybackHistory(media: FeedMedia?, date: Date? = Date()): Future<*> {
|
||||
return runOnDbThread {
|
||||
if (media != null) {
|
||||
Log.d(TAG, "Adding item to playback history")
|
||||
Logd(TAG, "Adding item to playback history")
|
||||
media.setPlaybackCompletionDate(date)
|
||||
|
||||
val adapter = getInstance()
|
||||
|
@ -313,7 +314,7 @@ import java.util.concurrent.TimeUnit
|
|||
* @param status The DownloadStatus object.
|
||||
*/
|
||||
fun addDownloadStatus(status: DownloadResult?): Future<*> {
|
||||
Log.d(TAG, "addDownloadStatus called")
|
||||
Logd(TAG, "addDownloadStatus called")
|
||||
return runOnDbThread {
|
||||
if (status != null) {
|
||||
val adapter = getInstance()
|
||||
|
@ -336,7 +337,7 @@ import java.util.concurrent.TimeUnit
|
|||
* @throws IndexOutOfBoundsException if index < 0 || index >= queue.size()
|
||||
*/
|
||||
@UnstableApi fun addQueueItemAt(context: Context, itemId: Long, index: Int, performAutoDownload: Boolean): Future<*> {
|
||||
Log.d(TAG, "addQueueItemAt called")
|
||||
Logd(TAG, "addQueueItemAt called")
|
||||
return runOnDbThread {
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
|
@ -366,7 +367,7 @@ import java.util.concurrent.TimeUnit
|
|||
}
|
||||
|
||||
fun addQueueItem(context: Context, markAsUnplayed: Boolean, vararg items: FeedItem): Future<*> {
|
||||
Log.d(TAG, "addQueueItem called")
|
||||
Logd(TAG, "addQueueItem called")
|
||||
val itemIds = LongList(items.size)
|
||||
for (item in items) {
|
||||
if (!item.hasMedia()) continue
|
||||
|
@ -398,7 +399,7 @@ import java.util.concurrent.TimeUnit
|
|||
* @param itemIds IDs of the FeedItem objects that should be added to the queue.
|
||||
*/
|
||||
@UnstableApi fun addQueueItem(context: Context, performAutoDownload: Boolean, markAsUnplayed: Boolean, vararg itemIds: Long): Future<*> {
|
||||
Log.d(TAG, "addQueueItem(context ...) called")
|
||||
Logd(TAG, "addQueueItem(context ...) called")
|
||||
return runOnDbThread {
|
||||
if (itemIds.isEmpty()) return@runOnDbThread
|
||||
|
||||
|
@ -500,7 +501,7 @@ import java.util.concurrent.TimeUnit
|
|||
}
|
||||
|
||||
@UnstableApi private fun removeQueueItemSynchronous(context: Context, performAutoDownload: Boolean, vararg itemIds: Long) {
|
||||
Log.d(TAG, "removeQueueItemSynchronous called $itemIds")
|
||||
Logd(TAG, "removeQueueItemSynchronous called $itemIds")
|
||||
if (itemIds.isEmpty()) return
|
||||
showStackTrace()
|
||||
|
||||
|
@ -515,7 +516,7 @@ import java.util.concurrent.TimeUnit
|
|||
val position = indexInItemList(queue, itemId)
|
||||
if (position >= 0) {
|
||||
val item = getFeedItem(itemId)
|
||||
Log.d(TAG, "removing item from queue: ${item?.title}")
|
||||
Logd(TAG, "removing item from queue: ${item?.title}")
|
||||
if (item == null) {
|
||||
Log.e(TAG, "removeQueueItem - item in queue but somehow cannot be loaded. Item ignored. It should never happen. id:$itemId")
|
||||
continue
|
||||
|
@ -776,7 +777,7 @@ import java.util.concurrent.TimeUnit
|
|||
* @param media The FeedMedia object.
|
||||
*/
|
||||
fun persistFeedMedia(media: FeedMedia): Future<*> {
|
||||
Log.d(TAG, "persistFeedMedia called")
|
||||
Logd(TAG, "persistFeedMedia called")
|
||||
return runOnDbThread {
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
|
@ -792,7 +793,7 @@ import java.util.concurrent.TimeUnit
|
|||
*/
|
||||
@JvmStatic
|
||||
fun persistFeedMediaPlaybackInfo(media: FeedMedia?): Future<*> {
|
||||
Log.d(TAG, "persistFeedMediaPlaybackInfo called")
|
||||
Logd(TAG, "persistFeedMediaPlaybackInfo called")
|
||||
return runOnDbThread {
|
||||
if (media != null) {
|
||||
val adapter = getInstance()
|
||||
|
@ -811,7 +812,7 @@ import java.util.concurrent.TimeUnit
|
|||
*/
|
||||
@JvmStatic
|
||||
fun persistFeedItem(item: FeedItem?): Future<*> {
|
||||
Log.d(TAG, "persistFeedItem called")
|
||||
Logd(TAG, "persistFeedItem called")
|
||||
return runOnDbThread {
|
||||
if (item != null) {
|
||||
val adapter = getInstance()
|
||||
|
@ -827,7 +828,7 @@ import java.util.concurrent.TimeUnit
|
|||
* Updates download URL of a feed
|
||||
*/
|
||||
fun updateFeedDownloadURL(original: String, updated: String): Future<*> {
|
||||
Log.d(TAG, "updateFeedDownloadURL(original: $original, updated: $updated)")
|
||||
Logd(TAG, "updateFeedDownloadURL(original: $original, updated: $updated)")
|
||||
return runOnDbThread {
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
|
@ -856,7 +857,7 @@ import java.util.concurrent.TimeUnit
|
|||
}
|
||||
|
||||
private fun indexInItemList(items: List<FeedItem?>, itemId: Long): Int {
|
||||
Log.d(TAG, "indexInItemList called")
|
||||
Logd(TAG, "indexInItemList called")
|
||||
for (i in items.indices) {
|
||||
val item = items[i]
|
||||
if (item?.id == itemId) return i
|
||||
|
@ -921,7 +922,7 @@ import java.util.concurrent.TimeUnit
|
|||
* @param filterValues Values that represent properties to filter by
|
||||
*/
|
||||
fun persistFeedItemsFilter(feedId: Long, filterValues: Set<String>): Future<*> {
|
||||
Log.d(TAG, "persistFeedItemsFilter() called with: feedId = [$feedId], filterValues = [$filterValues]")
|
||||
Logd(TAG, "persistFeedItemsFilter() called with: feedId = [$feedId], filterValues = [$filterValues]")
|
||||
return runOnDbThread {
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.os.ParcelFileDescriptor
|
|||
import android.text.format.Formatter
|
||||
import android.util.Log
|
||||
import ac.mdiq.podcini.storage.database.PodDBAdapter
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.FileInputStream
|
||||
|
@ -39,7 +40,7 @@ object DatabaseTransporter {
|
|||
try {
|
||||
pfd.close()
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "Unable to close ParcelFileDescriptor")
|
||||
Logd(TAG, "Unable to close ParcelFileDescriptor")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,10 @@ class NavDrawerData(@JvmField val items: List<FeedDrawerItem>,
|
|||
@JvmField val numNewItems: Int,
|
||||
val numDownloadedItems: Int,
|
||||
val feedCounters: Map<Long, Int>,
|
||||
val reclaimableSpace: Int
|
||||
) {
|
||||
val reclaimableSpace: Int,
|
||||
@JvmField val numItems: Int,
|
||||
@JvmField val numFeeds: Int) {
|
||||
|
||||
class FeedDrawerItem(val feed: Feed, val id: Long, val counter: Int) {
|
||||
var layer: Int = 0
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import ac.mdiq.podcini.storage.DBReader.getFeedList
|
|||
import ac.mdiq.podcini.storage.DBTasks.updateFeed
|
||||
import ac.mdiq.podcini.net.download.FeedUpdateManager.runOnce
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import org.apache.commons.io.IOUtils
|
||||
|
@ -40,7 +41,7 @@ class OpmlBackupAgent : BackupAgentHelper() {
|
|||
private var mChecksum: ByteArray = byteArrayOf()
|
||||
|
||||
override fun performBackup(oldState: ParcelFileDescriptor, data: BackupDataOutput, newState: ParcelFileDescriptor) {
|
||||
Log.d(TAG, "Performing backup")
|
||||
Logd(TAG, "Performing backup")
|
||||
val byteStream = ByteArrayOutputStream()
|
||||
var digester: MessageDigest? = null
|
||||
var writer: Writer
|
||||
|
@ -59,7 +60,7 @@ class OpmlBackupAgent : BackupAgentHelper() {
|
|||
// Compare checksum of new and old file to see if we need to perform a backup at all
|
||||
if (digester != null) {
|
||||
val newChecksum = digester.digest()
|
||||
Log.d(TAG, "New checksum: " + BigInteger(1, newChecksum).toString(16))
|
||||
Logd(TAG, "New checksum: " + BigInteger(1, newChecksum).toString(16))
|
||||
|
||||
// Get the old checksum
|
||||
if (oldState != null) {
|
||||
|
@ -69,10 +70,10 @@ class OpmlBackupAgent : BackupAgentHelper() {
|
|||
if (len != -1) {
|
||||
val oldChecksum = ByteArray(len)
|
||||
IOUtils.read(inState, oldChecksum, 0, len)
|
||||
Log.d(TAG, "Old checksum: " + BigInteger(1, oldChecksum).toString(16))
|
||||
Logd(TAG, "Old checksum: " + BigInteger(1, oldChecksum).toString(16))
|
||||
|
||||
if (oldChecksum.contentEquals(newChecksum)) {
|
||||
Log.d(TAG, "Checksums are the same; won't backup")
|
||||
Logd(TAG, "Checksums are the same; won't backup")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ class OpmlBackupAgent : BackupAgentHelper() {
|
|||
writeNewStateDescription(newState, newChecksum)
|
||||
}
|
||||
|
||||
Log.d(TAG, "Backing up OPML")
|
||||
Logd(TAG, "Backing up OPML")
|
||||
val bytes = byteStream.toByteArray()
|
||||
data.writeEntityHeader(OPML_ENTITY_KEY, bytes.size)
|
||||
data.writeEntityData(bytes, bytes.size)
|
||||
|
@ -93,10 +94,10 @@ class OpmlBackupAgent : BackupAgentHelper() {
|
|||
}
|
||||
|
||||
@OptIn(UnstableApi::class) override fun restoreEntity(data: BackupDataInputStream) {
|
||||
Log.d(TAG, "Backup restore")
|
||||
Logd(TAG, "Backup restore")
|
||||
|
||||
if (OPML_ENTITY_KEY != data.key) {
|
||||
Log.d(TAG, "Unknown entity key: " + data.key)
|
||||
Logd(TAG, "Unknown entity key: " + data.key)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.media.MediaMetadataRetriever
|
|||
import android.util.Log
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedPreferences
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
|
||||
internal object DBUpgrader {
|
||||
/**
|
||||
|
@ -155,7 +156,7 @@ internal object DBUpgrader {
|
|||
+ PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_POSITION + " = 0 AND " // not partially played
|
||||
+ PodDBAdapter.TABLE_NAME_QUEUE + "." + PodDBAdapter.KEY_ID + " IS NULL") // not in queue
|
||||
val sql = ("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + " SET " + PodDBAdapter.KEY_READ + "=" + FeedItem.NEW + " WHERE " + PodDBAdapter.KEY_ID + " IN (" + selectNew + ")")
|
||||
Log.d("Migration", "SQL: $sql")
|
||||
Logd("Migration", "SQL: $sql")
|
||||
db.execSQL(sql)
|
||||
}
|
||||
if (oldVersion <= 17) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import ac.mdiq.podcini.storage.model.feed.*
|
|||
import ac.mdiq.podcini.storage.model.feed.SortOrder.Companion.toCodeString
|
||||
import ac.mdiq.podcini.storage.database.mapper.FeedItemFilterQuery.generateFrom
|
||||
import ac.mdiq.podcini.storage.database.mapper.FeedItemSortQuery.generateFrom
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import org.apache.commons.io.FileUtils
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
@ -77,20 +78,17 @@ class PodDBAdapter private constructor() {
|
|||
|
||||
values.put(KEY_IS_PAGED, feed.isPaged)
|
||||
values.put(KEY_NEXT_PAGE_LINK, feed.nextPageLink)
|
||||
if (!feed.itemFilter?.values.isNullOrEmpty()) {
|
||||
values.put(KEY_HIDE, TextUtils.join(",", feed.itemFilter!!.values))
|
||||
} else {
|
||||
values.put(KEY_HIDE, "")
|
||||
}
|
||||
values.put(KEY_SORT_ORDER, toCodeString(
|
||||
feed.sortOrder))
|
||||
if (!feed.itemFilter?.values.isNullOrEmpty()) values.put(KEY_HIDE, TextUtils.join(",", feed.itemFilter!!.values))
|
||||
else values.put(KEY_HIDE, "")
|
||||
|
||||
values.put(KEY_SORT_ORDER, toCodeString(feed.sortOrder))
|
||||
values.put(KEY_LAST_UPDATE_FAILED, feed.hasLastUpdateFailed())
|
||||
if (feed.id == 0L) {
|
||||
// Create new entry
|
||||
Log.d(this.toString(), "Inserting new Feed into db")
|
||||
Logd(this.toString(), "Inserting new Feed into db")
|
||||
feed.id = db.insert(TABLE_NAME_FEEDS, null, values)
|
||||
} else {
|
||||
Log.d(this.toString(), "Updating existing Feed in db")
|
||||
Logd(this.toString(), "Updating existing Feed in db")
|
||||
db.update(TABLE_NAME_FEEDS, values, "$KEY_ID=?", arrayOf(feed.id.toString()))
|
||||
}
|
||||
return feed.id
|
||||
|
@ -120,7 +118,7 @@ class PodDBAdapter private constructor() {
|
|||
|
||||
fun setFeedItemFilter(feedId: Long, filterValues: Set<String?>?) {
|
||||
val valuesList = TextUtils.join(",", filterValues!!)
|
||||
Log.d(TAG, String.format(Locale.US, "setFeedItemFilter() called with: feedId = [%d], filterValues = [%s]", feedId, valuesList))
|
||||
Logd(TAG, String.format(Locale.US, "setFeedItemFilter() called with: feedId = [%d], filterValues = [%s]", feedId, valuesList))
|
||||
val values = ContentValues()
|
||||
values.put(KEY_HIDE, valuesList)
|
||||
db.update(TABLE_NAME_FEEDS, values, "$KEY_ID=?", arrayOf(feedId.toString()))
|
||||
|
@ -419,7 +417,7 @@ class PodDBAdapter private constructor() {
|
|||
fun addFavoriteItem(item: FeedItem) {
|
||||
// don't add an item that's already there...
|
||||
if (isItemInFavorites(item)) {
|
||||
Log.d(TAG, "item already in favorites")
|
||||
Logd(TAG, "item already in favorites")
|
||||
return
|
||||
}
|
||||
val values = ContentValues()
|
||||
|
@ -570,7 +568,7 @@ class PodDBAdapter private constructor() {
|
|||
* @param feed The feed you want to get the FeedItems from.
|
||||
* @return The cursor of the query
|
||||
*/
|
||||
fun getItemsOfFeedCursor(feed: Feed, filter: FeedItemFilter?): Cursor {
|
||||
fun getItemsOfFeedCursor(feed: Feed, filter: FeedItemFilter): Cursor {
|
||||
val filterQuery = generateFrom(filter!!)
|
||||
val whereClauseAnd = if ("" == filterQuery) "" else " AND $filterQuery"
|
||||
val query = ("$SELECT_FEED_ITEMS_AND_MEDIA WHERE $TABLE_NAME_FEED_ITEMS.$KEY_FEED=${feed.id}$whereClauseAnd")
|
||||
|
@ -649,7 +647,7 @@ class PodDBAdapter private constructor() {
|
|||
db.execSQL(sql)
|
||||
}
|
||||
|
||||
fun getEpisodesCursor(offset: Int, limit: Int, filter: FeedItemFilter?, sortOrder: SortOrder?): Cursor {
|
||||
fun getEpisodesCursor(offset: Int, limit: Int, filter: FeedItemFilter, sortOrder: SortOrder?): Cursor {
|
||||
val orderByQuery = generateFrom(sortOrder)
|
||||
val filterQuery = generateFrom(filter!!)
|
||||
val whereClause = if ("" == filterQuery) "" else " WHERE $filterQuery"
|
||||
|
@ -657,7 +655,7 @@ class PodDBAdapter private constructor() {
|
|||
return db.rawQuery(query, null)
|
||||
}
|
||||
|
||||
fun getEpisodeCountCursor(filter: FeedItemFilter?): Cursor {
|
||||
fun getEpisodeCountCursor(filter: FeedItemFilter): Cursor {
|
||||
val filterQuery = generateFrom(filter!!)
|
||||
val whereClause = if ("" == filterQuery) "" else " WHERE $filterQuery"
|
||||
val query = ("SELECT count($TABLE_NAME_FEED_ITEMS.$KEY_ID) FROM $TABLE_NAME_FEED_ITEMS$JOIN_FEED_ITEM_AND_MEDIA$whereClause")
|
||||
|
@ -787,9 +785,9 @@ class PodDBAdapter private constructor() {
|
|||
return result
|
||||
}
|
||||
|
||||
fun getFeedCounters(setting: FeedCounter?, vararg feedIds: Long): Map<Long, Int> {
|
||||
fun getFeedEpisodesCounters(setting: FeedCounter?, vararg feedIds: Long): Map<Long, Int> {
|
||||
val whereRead = when (setting) {
|
||||
// FeedCounter.SHOW_NEW -> KEY_READ + "=" + FeedItem.NEW
|
||||
FeedCounter.SHOW_NEW -> KEY_READ + "=" + FeedItem.NEW
|
||||
FeedCounter.SHOW_UNPLAYED -> ("(" + KEY_READ + "=" + FeedItem.NEW + " OR " + KEY_READ + "=" + FeedItem.UNPLAYED + ")")
|
||||
FeedCounter.SHOW_DOWNLOADED -> "$KEY_DOWNLOADED=1"
|
||||
FeedCounter.SHOW_DOWNLOADED_UNPLAYED -> ("(" + KEY_READ + "=" + FeedItem.NEW
|
||||
|
@ -798,10 +796,10 @@ class PodDBAdapter private constructor() {
|
|||
FeedCounter.SHOW_NONE -> return HashMap()
|
||||
else -> return HashMap()
|
||||
}
|
||||
return conditionalFeedCounterRead(whereRead, *feedIds)
|
||||
return conditionalFeedEpisodesCounterRead(whereRead, *feedIds)
|
||||
}
|
||||
|
||||
private fun conditionalFeedCounterRead(whereRead: String, vararg feedIds: Long): Map<Long, Int> {
|
||||
private fun conditionalFeedEpisodesCounterRead(whereRead: String, vararg feedIds: Long): Map<Long, Int> {
|
||||
var limitFeeds = ""
|
||||
if (feedIds.isNotEmpty()) {
|
||||
// work around TextUtils.join wanting only boxed items
|
||||
|
@ -833,7 +831,7 @@ class PodDBAdapter private constructor() {
|
|||
|
||||
fun getPlayedEpisodesCounters(vararg feedIds: Long): Map<Long, Int> {
|
||||
val whereRead = KEY_READ + "=" + FeedItem.PLAYED
|
||||
return conditionalFeedCounterRead(whereRead, *feedIds)
|
||||
return conditionalFeedEpisodesCounterRead(whereRead, *feedIds)
|
||||
}
|
||||
|
||||
val mostRecentItemDates: Map<Long, Long>
|
||||
|
@ -968,9 +966,9 @@ class PodDBAdapter private constructor() {
|
|||
val backupFile = File(backupFolder, "CorruptedDatabaseBackup.db")
|
||||
try {
|
||||
FileUtils.copyFile(dbPath, backupFile)
|
||||
Log.d(TAG, "Dumped database to " + backupFile.path)
|
||||
Logd(TAG, "Dumped database to " + backupFile.path)
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, Log.getStackTraceString(e))
|
||||
Logd(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
|
||||
DefaultDatabaseErrorHandler().onCorruption(db) // This deletes the database
|
||||
|
|
|
@ -8,6 +8,7 @@ import ac.mdiq.podcini.storage.model.feed.Feed
|
|||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
||||
import ac.mdiq.podcini.storage.model.feed.SortOrder
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.IOException
|
||||
import java.io.Writer
|
||||
|
@ -17,7 +18,7 @@ import java.util.*
|
|||
class FavoritesWriter : ExportWriter {
|
||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||
override fun writeDocument(feeds: List<Feed?>?, writer: Writer?, context: Context) {
|
||||
Log.d(TAG, "Starting to write document")
|
||||
Logd(TAG, "Starting to write document")
|
||||
|
||||
val templateStream = context!!.assets.open("html-export-template.html")
|
||||
var template = IOUtils.toString(templateStream, UTF_8)
|
||||
|
@ -50,7 +51,7 @@ class FavoritesWriter : ExportWriter {
|
|||
|
||||
writer.append(templateParts[1])
|
||||
|
||||
Log.d(TAG, "Finished writing document")
|
||||
Logd(TAG, "Finished writing document")
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.util.Log
|
||||
import ac.mdiq.podcini.storage.export.ExportWriter
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.IOException
|
||||
import java.io.Writer
|
||||
|
@ -16,7 +17,7 @@ class HtmlWriter : ExportWriter {
|
|||
*/
|
||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||
override fun writeDocument(feeds: List<Feed?>?, writer: Writer?, context: Context) {
|
||||
Log.d(TAG, "Starting to write document")
|
||||
Logd(TAG, "Starting to write document")
|
||||
|
||||
val templateStream = context!!.assets.open("html-export-template.html")
|
||||
var template = IOUtils.toString(templateStream, "UTF-8")
|
||||
|
@ -36,7 +37,7 @@ class HtmlWriter : ExportWriter {
|
|||
writer.append("\">Feed</a></span></p></div></li>\n")
|
||||
}
|
||||
writer.append(templateParts[1])
|
||||
Log.d(TAG, "Finished writing document")
|
||||
Logd(TAG, "Finished writing document")
|
||||
}
|
||||
|
||||
override fun fileExtension(): String {
|
||||
|
|
|
@ -7,6 +7,7 @@ import ac.mdiq.podcini.storage.export.CommonSymbols
|
|||
import ac.mdiq.podcini.storage.export.ExportWriter
|
||||
import ac.mdiq.podcini.util.DateFormatter.formatRfc822Date
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import java.io.IOException
|
||||
import java.io.Writer
|
||||
import java.util.*
|
||||
|
@ -19,7 +20,7 @@ class OpmlWriter : ExportWriter {
|
|||
*/
|
||||
@Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class)
|
||||
override fun writeDocument(feeds: List<Feed?>?, writer: Writer?, context: Context) {
|
||||
Log.d(TAG, "Starting to write document")
|
||||
Logd(TAG, "Starting to write document")
|
||||
val xs = Xml.newSerializer()
|
||||
xs.setFeature(CommonSymbols.XML_FEATURE_INDENT_OUTPUT, true)
|
||||
xs.setOutput(writer)
|
||||
|
@ -50,7 +51,7 @@ class OpmlWriter : ExportWriter {
|
|||
xs.endTag(null, CommonSymbols.BODY)
|
||||
xs.endTag(null, OpmlSymbols.OPML)
|
||||
xs.endDocument()
|
||||
Log.d(TAG, "Finished writing document")
|
||||
Logd(TAG, "Finished writing document")
|
||||
}
|
||||
|
||||
override fun fileExtension(): String {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package ac.mdiq.podcini.storage.model.feed
|
||||
|
||||
enum class FeedCounter(val id: Int) {
|
||||
// SHOW_NEW(1),
|
||||
SHOW_NEW(1),
|
||||
SHOW_UNPLAYED(2),
|
||||
SHOW_NONE(3),
|
||||
SHOW_DOWNLOADED(4),
|
||||
|
|
|
@ -221,6 +221,14 @@ class FeedItem : FeedComponent, Serializable {
|
|||
val isInProgress: Boolean
|
||||
get() = (media != null && media!!.isInProgress)
|
||||
|
||||
fun setDescription(newDescription: String?) {
|
||||
this.description = newDescription
|
||||
}
|
||||
|
||||
fun setTranscript(newTranscript: String?) {
|
||||
this.transcript = newTranscript
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this item's description property if the given argument is longer than the already stored description
|
||||
* @param newDescription The new item description, content:encoded, itunes:description, etc.
|
||||
|
|
|
@ -11,7 +11,7 @@ import android.widget.ImageView
|
|||
import androidx.media3.common.util.UnstableApi
|
||||
|
||||
abstract class ItemActionButton internal constructor(@JvmField var item: FeedItem) {
|
||||
val TAG = this::class.simpleName
|
||||
val TAG = this::class.simpleName ?: "ItemActionButton"
|
||||
|
||||
abstract fun getLabel(): Int
|
||||
|
||||
|
|
|
@ -32,10 +32,10 @@ class PlayActionButton(item: FeedItem) : ItemActionButton(item) {
|
|||
return
|
||||
}
|
||||
|
||||
EventBus.getDefault().post(StartPlayEvent(item))
|
||||
PlaybackServiceStarter(context, media)
|
||||
.callEvenIfRunning(true)
|
||||
.start()
|
||||
EventBus.getDefault().post(StartPlayEvent(item))
|
||||
|
||||
if (media.getMediaType() == MediaType.VIDEO) context.startActivity(getPlayerActivityIntent(context, MediaType.VIDEO))
|
||||
}
|
||||
|
|
|
@ -36,11 +36,11 @@ class StreamActionButton(item: FeedItem) : ItemActionButton(item) {
|
|||
return
|
||||
}
|
||||
|
||||
EventBus.getDefault().post(StartPlayEvent(item))
|
||||
PlaybackServiceStarter(context, media)
|
||||
.shouldStreamThisTime(true)
|
||||
.callEvenIfRunning(true)
|
||||
.start()
|
||||
EventBus.getDefault().post(StartPlayEvent(item))
|
||||
|
||||
if (media.getMediaType() == MediaType.VIDEO) context.startActivity(getPlayerActivityIntent(context, MediaType.VIDEO))
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
|||
import ac.mdiq.podcini.ui.fragment.FeedItemlistFragment.Companion.tts
|
||||
import ac.mdiq.podcini.ui.fragment.FeedItemlistFragment.Companion.ttsReady
|
||||
import ac.mdiq.podcini.ui.fragment.FeedItemlistFragment.Companion.ttsWorking
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.NetworkUtils.fetchHtmlSource
|
||||
import ac.mdiq.podcini.util.event.FeedItemEvent.Companion.updated
|
||||
import android.content.Context
|
||||
|
@ -44,7 +45,7 @@ class TTSActionButton(item: FeedItem) : ItemActionButton(item) {
|
|||
}
|
||||
|
||||
@OptIn(UnstableApi::class) override fun onClick(context: Context) {
|
||||
Log.d("TTSActionButton", "onClick called")
|
||||
Logd("TTSActionButton", "onClick called")
|
||||
if (item.link.isNullOrEmpty()) {
|
||||
Toast.makeText(context, R.string.episode_has_no_content, Toast.LENGTH_LONG).show()
|
||||
return
|
||||
|
@ -61,7 +62,7 @@ class TTSActionButton(item: FeedItem) : ItemActionButton(item) {
|
|||
readerText = article.textContent
|
||||
item.setTranscriptIfLonger(article.contentWithDocumentsCharsetOrUtf8)
|
||||
persistFeedItem(item)
|
||||
Log.d(TAG, "readability4J: ${readerText?.substring(max(0, readerText!!.length-100), readerText!!.length)}")
|
||||
Logd(TAG, "readability4J: ${readerText?.substring(max(0, readerText!!.length-100), readerText!!.length)}")
|
||||
}
|
||||
} else readerText = HtmlCompat.fromHtml(item.transcript!!, HtmlCompat.FROM_HTML_MODE_COMPACT).toString()
|
||||
processing = 0.1f
|
||||
|
@ -87,7 +88,7 @@ class TTSActionButton(item: FeedItem) : ItemActionButton(item) {
|
|||
override fun onStart(utteranceId: String?) {}
|
||||
override fun onDone(utteranceId: String?) {
|
||||
j++
|
||||
Log.d(TAG, "onDone ${mediaFile.length()} $utteranceId")
|
||||
Logd(TAG, "onDone ${mediaFile.length()} $utteranceId")
|
||||
}
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onError(utteranceId: String) {
|
||||
|
@ -100,20 +101,20 @@ class TTSActionButton(item: FeedItem) : ItemActionButton(item) {
|
|||
}
|
||||
})
|
||||
|
||||
Log.d(TAG, "readerText: ${readerText?.length}")
|
||||
Logd(TAG, "readerText: ${readerText?.length}")
|
||||
var startIndex = 0
|
||||
var i = 0
|
||||
val parts = mutableListOf<String>()
|
||||
val chunkLength = getMaxSpeechInputLength()
|
||||
var status = TextToSpeech.ERROR
|
||||
while (startIndex < readerText!!.length) {
|
||||
Log.d(TAG, "working on chunk $i $startIndex")
|
||||
Logd(TAG, "working on chunk $i $startIndex")
|
||||
val endIndex = minOf(startIndex + chunkLength, readerText!!.length)
|
||||
val chunk = readerText!!.substring(startIndex, endIndex)
|
||||
val tempFile = File.createTempFile("tts_temp_${i}_", ".wav")
|
||||
parts.add(tempFile.absolutePath)
|
||||
status = tts?.synthesizeToFile(chunk, null, tempFile, tempFile.absolutePath) ?: 0
|
||||
Log.d(TAG, "status: $status chunk: ${chunk.substring(0, min(80, chunk.length))}")
|
||||
Logd(TAG, "status: $status chunk: ${chunk.substring(0, min(80, chunk.length))}")
|
||||
if (status == TextToSpeech.ERROR) {
|
||||
withContext(Dispatchers.Main) { Toast.makeText(context, "Error generating audio file $tempFile.absolutePath", Toast.LENGTH_LONG).show() }
|
||||
break
|
||||
|
@ -138,7 +139,7 @@ class TTSActionButton(item: FeedItem) : ItemActionButton(item) {
|
|||
// }
|
||||
// }
|
||||
val mFilename = mediaFile.absolutePath
|
||||
Log.d(TAG, "saving TTS to file $mFilename")
|
||||
Logd(TAG, "saving TTS to file $mFilename")
|
||||
val media = FeedMedia(item, null, 0, "audio/*")
|
||||
media.setFile_url(mFilename)
|
||||
media.setDownloaded(true)
|
||||
|
|
|
@ -186,7 +186,7 @@ object FeedItemMenuHandler {
|
|||
shareDialog.show((fragment.requireActivity().supportFragmentManager), "ShareEpisodeDialog")
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "Unknown menuItemId: $menuItemId")
|
||||
Logd(TAG, "Unknown menuItemId: $menuItemId")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ object FeedItemMenuHandler {
|
|||
fun markReadWithUndo(fragment: Fragment, item: FeedItem?, playState: Int, showSnackbar: Boolean) {
|
||||
if (item == null) return
|
||||
|
||||
Log.d(TAG, "markReadWithUndo(" + item.id + ")")
|
||||
Logd(TAG, "markReadWithUndo(" + item.id + ")")
|
||||
// we're marking it as unplayed since the user didn't actually play it
|
||||
// but they don't want it considered 'NEW' anymore
|
||||
DBWriter.markItemPlayed(playState, item.id)
|
||||
|
|
|
@ -5,6 +5,7 @@ import ac.mdiq.podcini.databinding.BugReportBinding
|
|||
import ac.mdiq.podcini.preferences.ThemeSwitcher.getTheme
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.getDataFolder
|
||||
import ac.mdiq.podcini.util.IntentUtils.openInBrowser
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.error.CrashReportWriter
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
|
@ -43,7 +44,7 @@ class BugReportActivity : AppCompatActivity() {
|
|||
try {
|
||||
val crashFile = CrashReportWriter.file
|
||||
if (crashFile.exists()) stacktrace = IOUtils.toString(FileInputStream(crashFile), Charset.forName("UTF-8"))
|
||||
else Log.d(TAG, stacktrace)
|
||||
else Logd(TAG, stacktrace)
|
||||
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
|
|
|
@ -277,8 +277,14 @@ class MainActivity : CastEnabledActivity() {
|
|||
override fun onStateChanged(view: View, state: Int) {
|
||||
Logd(TAG, "bottomSheet onStateChanged $state")
|
||||
when (state) {
|
||||
BottomSheetBehavior.STATE_COLLAPSED -> onSlide(view,0.0f)
|
||||
BottomSheetBehavior.STATE_COLLAPSED -> {
|
||||
audioPlayerFragment.isCollapsed = true
|
||||
// audioPlayerFragment.updateUi()
|
||||
onSlide(view,0.0f)
|
||||
}
|
||||
BottomSheetBehavior.STATE_EXPANDED -> {
|
||||
audioPlayerFragment.isCollapsed = false
|
||||
// audioPlayerFragment.updateUi()
|
||||
audioPlayerFragment.initDetailedView()
|
||||
onSlide(view, 1.0f)
|
||||
}
|
||||
|
|
|
@ -28,10 +28,10 @@ import coil.imageLoader
|
|||
import coil.request.ErrorResult
|
||||
import coil.request.ImageRequest
|
||||
import coil.request.SuccessResult
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
// TODO: need to enable
|
||||
class SelectSubscriptionActivity : AppCompatActivity() {
|
||||
|
@ -41,7 +41,8 @@ class SelectSubscriptionActivity : AppCompatActivity() {
|
|||
@Volatile
|
||||
private var listItems: List<Feed> = listOf()
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
// private var disposable: Disposable? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(ThemeSwitcher.getTranslucentTheme(this))
|
||||
|
@ -100,22 +101,6 @@ class SelectSubscriptionActivity : AppCompatActivity() {
|
|||
|
||||
private fun getBitmapFromUrl(feed: Feed) {
|
||||
val iconSize = (128 * resources.displayMetrics.density).toInt()
|
||||
|
||||
// if (!feed.imageUrl.isNullOrBlank()) Glide.with(this)
|
||||
// .asBitmap()
|
||||
// .load(feed.imageUrl)
|
||||
// .apply(RequestOptions.overrideOf(iconSize, iconSize))
|
||||
// .listener(object : RequestListener<Bitmap?> {
|
||||
// @UnstableApi override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Bitmap?>, isFirstResource: Boolean): Boolean {
|
||||
// addShortcut(feed, null)
|
||||
// return true
|
||||
// }
|
||||
// @UnstableApi override fun onResourceReady(resource: Bitmap, model: Any, target: Target<Bitmap?>, dataSource: DataSource, isFirstResource: Boolean): Boolean {
|
||||
// addShortcut(feed, resource)
|
||||
// return true
|
||||
// }
|
||||
// }).submit()
|
||||
|
||||
val request = ImageRequest.Builder(this)
|
||||
.data(feed.imageUrl)
|
||||
.setHeader("User-Agent", "Mozilla/5.0")
|
||||
|
@ -134,24 +119,45 @@ class SelectSubscriptionActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun loadSubscriptions() {
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
|
||||
disposable = Observable.fromCallable {
|
||||
// disposable = Observable.fromCallable {
|
||||
// val data: NavDrawerData = DBReader.getNavDrawerData(UserPreferences.subscriptionsFilter)
|
||||
// getFeedItems(data.items, ArrayList())
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { result: List<Feed> ->
|
||||
// listItems = result
|
||||
// val titles = ArrayList<String>()
|
||||
// for (feed in result) {
|
||||
// if (feed.title != null) titles.add(feed.title!!)
|
||||
// }
|
||||
// val adapter: ArrayAdapter<String> = ArrayAdapter<String>(this, R.layout.simple_list_item_multiple_choice_on_start, titles)
|
||||
// binding.list.adapter = adapter
|
||||
// }, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
val data: NavDrawerData = DBReader.getNavDrawerData(UserPreferences.subscriptionsFilter)
|
||||
getFeedItems(data.items, ArrayList())
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ result: List<Feed> ->
|
||||
withContext(Dispatchers.Main) {
|
||||
listItems = result
|
||||
val titles = ArrayList<String>()
|
||||
for (feed in result) {
|
||||
if (feed.title != null) titles.add(feed.title!!)
|
||||
}
|
||||
val adapter: ArrayAdapter<String> = ArrayAdapter<String>(this, R.layout.simple_list_item_multiple_choice_on_start, titles)
|
||||
val adapter: ArrayAdapter<String> = ArrayAdapter<String>(this@SelectSubscriptionActivity, R.layout.simple_list_item_multiple_choice_on_start, titles)
|
||||
binding.list.adapter = adapter
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
|
|
@ -2,12 +2,10 @@ package ac.mdiq.podcini.ui.adapter
|
|||
|
||||
import ac.mdiq.podcini.R
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import coil.Coil
|
||||
import coil.ImageLoader
|
||||
import coil.imageLoader
|
||||
import coil.request.ErrorResult
|
||||
|
|
|
@ -6,12 +6,11 @@ import ac.mdiq.podcini.databinding.NavSectionItemBinding
|
|||
import ac.mdiq.podcini.preferences.UserPreferences
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.episodeCacheSize
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.hiddenDrawerItems
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.subscriptionsFilter
|
||||
import ac.mdiq.podcini.storage.NavDrawerData.FeedDrawerItem
|
||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
||||
import ac.mdiq.podcini.ui.fragment.*
|
||||
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.app.Activity
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
|
@ -22,14 +21,12 @@ import android.view.ContextMenu.ContextMenuInfo
|
|||
import android.view.View.OnCreateContextMenuListener
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.apache.commons.lang3.ArrayUtils
|
||||
import java.lang.ref.WeakReference
|
||||
|
@ -48,7 +45,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
private val titles: Array<String> = context.resources.getStringArray(R.array.nav_drawer_titles)
|
||||
private val activity = WeakReference(context)
|
||||
@JvmField
|
||||
var showSubscriptionList: Boolean = true
|
||||
var showSubscriptionList: Boolean = false
|
||||
|
||||
init {
|
||||
loadItems()
|
||||
|
@ -65,16 +62,6 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
val newTags: MutableList<String?> = ArrayList(listOf(*NavDrawerFragment.NAV_DRAWER_TAGS))
|
||||
val hiddenFragments = hiddenDrawerItems
|
||||
newTags.removeAll(hiddenFragments)
|
||||
|
||||
if (newTags.contains(SUBSCRIPTION_LIST_TAG)) {
|
||||
// we never want SUBSCRIPTION_LIST_TAG to be in 'tags'
|
||||
// since it doesn't actually correspond to a position in the list, but is
|
||||
// a placeholder that indicates if we should show the subscription list in the
|
||||
// nav drawer at all.
|
||||
showSubscriptionList = true
|
||||
newTags.remove(SUBSCRIPTION_LIST_TAG)
|
||||
} else showSubscriptionList = false
|
||||
|
||||
fragmentTags.clear()
|
||||
fragmentTags.addAll(newTags)
|
||||
notifyDataSetChanged()
|
||||
|
@ -104,15 +91,12 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
var baseCount = subscriptionOffset
|
||||
if (showSubscriptionList) baseCount += itemAccess.count
|
||||
return baseCount
|
||||
return subscriptionOffset
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
val viewType = getItemViewType(position)
|
||||
return when (viewType) {
|
||||
VIEW_TYPE_SUBSCRIPTION -> itemAccess.getItem(position - subscriptionOffset)?.id?:0
|
||||
VIEW_TYPE_NAV -> (-abs(fragmentTags[position].hashCode().toLong().toDouble()) - 1).toLong() // Folder IDs are >0
|
||||
else -> 0
|
||||
}
|
||||
|
@ -122,7 +106,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
return when {
|
||||
0 <= position && position < fragmentTags.size -> VIEW_TYPE_NAV
|
||||
position < subscriptionOffset -> VIEW_TYPE_SECTION_DIVIDER
|
||||
else -> VIEW_TYPE_SUBSCRIPTION
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,8 +117,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
val inflater = LayoutInflater.from(activity.get())
|
||||
return when (viewType) {
|
||||
VIEW_TYPE_NAV -> NavHolder(inflater.inflate(R.layout.nav_listitem, parent, false))
|
||||
VIEW_TYPE_SECTION_DIVIDER -> DividerHolder(inflater.inflate(R.layout.nav_section_item, parent, false))
|
||||
else -> FeedHolder(inflater.inflate(R.layout.nav_listitem, parent, false))
|
||||
else -> DividerHolder(inflater.inflate(R.layout.nav_section_item, parent, false))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,16 +127,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
holder.itemView.setOnCreateContextMenuListener(null)
|
||||
when (viewType) {
|
||||
VIEW_TYPE_NAV -> bindNavView(getLabel(fragmentTags[position]), position, holder as NavHolder)
|
||||
VIEW_TYPE_SECTION_DIVIDER -> bindSectionDivider(holder as DividerHolder)
|
||||
else -> {
|
||||
val itemPos = position - subscriptionOffset
|
||||
val item = itemAccess.getItem(itemPos)
|
||||
if (item != null) {
|
||||
bindListItem(item, holder as FeedHolder)
|
||||
bindFeedView(item, holder)
|
||||
}
|
||||
holder.itemView.setOnCreateContextMenuListener(itemAccess)
|
||||
}
|
||||
else -> bindSectionDivider(holder as DividerHolder)
|
||||
}
|
||||
if (viewType != VIEW_TYPE_SECTION_DIVIDER) {
|
||||
holder.itemView.isSelected = itemAccess.isSelected(position)
|
||||
|
@ -179,32 +153,34 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
holder.count.isClickable = false
|
||||
|
||||
val tag = fragmentTags[position]
|
||||
when {
|
||||
tag == QueueFragment.TAG -> {
|
||||
when (tag) {
|
||||
SubscriptionFragment.TAG -> {
|
||||
val sum = itemAccess.numberOfFeeds
|
||||
if (sum > 0) {
|
||||
holder.count.text = NumberFormat.getInstance().format(sum.toLong())
|
||||
holder.count.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
QueueFragment.TAG -> {
|
||||
val queueSize = itemAccess.queueSize
|
||||
if (queueSize > 0) {
|
||||
holder.count.text = NumberFormat.getInstance().format(queueSize.toLong())
|
||||
holder.count.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
// tag == InboxFragment.TAG -> {
|
||||
// val unreadItems = itemAccess.numberOfNewItems
|
||||
// if (unreadItems > 0) {
|
||||
// holder.count.text = NumberFormat.getInstance().format(unreadItems.toLong())
|
||||
// holder.count.visibility = View.VISIBLE
|
||||
// }
|
||||
// }
|
||||
tag == SubscriptionFragment.TAG -> {
|
||||
val sum = itemAccess.feedCounterSum
|
||||
if (sum > 0) {
|
||||
holder.count.text = NumberFormat.getInstance().format(sum.toLong())
|
||||
AllEpisodesFragment.TAG -> {
|
||||
val numEpisodes = itemAccess.numberOfItems
|
||||
if (numEpisodes > 0) {
|
||||
holder.count.text = NumberFormat.getInstance().format(numEpisodes.toLong())
|
||||
holder.count.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
tag == DownloadsFragment.TAG && isEnableAutodownload -> {
|
||||
DownloadsFragment.TAG -> {
|
||||
val epCacheSize = episodeCacheSize
|
||||
// don't count episodes that can be reclaimed
|
||||
val spaceUsed = (itemAccess.numberOfDownloadedItems - itemAccess.reclaimableItems)
|
||||
holder.count.text = NumberFormat.getInstance().format(spaceUsed.toLong())
|
||||
holder.count.visibility = View.VISIBLE
|
||||
if (epCacheSize in 1..spaceUsed) {
|
||||
holder.count.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_disc_alert, 0)
|
||||
holder.count.visibility = View.VISIBLE
|
||||
|
@ -224,71 +200,14 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
}
|
||||
}
|
||||
|
||||
Logd("NavListAdapter", "bindNavView getting drawable for: ${fragmentTags[position]}")
|
||||
holder.image.setImageResource(getDrawable(fragmentTags[position]))
|
||||
}
|
||||
|
||||
private fun bindSectionDivider(holder: DividerHolder) {
|
||||
// val context = activity.get() ?: return
|
||||
|
||||
if (subscriptionsFilter.isEnabled && showSubscriptionList) {
|
||||
holder.itemView.isEnabled = true
|
||||
holder.feedsFilteredMsg.visibility = View.VISIBLE
|
||||
} else {
|
||||
holder.itemView.isEnabled = false
|
||||
holder.feedsFilteredMsg.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindListItem(item: FeedDrawerItem, holder: FeedHolder) {
|
||||
if (item.counter > 0) {
|
||||
holder.count.visibility = View.VISIBLE
|
||||
holder.count.text = NumberFormat.getInstance().format(item.counter.toLong())
|
||||
} else holder.count.visibility = View.GONE
|
||||
|
||||
holder.title.text = item.title
|
||||
val padding = (activity.get()!!.resources.getDimension(R.dimen.thumbnail_length_navlist) / 2).toInt()
|
||||
holder.itemView.setPadding(item.layer * padding, 0, 0, 0)
|
||||
}
|
||||
|
||||
private fun bindFeedView(drawerItem: FeedDrawerItem, holder: FeedHolder) {
|
||||
val feed = drawerItem.feed
|
||||
val context = activity.get() ?: return
|
||||
|
||||
// if (!feed.imageUrl.isNullOrBlank()) Glide.with(context)
|
||||
// .load(feed.imageUrl)
|
||||
// .apply(RequestOptions()
|
||||
// .placeholder(R.color.light_gray)
|
||||
// .error(R.color.light_gray)
|
||||
// .transform(FitCenter(),
|
||||
// RoundedCorners((4 * context.resources.displayMetrics.density).toInt()))
|
||||
// .dontAnimate())
|
||||
// .into(holder.image)
|
||||
|
||||
holder.image.load(feed.imageUrl) {
|
||||
placeholder(R.color.light_gray)
|
||||
error(R.mipmap.ic_launcher)
|
||||
}
|
||||
|
||||
if (feed.hasLastUpdateFailed()) {
|
||||
val p = holder.title.layoutParams as RelativeLayout.LayoutParams
|
||||
p.addRule(RelativeLayout.LEFT_OF, R.id.itxtvFailure)
|
||||
holder.failure.visibility = View.VISIBLE
|
||||
} else {
|
||||
val p = holder.title.layoutParams as RelativeLayout.LayoutParams
|
||||
p.addRule(RelativeLayout.LEFT_OF, R.id.txtvCount)
|
||||
holder.failure.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
// private fun bindTagView(tag: TagDrawerItem, holder: FeedHolder) {
|
||||
// val context = activity.get() ?: return
|
||||
// if (tag.isOpen) {
|
||||
// holder.count.visibility = View.GONE
|
||||
// }
|
||||
// Glide.with(context).clear(holder.image)
|
||||
// holder.image.setImageResource(R.drawable.ic_tag)
|
||||
// holder.failure.visibility = View.GONE
|
||||
// }
|
||||
|
||||
open class Holder(itemView: View) : RecyclerView.ViewHolder(itemView)
|
||||
|
||||
|
@ -304,14 +223,6 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
val count: TextView = binding.txtvCount
|
||||
}
|
||||
|
||||
internal class FeedHolder(itemView: View) : Holder(itemView) {
|
||||
val binding = NavListitemBinding.bind(itemView)
|
||||
val image: ImageView = binding.imgvCover
|
||||
val title: TextView = binding.txtvTitle
|
||||
val failure: ImageView = binding.itxtvFailure
|
||||
val count: TextView = binding.txtvCount
|
||||
}
|
||||
|
||||
interface ItemAccess : OnCreateContextMenuListener {
|
||||
val count: Int
|
||||
|
||||
|
@ -323,12 +234,16 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
|
||||
val numberOfNewItems: Int
|
||||
|
||||
val numberOfItems: Int
|
||||
|
||||
val numberOfDownloadedItems: Int
|
||||
|
||||
val reclaimableItems: Int
|
||||
|
||||
val feedCounterSum: Int
|
||||
|
||||
val numberOfFeeds: Int
|
||||
|
||||
fun onItemClick(position: Int)
|
||||
|
||||
fun onItemLongClick(position: Int): Boolean
|
||||
|
@ -339,12 +254,5 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
companion object {
|
||||
const val VIEW_TYPE_NAV: Int = 0
|
||||
const val VIEW_TYPE_SECTION_DIVIDER: Int = 1
|
||||
private const val VIEW_TYPE_SUBSCRIPTION = 2
|
||||
|
||||
/**
|
||||
* a tag used as a placeholder to indicate if the subscription list should be displayed or not
|
||||
* This tag doesn't correspond to any specific activity.
|
||||
*/
|
||||
const val SUBSCRIPTION_LIST_TAG: String = "SubscriptionList"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import ac.mdiq.podcini.R
|
|||
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions
|
||||
import ac.mdiq.podcini.preferences.UserPreferences
|
||||
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
|
||||
/**
|
||||
* List adapter for the queue.
|
||||
|
@ -45,7 +46,7 @@ open class QueueRecyclerAdapter(mainActivity: MainActivity, private val swipeAct
|
|||
val isLtr = holder.itemView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR
|
||||
val factor = (if (isLtr) 1 else -1).toFloat()
|
||||
if (factor * event.x < factor * 0.5 * v1.width) swipeActions.startDrag(holder)
|
||||
else Log.d(TAG, "Ignoring drag in right half of the image")
|
||||
else Logd(TAG, "Ignoring drag in right half of the image")
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
@ -10,9 +10,10 @@ import android.content.DialogInterface
|
|||
import android.util.Log
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
object RemoveFeedDialog {
|
||||
private const val TAG = "RemoveFeedDialog"
|
||||
|
@ -41,21 +42,39 @@ object RemoveFeedDialog {
|
|||
progressDialog.setCancelable(false)
|
||||
progressDialog.show()
|
||||
|
||||
Completable.fromAction {
|
||||
// Completable.fromAction {
|
||||
// for (feed in feeds) {
|
||||
// DBWriter.deleteFeed(context, feed.id).get()
|
||||
// }
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// {
|
||||
// Logd(TAG, "Feed(s) deleted")
|
||||
// progressDialog.dismiss()
|
||||
// }, { error: Throwable? ->
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// progressDialog.dismiss()
|
||||
// })
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
scope.launch {
|
||||
try {
|
||||
withContext(Dispatchers.IO) {
|
||||
for (feed in feeds) {
|
||||
DBWriter.deleteFeed(context, feed.id).get()
|
||||
}
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
withContext(Dispatchers.Main) {
|
||||
Logd(TAG, "Feed(s) deleted")
|
||||
progressDialog.dismiss()
|
||||
}, { error: Throwable? ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
progressDialog.dismiss()
|
||||
})
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
withContext(Dispatchers.Main) { progressDialog.dismiss() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dialog.createNewDialog().show()
|
||||
|
|
|
@ -19,9 +19,10 @@ import androidx.fragment.app.DialogFragment
|
|||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.io.Serializable
|
||||
|
||||
|
@ -87,18 +88,33 @@ class TagSettingsDialog : DialogFragment() {
|
|||
}
|
||||
|
||||
private fun loadTags() {
|
||||
Observable.fromCallable {
|
||||
// Observable.fromCallable {
|
||||
// DBReader.getTags()
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { result: List<String> ->
|
||||
// val acAdapter = ArrayAdapter(requireContext(), R.layout.single_tag_text_view, result)
|
||||
// binding.newTagEditText.setAdapter(acAdapter)
|
||||
// }, { error: Throwable? ->
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// })
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
scope.launch {
|
||||
try {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
DBReader.getTags()
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ result: List<String> ->
|
||||
withContext(Dispatchers.Main) {
|
||||
val acAdapter = ArrayAdapter(requireContext(), R.layout.single_tag_text_view, result)
|
||||
binding.newTagEditText.setAdapter(acAdapter)
|
||||
}, { error: Throwable? ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
})
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTag(name: String) {
|
||||
|
|
|
@ -10,6 +10,7 @@ import ac.mdiq.podcini.storage.model.feed.Feed
|
|||
import ac.mdiq.podcini.storage.model.feed.SortOrder
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.ui.activity.OpmlImportActivity
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.content.*
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
|
@ -26,9 +27,10 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.media3.common.util.UnstableApi
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
* Provides actions for adding new podcast subscriptions.
|
||||
|
@ -52,7 +54,7 @@ class AddFeedFragment : Fragment() {
|
|||
_binding = AddfeedBinding.inflate(inflater)
|
||||
activity = getActivity() as? MainActivity
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
displayUpArrow = parentFragmentManager.backStackEntryCount != 0
|
||||
if (savedInstanceState != null) displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
|
||||
|
||||
|
@ -162,18 +164,36 @@ class AddFeedFragment : Fragment() {
|
|||
@UnstableApi private fun addLocalFolderResult(uri: Uri?) {
|
||||
if (uri == null) return
|
||||
|
||||
Observable.fromCallable<Feed> { addLocalFolder(uri) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ feed: Feed ->
|
||||
// Observable.fromCallable<Feed> { addLocalFolder(uri) }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { feed: Feed ->
|
||||
// val fragment: Fragment = FeedItemlistFragment.newInstance(feed.id)
|
||||
// (getActivity() as MainActivity).loadChildFragment(fragment)
|
||||
// }, { error: Throwable ->
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// (getActivity() as MainActivity)
|
||||
// .showSnackbarAbovePlayer(error.localizedMessage, Snackbar.LENGTH_LONG)
|
||||
// })
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
scope.launch {
|
||||
try {
|
||||
val feed = withContext(Dispatchers.IO) {
|
||||
addLocalFolder(uri)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (feed != null) {
|
||||
val fragment: Fragment = FeedItemlistFragment.newInstance(feed.id)
|
||||
(getActivity() as MainActivity).loadChildFragment(fragment)
|
||||
}, { error: Throwable ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
(getActivity() as MainActivity)
|
||||
.showSnackbarAbovePlayer(error.localizedMessage, Snackbar.LENGTH_LONG)
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
(getActivity() as MainActivity).showSnackbarAbovePlayer(e.localizedMessage, Snackbar.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@UnstableApi private fun addLocalFolder(uri: Uri): Feed? {
|
||||
|
|
|
@ -10,9 +10,9 @@ import ac.mdiq.podcini.storage.model.feed.SortOrder
|
|||
import ac.mdiq.podcini.ui.dialog.AllEpisodesFilterDialog
|
||||
import ac.mdiq.podcini.ui.dialog.AllEpisodesFilterDialog.AllEpisodesFilterChangedEvent
|
||||
import ac.mdiq.podcini.ui.dialog.ItemSortDialog
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.FeedListUpdateEvent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
|
@ -27,9 +27,10 @@ import org.greenrobot.eventbus.Subscribe
|
|||
* Shows all episodes (possibly filtered by user).
|
||||
*/
|
||||
class AllEpisodesFragment : BaseEpisodesListFragment() {
|
||||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val root = super.onCreateView(inflater, container, savedInstanceState)
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
|
||||
toolbar.inflateMenu(R.menu.episodes)
|
||||
toolbar.setTitle(R.string.episodes_label)
|
||||
|
@ -128,7 +129,7 @@ class AllEpisodesFragment : BaseEpisodesListFragment() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val TAG: String = "EpisodesFragment"
|
||||
const val TAG: String = "AllEpisodesFragment"
|
||||
const val PREF_NAME: String = "PrefAllEpisodesFragment"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,11 +64,8 @@ import coil.request.ImageRequest
|
|||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.elevation.SurfaceColors
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.MaybeEmitter
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
@ -97,8 +94,9 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
private lateinit var cardViewSeek: CardView
|
||||
private lateinit var txtvSeek: TextView
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
private var controller: PlaybackController? = null
|
||||
private var disposable: Disposable? = null
|
||||
// private var disposable: Disposable? = null
|
||||
private var seekedToChapterStart = false
|
||||
private var currentChapterIndex = -1
|
||||
private var duration = 0
|
||||
|
@ -106,6 +104,8 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
private var currentMedia: Playable? = null
|
||||
private var currentitem: FeedItem? = null
|
||||
|
||||
var isCollapsed = true
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
_binding = AudioplayerFragmentBinding.inflate(inflater)
|
||||
|
@ -163,6 +163,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
_binding = null
|
||||
controller?.release()
|
||||
controller = null
|
||||
scope.cancel()
|
||||
EventBus.getDefault().unregister(this)
|
||||
Logd(TAG, "Fragment destroyed")
|
||||
}
|
||||
|
@ -192,32 +193,55 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
(activity as MainActivity).bottomSheet.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
|
||||
private fun loadMediaInfo(includingChapters: Boolean) {
|
||||
Logd(TAG, "loadMediaInfo called")
|
||||
// private fun loadMediaInfo0(includingChapters: Boolean) {
|
||||
// Logd(TAG, "loadMediaInfo called")
|
||||
//
|
||||
// val theMedia = controller?.getMedia() ?: return
|
||||
// Logd(TAG, "loadMediaInfo $theMedia")
|
||||
//
|
||||
// if (currentMedia == null || theMedia.getIdentifier() != currentMedia?.getIdentifier()) {
|
||||
// Logd(TAG, "loadMediaInfo loading details")
|
||||
// disposable?.dispose()
|
||||
// disposable = Maybe.create<Playable> { emitter: MaybeEmitter<Playable?> ->
|
||||
// val media: Playable? = theMedia
|
||||
// if (media != null) {
|
||||
// if (includingChapters) ChapterUtils.loadChapters(media, requireContext(), false)
|
||||
// emitter.onSuccess(media)
|
||||
// } else emitter.onComplete()
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ media: Playable ->
|
||||
// currentMedia = media
|
||||
// updateUi(media)
|
||||
// playerFragment1?.updateUi(media)
|
||||
// playerFragment2?.updateUi(media)
|
||||
// if (!includingChapters) loadMediaInfo(true)
|
||||
// }, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) },
|
||||
// { updateUi(null) })
|
||||
// }
|
||||
// }
|
||||
|
||||
fun loadMediaInfo(includingChapters: Boolean) {
|
||||
val theMedia = controller?.getMedia() ?: return
|
||||
Logd(TAG, "loadMediaInfo $theMedia")
|
||||
|
||||
if (currentMedia == null || theMedia.getIdentifier() != currentMedia?.getIdentifier()) {
|
||||
Logd(TAG, "loadMediaInfo loading details")
|
||||
disposable?.dispose()
|
||||
disposable = Maybe.create<Playable> { emitter: MaybeEmitter<Playable?> ->
|
||||
val media: Playable? = theMedia
|
||||
if (media != null) {
|
||||
if (includingChapters) ChapterUtils.loadChapters(media, requireContext(), false)
|
||||
emitter.onSuccess(media)
|
||||
} else emitter.onComplete()
|
||||
if (currentMedia == null || theMedia.getIdentifier() != currentMedia?.getIdentifier() || (includingChapters && !theMedia.chaptersLoaded())) {
|
||||
Logd(TAG, "loadMediaInfo loading details ${theMedia.getIdentifier()} chapter: $includingChapters")
|
||||
scope.launch {
|
||||
val media: Playable = withContext(Dispatchers.IO) {
|
||||
theMedia.apply {
|
||||
if (includingChapters) ChapterUtils.loadChapters(this, requireContext(), false)
|
||||
}
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ media: Playable ->
|
||||
currentMedia = media
|
||||
updateUi(media)
|
||||
playerFragment1?.updateUi(media)
|
||||
playerFragment2?.updateUi(media)
|
||||
updateUi()
|
||||
playerFragment1?.updateUi(currentMedia)
|
||||
playerFragment2?.updateUi(currentMedia)
|
||||
if (!includingChapters) loadMediaInfo(true)
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) },
|
||||
{ updateUi(null) })
|
||||
}.invokeOnCompletion { throwable ->
|
||||
if (throwable!= null) {
|
||||
Log.e(TAG, Log.getStackTraceString(throwable))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,10 +264,10 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateUi(media: Playable?) {
|
||||
fun updateUi() {
|
||||
Logd(TAG, "updateUi called")
|
||||
setChapterDividers(media)
|
||||
setupOptionsMenu(media)
|
||||
setChapterDividers(currentMedia)
|
||||
setupOptionsMenu(currentMedia)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
@ -265,7 +289,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
override fun onStop() {
|
||||
super.onStop()
|
||||
// progressIndicator.visibility = View.GONE // Controller released; we will not receive buffering updates
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
}
|
||||
|
||||
// @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
@ -328,7 +352,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
txtvSeek.text = controller!!.getMedia()?.getChapters()?.get(newChapterIndex)?.title ?: ("\n${Converter.getDurationStringLong(position)}")
|
||||
} else txtvSeek.text = Converter.getDurationStringLong(position)
|
||||
}
|
||||
duration != controller!!.duration -> updateUi(controller!!.getMedia())
|
||||
duration != controller!!.duration -> updateUi()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -695,7 +719,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
|||
(activity as MainActivity).setPlayerVisible(true)
|
||||
onPositionObserverUpdate(PlaybackPositionEvent(media.getPosition(), media.getDuration()))
|
||||
|
||||
val imgLoc = ImageResourceUtils.getEpisodeListImageLocation(media) + "sdfsdf"
|
||||
val imgLoc = ImageResourceUtils.getEpisodeListImageLocation(media)
|
||||
val imgLocFB = ImageResourceUtils.getFallbackImageLocation(media)
|
||||
val imageLoader = imgvCover.context.imageLoader
|
||||
val imageRequest = ImageRequest.Builder(requireContext())
|
||||
|
|
|
@ -39,11 +39,7 @@ import com.google.android.material.appbar.MaterialToolbar
|
|||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.leinardi.android.speeddial.SpeedDialActionItem
|
||||
import com.leinardi.android.speeddial.SpeedDialView
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
@ -62,6 +58,8 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
|||
var _binding: BaseEpisodesListFragmentBinding? = null
|
||||
protected val binding get() = _binding!!
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
lateinit var recyclerView: EpisodeItemListRecyclerView
|
||||
lateinit var emptyView: EmptyViewHandler
|
||||
lateinit var speedDialView: SpeedDialView
|
||||
|
@ -77,7 +75,7 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
|||
@JvmField
|
||||
var episodes: MutableList<FeedItem> = ArrayList()
|
||||
|
||||
protected var disposable: Disposable? = null
|
||||
// protected var disposable: Disposable? = null
|
||||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
@ -202,7 +200,7 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
|||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
}
|
||||
|
||||
@UnstableApi override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
@ -242,7 +240,26 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
|||
|
||||
@UnstableApi private fun performMultiSelectAction(actionItemId: Int) {
|
||||
val handler = EpisodeMultiSelectActionHandler((activity as MainActivity), actionItemId)
|
||||
Completable.fromAction {
|
||||
// Completable.fromAction {
|
||||
// handler.handleAction(listAdapter.selectedItems.filterIsInstance<FeedItem>())
|
||||
// if (listAdapter.shouldSelectLazyLoadedItems()) {
|
||||
// var applyPage = page + 1
|
||||
// var nextPage: List<FeedItem>
|
||||
// do {
|
||||
// nextPage = loadMoreData(applyPage)
|
||||
// handler.handleAction(nextPage)
|
||||
// applyPage++
|
||||
// } while (nextPage.size == EPISODES_PER_PAGE)
|
||||
// }
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ listAdapter.endSelectMode() },
|
||||
// { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
withContext(Dispatchers.IO) {
|
||||
handler.handleAction(listAdapter.selectedItems.filterIsInstance<FeedItem>())
|
||||
if (listAdapter.shouldSelectLazyLoadedItems()) {
|
||||
var applyPage = page + 1
|
||||
|
@ -253,57 +270,85 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
|||
applyPage++
|
||||
} while (nextPage.size == EPISODES_PER_PAGE)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
listAdapter.endSelectMode()
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ listAdapter.endSelectMode() },
|
||||
{ error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
||||
private fun setupLoadMoreScrollListener() {
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(view: RecyclerView, deltaX: Int, deltaY: Int) {
|
||||
super.onScrolled(view, deltaX, deltaY)
|
||||
Logd(TAG, "addOnScrollListener called isLoadingMore:$isLoadingMore hasMoreItems:$hasMoreItems ${recyclerView.isScrolledToBottom}")
|
||||
if (!isLoadingMore && hasMoreItems && recyclerView.isScrolledToBottom) {
|
||||
/* The end of the list has been reached. Load more data. */
|
||||
page++
|
||||
loadMoreItems()
|
||||
isLoadingMore = true
|
||||
// isLoadingMore = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadMoreItems() {
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
Logd(TAG, "loadMoreItems() called $page")
|
||||
|
||||
isLoadingMore = true
|
||||
listAdapter.setDummyViews(1)
|
||||
listAdapter.notifyItemInserted(listAdapter.itemCount - 1)
|
||||
disposable = Observable.fromCallable { loadMoreData(page) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ data: List<FeedItem> ->
|
||||
// disposable = Observable.fromCallable { loadMoreData(page) }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ data: List<FeedItem> ->
|
||||
// if (data.size < EPISODES_PER_PAGE) hasMoreItems = false
|
||||
// Logd(TAG, "loadMoreItems $page ${data.size}")
|
||||
// episodes.addAll(data)
|
||||
// listAdapter.setDummyViews(0)
|
||||
// listAdapter.updateItems(episodes)
|
||||
// if (listAdapter.shouldSelectLazyLoadedItems()) listAdapter.setSelected(episodes.size - data.size, episodes.size, true)
|
||||
//
|
||||
// }, { error: Throwable? ->
|
||||
// listAdapter.setDummyViews(0)
|
||||
// listAdapter.updateItems(emptyList())
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// }, {
|
||||
// // Make sure to not always load 2 pages at once
|
||||
// recyclerView.post { isLoadingMore = false }
|
||||
// })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
val data = withContext(Dispatchers.IO) {
|
||||
loadMoreData(page)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (data.size < EPISODES_PER_PAGE) hasMoreItems = false
|
||||
Logd(TAG, "loadMoreItems $page ${data.size}")
|
||||
episodes.addAll(data)
|
||||
listAdapter.setDummyViews(0)
|
||||
listAdapter.updateItems(episodes)
|
||||
if (listAdapter.shouldSelectLazyLoadedItems()) listAdapter.setSelected(episodes.size - data.size, episodes.size, true)
|
||||
|
||||
}, { error: Throwable? ->
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
listAdapter.setDummyViews(0)
|
||||
listAdapter.updateItems(emptyList())
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
}, {
|
||||
// Make sure to not always load 2 pages at once
|
||||
recyclerView.post { isLoadingMore = false }
|
||||
})
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
} finally {
|
||||
withContext(Dispatchers.Main) { recyclerView.post { isLoadingMore = false } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
scope.cancel()
|
||||
EventBus.getDefault().unregister(this)
|
||||
listAdapter.endSelectMode()
|
||||
}
|
||||
|
@ -319,7 +364,7 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
|||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: FeedItemEvent) {
|
||||
Log.d(TAG, "onEventMainThread() called with FeedItemEvent event = [$event]")
|
||||
Logd(TAG, "onEventMainThread() called with FeedItemEvent event = [$event]")
|
||||
for (item in event.items) {
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithId(episodes, item.id)
|
||||
if (pos >= 0) {
|
||||
|
@ -338,7 +383,7 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
|||
if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem)
|
||||
currentPlaying!!.notifyPlaybackPositionUpdated(event)
|
||||
else {
|
||||
Log.d(TAG, "onEventMainThread() search list")
|
||||
Logd(TAG, "onEventMainThread() search list")
|
||||
for (i in 0 until listAdapter.itemCount) {
|
||||
val holder: EpisodeItemViewHolder? = recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||
|
@ -395,15 +440,37 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
|||
}
|
||||
|
||||
fun loadItems() {
|
||||
disposable?.dispose()
|
||||
Logd(TAG, "loadItems() called")
|
||||
// disposable?.dispose()
|
||||
|
||||
disposable = Observable.fromCallable {
|
||||
// disposable = Observable.fromCallable {
|
||||
// Pair(loadData().toMutableList(), loadTotalItemCount())
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { data: Pair<MutableList<FeedItem>, Int> ->
|
||||
// val restoreScrollPosition = episodes.isEmpty()
|
||||
// episodes = data.first
|
||||
// hasMoreItems = !(page == 1 && episodes.size < EPISODES_PER_PAGE)
|
||||
// progressBar.visibility = View.GONE
|
||||
// listAdapter.setDummyViews(0)
|
||||
// listAdapter.updateItems(episodes)
|
||||
// listAdapter.setTotalNumberOfItems(data.second)
|
||||
// if (restoreScrollPosition) recyclerView.restoreScrollPosition(getPrefName())
|
||||
// updateToolbar()
|
||||
// }, { error: Throwable? ->
|
||||
// listAdapter.setDummyViews(0)
|
||||
// listAdapter.updateItems(emptyList())
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
val data = withContext(Dispatchers.IO) {
|
||||
Pair(loadData().toMutableList(), loadTotalItemCount())
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ data: Pair<MutableList<FeedItem>, Int> ->
|
||||
withContext(Dispatchers.Main) {
|
||||
val restoreScrollPosition = episodes.isEmpty()
|
||||
episodes = data.first
|
||||
hasMoreItems = !(page == 1 && episodes.size < EPISODES_PER_PAGE)
|
||||
|
@ -413,11 +480,14 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
|||
listAdapter.setTotalNumberOfItems(data.second)
|
||||
if (restoreScrollPosition) recyclerView.restoreScrollPosition(getPrefName())
|
||||
updateToolbar()
|
||||
}, { error: Throwable? ->
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
listAdapter.setDummyViews(0)
|
||||
listAdapter.updateItems(emptyList())
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
})
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected abstract fun loadData(): List<FeedItem>
|
||||
|
@ -446,7 +516,7 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val TAG: String = "EpisodesListFragment"
|
||||
const val TAG: String = "BaseEpisodesListFragment"
|
||||
private const val KEY_UP_ARROW = "up_arrow"
|
||||
const val EPISODES_PER_PAGE: Int = 150
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import ac.mdiq.podcini.net.discovery.PodcastSearchResult
|
|||
import ac.mdiq.podcini.storage.DBReader
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.ui.adapter.OnlineFeedsAdapter
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.DiscoveryDefaultUpdateEvent
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
|
@ -29,10 +30,7 @@ import androidx.media3.common.util.UnstableApi
|
|||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.util.*
|
||||
|
||||
|
@ -61,7 +59,9 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
*/
|
||||
private var searchResults: List<PodcastSearchResult>? = null
|
||||
private var topList: List<PodcastSearchResult>? = null
|
||||
private var disposable: Disposable? = null
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
// private var disposable: Disposable? = null
|
||||
private var countryCode: String? = "US"
|
||||
private var hidden = false
|
||||
private var needsConfirm = false
|
||||
|
@ -101,7 +101,7 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
_binding = FragmentItunesSearchBinding.inflate(inflater)
|
||||
// val root = inflater.inflate(R.layout.fragment_itunes_search, container, false)
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
gridView = binding.gridView
|
||||
adapter = OnlineFeedsAdapter(requireActivity(), ArrayList())
|
||||
gridView.setAdapter(adapter)
|
||||
|
@ -135,13 +135,14 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
_binding = null
|
||||
disposable?.dispose()
|
||||
scope.cancel()
|
||||
// disposable?.dispose()
|
||||
|
||||
adapter = null
|
||||
}
|
||||
|
||||
private fun loadToplist(country: String?) {
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
|
||||
gridView.visibility = View.GONE
|
||||
txtvError.visibility = View.GONE
|
||||
|
@ -175,23 +176,44 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
|
||||
val loader = ItunesTopListLoader(requireContext())
|
||||
disposable = Observable.fromCallable { loader.loadToplist(country?:"",
|
||||
NUM_OF_TOP_PODCASTS, DBReader.getFeedList()) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ podcasts: List<PodcastSearchResult>? ->
|
||||
// disposable = Observable.fromCallable { loader.loadToplist(country?:"",
|
||||
// NUM_OF_TOP_PODCASTS, DBReader.getFeedList()) }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { podcasts: List<PodcastSearchResult>? ->
|
||||
// progressBar.visibility = View.GONE
|
||||
// topList = podcasts
|
||||
// updateData(topList)
|
||||
// }, { error: Throwable ->
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// progressBar.visibility = View.GONE
|
||||
// txtvError.text = error.message
|
||||
// txtvError.visibility = View.VISIBLE
|
||||
// butRetry.setOnClickListener { loadToplist(country) }
|
||||
// butRetry.visibility = View.VISIBLE
|
||||
// })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
val podcasts = withContext(Dispatchers.IO) {
|
||||
loader.loadToplist(country?:"", NUM_OF_TOP_PODCASTS, DBReader.getFeedList())
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
progressBar.visibility = View.GONE
|
||||
topList = podcasts
|
||||
updateData(topList)
|
||||
}, { error: Throwable ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
progressBar.visibility = View.GONE
|
||||
txtvError.text = error.message
|
||||
txtvError.text = e.message
|
||||
txtvError.visibility = View.VISIBLE
|
||||
butRetry.setOnClickListener { loadToplist(country) }
|
||||
butRetry.visibility = View.VISIBLE
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
|
|
|
@ -8,6 +8,7 @@ import ac.mdiq.podcini.storage.model.download.DownloadResult
|
|||
import ac.mdiq.podcini.ui.adapter.DownloadLogAdapter
|
||||
import ac.mdiq.podcini.ui.dialog.DownloadLogDetailsDialog
|
||||
import ac.mdiq.podcini.ui.view.EmptyViewHandler
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.DownloadLogEvent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
|
@ -17,10 +18,7 @@ import android.widget.AdapterView.OnItemClickListener
|
|||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
|
||||
|
@ -34,15 +32,17 @@ class DownloadLogFragment : BottomSheetDialogFragment(), OnItemClickListener, To
|
|||
private lateinit var adapter: DownloadLogAdapter
|
||||
|
||||
private var downloadLog: List<DownloadResult> = ArrayList()
|
||||
private var disposable: Disposable? = null
|
||||
// private var disposable: Disposable? = null
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
disposable?.dispose()
|
||||
scope.cancel()
|
||||
// disposable?.dispose()
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
_binding = DownloadLogFragmentBinding.inflate(inflater)
|
||||
binding.toolbar.inflateMenu(R.menu.download_log)
|
||||
binding.toolbar.setOnMenuItemClickListener(this)
|
||||
|
@ -95,17 +95,31 @@ class DownloadLogFragment : BottomSheetDialogFragment(), OnItemClickListener, To
|
|||
}
|
||||
|
||||
private fun loadDownloadLog() {
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
|
||||
disposable = Observable.fromCallable { DBReader.getDownloadLog() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ result: List<DownloadResult>? ->
|
||||
if (result != null) {
|
||||
// disposable = Observable.fromCallable { DBReader.getDownloadLog() }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ result: List<DownloadResult>? ->
|
||||
// if (result != null) {
|
||||
// downloadLog = result
|
||||
// adapter.setDownloadLog(downloadLog)
|
||||
// }
|
||||
// }, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
DBReader.getDownloadLog()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
downloadLog = result
|
||||
adapter.setDownloadLog(downloadLog)
|
||||
}
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -41,10 +41,10 @@ import com.google.android.material.appbar.MaterialToolbar
|
|||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.leinardi.android.speeddial.SpeedDialActionItem
|
||||
import com.leinardi.android.speeddial.SpeedDialView
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
@ -69,7 +69,7 @@ class DownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeListener, To
|
|||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var emptyView: EmptyViewHandler
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
// private var disposable: Disposable? = null
|
||||
private var displayUpArrow = false
|
||||
private var currentPlaying: EpisodeItemViewHolder? = null
|
||||
|
||||
|
@ -166,7 +166,7 @@ class DownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeListener, To
|
|||
adapter.endSelectMode()
|
||||
toolbar.setOnMenuItemClickListener(null)
|
||||
toolbar.setOnLongClickListener(null)
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
@ -303,39 +303,72 @@ class DownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeListener, To
|
|||
}
|
||||
|
||||
private fun loadItems() {
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
|
||||
emptyView.hide()
|
||||
disposable = Observable.fromCallable {
|
||||
// disposable = Observable.fromCallable {
|
||||
// val sortOrder: SortOrder? = UserPreferences.downloadsSortedOrder
|
||||
// val downloadedItems: List<FeedItem> = DBReader.getEpisodes(0, Int.MAX_VALUE,
|
||||
// FeedItemFilter(FeedItemFilter.DOWNLOADED), sortOrder)
|
||||
//
|
||||
// val mediaUrls: MutableList<String> = ArrayList()
|
||||
// if (runningDownloads.isEmpty()) return@fromCallable downloadedItems
|
||||
//
|
||||
// for (url in runningDownloads) {
|
||||
// if (FeedItemUtil.indexOfItemWithDownloadUrl(downloadedItems, url) != -1) continue // Already in list
|
||||
// mediaUrls.add(url)
|
||||
// }
|
||||
// val currentDownloads: MutableList<FeedItem> = DBReader.getFeedItemsWithUrl(mediaUrls).toMutableList()
|
||||
// currentDownloads.addAll(downloadedItems)
|
||||
// currentDownloads
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { result: List<FeedItem> ->
|
||||
// items = result.toMutableList()
|
||||
// adapter.setDummyViews(0)
|
||||
// progressBar.visibility = View.GONE
|
||||
// adapter.updateItems(result)
|
||||
// refreshInfoBar()
|
||||
// }, { error: Throwable? ->
|
||||
// adapter.setDummyViews(0)
|
||||
// adapter.updateItems(emptyList())
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// })
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
scope.launch {
|
||||
try {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
val sortOrder: SortOrder? = UserPreferences.downloadsSortedOrder
|
||||
val downloadedItems: List<FeedItem> = DBReader.getEpisodes(0, Int.MAX_VALUE,
|
||||
FeedItemFilter(FeedItemFilter.DOWNLOADED), sortOrder)
|
||||
|
||||
val downloadedItems: List<FeedItem> = DBReader.getEpisodes(0, Int.MAX_VALUE, FeedItemFilter(FeedItemFilter.DOWNLOADED), sortOrder)
|
||||
val mediaUrls: MutableList<String> = ArrayList()
|
||||
if (runningDownloads.isEmpty()) return@fromCallable downloadedItems
|
||||
|
||||
if (runningDownloads.isEmpty()) {
|
||||
downloadedItems
|
||||
} else {
|
||||
for (url in runningDownloads) {
|
||||
if (FeedItemUtil.indexOfItemWithDownloadUrl(downloadedItems, url) != -1) continue // Already in list
|
||||
if (FeedItemUtil.indexOfItemWithDownloadUrl(downloadedItems, url) != -1) continue
|
||||
mediaUrls.add(url)
|
||||
}
|
||||
val currentDownloads: MutableList<FeedItem> = DBReader.getFeedItemsWithUrl(mediaUrls).toMutableList()
|
||||
currentDownloads.addAll(downloadedItems)
|
||||
currentDownloads
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ result: List<FeedItem> ->
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
items = result.toMutableList()
|
||||
adapter.setDummyViews(0)
|
||||
progressBar.visibility = View.GONE
|
||||
adapter.updateItems(result)
|
||||
refreshInfoBar()
|
||||
}, { error: Throwable? ->
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
adapter.setDummyViews(0)
|
||||
adapter.updateItems(emptyList())
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
})
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshInfoBar() {
|
||||
|
|
|
@ -5,6 +5,7 @@ import ac.mdiq.podcini.databinding.EpisodeHomeFragmentBinding
|
|||
import ac.mdiq.podcini.storage.DBWriter.persistFeedItem
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||
import ac.mdiq.podcini.ui.utils.ShownotesCleaner
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.NetworkUtils.fetchHtmlSource
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
|
@ -57,7 +58,7 @@ class EpisodeHomeFragment : Fragment() {
|
|||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
_binding = EpisodeHomeFragmentBinding.inflate(inflater, container, false)
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
|
||||
toolbar = binding.toolbar
|
||||
toolbar.title = ""
|
||||
|
@ -74,7 +75,7 @@ class EpisodeHomeFragment : Fragment() {
|
|||
webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
val isEmpty = view?.title.isNullOrEmpty() && view?.contentDescription.isNullOrEmpty()
|
||||
if (isEmpty) Log.d(TAG, "content is empty")
|
||||
if (isEmpty) Logd(TAG, "content is empty")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +128,7 @@ class EpisodeHomeFragment : Fragment() {
|
|||
}
|
||||
|
||||
private fun initializeTTS(context: Context) {
|
||||
Log.d(TAG, "starting TTS")
|
||||
Logd(TAG, "starting TTS")
|
||||
if (tts == null) {
|
||||
tts = TextToSpeech(context) { status: Int ->
|
||||
if (status == TextToSpeech.SUCCESS) {
|
||||
|
@ -142,7 +143,7 @@ class EpisodeHomeFragment : Fragment() {
|
|||
}
|
||||
ttsReady = true
|
||||
// semaphore.release()
|
||||
Log.d(TAG, "TTS init success")
|
||||
Logd(TAG, "TTS init success")
|
||||
} else {
|
||||
Log.w(TAG, "TTS init failed")
|
||||
requireActivity().runOnUiThread {Toast.makeText(context, R.string.tts_init_failed, Toast.LENGTH_LONG).show() }
|
||||
|
@ -154,7 +155,7 @@ class EpisodeHomeFragment : Fragment() {
|
|||
private fun showWebContent() {
|
||||
if (!currentItem?.link.isNullOrEmpty()) {
|
||||
binding.webView.settings.javaScriptEnabled = jsEnabled
|
||||
Log.d(TAG, "currentItem!!.link ${currentItem!!.link}")
|
||||
Logd(TAG, "currentItem!!.link ${currentItem!!.link}")
|
||||
binding.webView.loadUrl(currentItem!!.link!!)
|
||||
binding.readerView.visibility = View.GONE
|
||||
binding.webView.visibility = View.VISIBLE
|
||||
|
@ -169,7 +170,7 @@ class EpisodeHomeFragment : Fragment() {
|
|||
private val menuProvider = object: MenuProvider {
|
||||
override fun onPrepareMenu(menu: Menu) {
|
||||
// super.onPrepareMenu(menu)
|
||||
Log.d(TAG, "onPrepareMenu called")
|
||||
Logd(TAG, "onPrepareMenu called")
|
||||
val textSpeech = menu.findItem(R.id.text_speech)
|
||||
textSpeech?.isVisible = readMode && tts != null
|
||||
if (textSpeech?.isVisible == true) {
|
||||
|
@ -187,18 +188,18 @@ class EpisodeHomeFragment : Fragment() {
|
|||
@OptIn(UnstableApi::class) override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
when (menuItem.itemId) {
|
||||
R.id.switch_home -> {
|
||||
Log.d(TAG, "switch_home selected")
|
||||
Logd(TAG, "switch_home selected")
|
||||
switchMode()
|
||||
return true
|
||||
}
|
||||
R.id.switchJS -> {
|
||||
Log.d(TAG, "switchJS selected")
|
||||
Logd(TAG, "switchJS selected")
|
||||
jsEnabled = !jsEnabled
|
||||
showWebContent()
|
||||
return true
|
||||
}
|
||||
R.id.text_speech -> {
|
||||
Log.d(TAG, "text_speech selected: $readerText")
|
||||
Logd(TAG, "text_speech selected: $readerText")
|
||||
if (tts != null) {
|
||||
if (tts!!.isSpeaking) tts?.stop()
|
||||
if (!ttsPlaying) {
|
||||
|
@ -255,7 +256,7 @@ class EpisodeHomeFragment : Fragment() {
|
|||
|
||||
@OptIn(UnstableApi::class) override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
Log.d(TAG, "onDestroyView")
|
||||
Logd(TAG, "onDestroyView")
|
||||
cleatWebview(binding.webView)
|
||||
cleatWebview(binding.readerView)
|
||||
_binding = null
|
||||
|
@ -267,7 +268,7 @@ class EpisodeHomeFragment : Fragment() {
|
|||
|
||||
@UnstableApi private fun updateAppearance() {
|
||||
if (currentItem == null) {
|
||||
Log.d(TAG, "updateAppearance currentItem is null")
|
||||
Logd(TAG, "updateAppearance currentItem is null")
|
||||
return
|
||||
}
|
||||
// onPrepareOptionsMenu(toolbar.menu)
|
||||
|
@ -284,7 +285,7 @@ class EpisodeHomeFragment : Fragment() {
|
|||
@JvmStatic
|
||||
fun newInstance(item: FeedItem): EpisodeHomeFragment {
|
||||
val fragment = EpisodeHomeFragment()
|
||||
Log.d(TAG, "item.itemIdentifier ${item.itemIdentifier}")
|
||||
Logd(TAG, "item.itemIdentifier ${item.itemIdentifier}")
|
||||
if (item.itemIdentifier != currentItem?.itemIdentifier) {
|
||||
currentItem = item
|
||||
} else {
|
||||
|
|
|
@ -51,10 +51,10 @@ import com.skydoves.balloon.ArrowOrientation
|
|||
import com.skydoves.balloon.ArrowOrientationRules
|
||||
import com.skydoves.balloon.Balloon
|
||||
import com.skydoves.balloon.BalloonAnimation
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
@ -93,7 +93,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
private var actionButton1: ItemActionButton? = null
|
||||
private var actionButton2: ItemActionButton? = null
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
// private var disposable: Disposable? = null
|
||||
private var controller: PlaybackController? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -258,7 +258,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
_binding = null
|
||||
EventBus.getDefault().unregister(this)
|
||||
controller?.release()
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
root.removeView(webvDescription)
|
||||
webvDescription.clearHistory()
|
||||
webvDescription.clearCache(true)
|
||||
|
@ -411,33 +411,51 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
load()
|
||||
}
|
||||
|
||||
// @UnstableApi private fun load0() {
|
||||
// disposable?.dispose()
|
||||
// if (!itemsLoaded) progbarLoading.visibility = View.VISIBLE
|
||||
//
|
||||
// Logd(TAG, "load() called")
|
||||
// disposable = Observable.fromCallable<FeedItem?> { this.loadInBackground() }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ result: FeedItem? ->
|
||||
// progbarLoading.visibility = View.GONE
|
||||
// item = result
|
||||
// onFragmentLoaded()
|
||||
// itemsLoaded = true
|
||||
// },
|
||||
// { error: Throwable? ->
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// })
|
||||
// }
|
||||
|
||||
@UnstableApi private fun load() {
|
||||
disposable?.dispose()
|
||||
if (!itemsLoaded) progbarLoading.visibility = View.VISIBLE
|
||||
|
||||
Logd(TAG, "load() called")
|
||||
disposable = Observable.fromCallable<FeedItem?> { this.loadInBackground() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ result: FeedItem? ->
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
scope.launch {
|
||||
try {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
val feedItem = item
|
||||
if (feedItem != null) {
|
||||
val duration = feedItem.media?.getDuration()?: Int.MAX_VALUE
|
||||
if (feedItem.description == null || feedItem.transcript == null) DBReader.loadTextDetailsOfFeedItem(feedItem)
|
||||
webviewData = ShownotesCleaner(requireContext(), feedItem.description?:"", duration).processShownotes()
|
||||
}
|
||||
feedItem
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
progbarLoading.visibility = View.GONE
|
||||
item = result
|
||||
onFragmentLoaded()
|
||||
itemsLoaded = true
|
||||
},
|
||||
{ error: Throwable? ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadInBackground(): FeedItem? {
|
||||
val feedItem = item
|
||||
if (feedItem != null) {
|
||||
val duration = feedItem.media?.getDuration()?: Int.MAX_VALUE
|
||||
DBReader.loadTextDetailsOfFeedItem(feedItem)
|
||||
webviewData = ShownotesCleaner(requireContext(), feedItem.description?:"", duration).processShownotes()
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
return feedItem
|
||||
}
|
||||
|
||||
fun setItem(item_: FeedItem) {
|
||||
|
@ -446,15 +464,11 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
companion object {
|
||||
private const val TAG = "EpisodeInfoFragment"
|
||||
private const val ARG_FEEDITEM = "feeditem"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(item: FeedItem): EpisodeInfoFragment {
|
||||
val fragment = EpisodeInfoFragment()
|
||||
fragment.setItem(item)
|
||||
// val args = Bundle()
|
||||
// args.putSerializable(ARG_FEEDITEM, item)
|
||||
// fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package ac.mdiq.podcini.ui.fragment
|
|||
import ac.mdiq.podcini.R
|
||||
import ac.mdiq.podcini.databinding.FeedinfoBinding
|
||||
import ac.mdiq.podcini.net.discovery.CombinedSearcher
|
||||
import ac.mdiq.podcini.storage.DBReader
|
||||
import ac.mdiq.podcini.storage.DBTasks
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedFunding
|
||||
|
@ -13,6 +12,7 @@ import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
|||
import ac.mdiq.podcini.ui.statistics.feed.FeedStatisticsFragment
|
||||
import ac.mdiq.podcini.ui.view.ToolbarIconTintManager
|
||||
import ac.mdiq.podcini.util.IntentUtils
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.ShareUtils
|
||||
import ac.mdiq.podcini.util.syndication.HtmlToPlainText
|
||||
import android.R.string
|
||||
|
@ -44,12 +44,10 @@ import com.google.android.material.appbar.CollapsingToolbarLayout
|
|||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.MaybeEmitter
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
/**
|
||||
|
@ -57,14 +55,10 @@ import org.apache.commons.lang3.StringUtils
|
|||
*/
|
||||
@UnstableApi
|
||||
class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||
private val addLocalFolderLauncher = registerForActivityResult<Uri?, Uri>(AddLocalFolder()) { uri: Uri? -> this.addLocalFolderResult(uri) }
|
||||
|
||||
private var _binding: FeedinfoBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private var feed: Feed? = null
|
||||
private var disposable: Disposable? = null
|
||||
|
||||
private lateinit var imgvCover: ImageView
|
||||
private lateinit var txtvTitle: TextView
|
||||
private lateinit var txtvDescription: TextView
|
||||
|
@ -77,6 +71,10 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
private lateinit var header: View
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
|
||||
private val addLocalFolderLauncher = registerForActivityResult<Uri?, Uri>(AddLocalFolder()) {
|
||||
uri: Uri? -> this.addLocalFolderResult(uri)
|
||||
}
|
||||
|
||||
private val copyUrlToClipboard = View.OnClickListener {
|
||||
if (feed != null && feed!!.download_url != null) {
|
||||
val url: String = feed!!.download_url!!
|
||||
|
@ -90,7 +88,7 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = FeedinfoBinding.inflate(inflater)
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
toolbar = binding.toolbar
|
||||
toolbar.title = ""
|
||||
toolbar.inflateMenu(R.menu.feedinfo)
|
||||
|
@ -134,7 +132,8 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
txtvUrl.setOnClickListener(copyUrlToClipboard)
|
||||
|
||||
val feedId = requireArguments().getLong(EXTRA_FEED_ID)
|
||||
// val feedId = requireArguments().getLong(EXTRA_FEED_ID)
|
||||
val feedId = feed!!.id
|
||||
parentFragmentManager.beginTransaction().replace(R.id.statisticsFragmentContainer,
|
||||
FeedStatisticsFragment.newInstance(feedId, false), "feed_statistics_fragment")
|
||||
.commitAllowingStateLoss()
|
||||
|
@ -144,22 +143,8 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
(activity as MainActivity).loadChildFragment(fragment, TransitionEffect.SLIDE)
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val feedId = requireArguments().getLong(EXTRA_FEED_ID)
|
||||
disposable = Maybe.create { emitter: MaybeEmitter<Feed?> ->
|
||||
val feed: Feed? = DBReader.getFeed(feedId)
|
||||
if (feed != null) emitter.onSuccess(feed)
|
||||
else emitter.onComplete()
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ result: Feed? ->
|
||||
feed = result
|
||||
showFeed()
|
||||
}, { error: Throwable? -> Log.d(TAG, Log.getStackTraceString(error)) }, {})
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
|
@ -171,28 +156,11 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
private fun showFeed() {
|
||||
if (feed == null) return
|
||||
Log.d(TAG, "Language is " + feed!!.language)
|
||||
Log.d(TAG, "Author is " + feed!!.author)
|
||||
Log.d(TAG, "URL is " + feed!!.download_url)
|
||||
// if (!feed?.imageUrl.isNullOrBlank()) {
|
||||
// Glide.with(this)
|
||||
// .load(feed!!.imageUrl)
|
||||
// .apply(RequestOptions()
|
||||
// .placeholder(R.color.light_gray)
|
||||
// .error(R.color.light_gray)
|
||||
// .fitCenter()
|
||||
// .dontAnimate())
|
||||
// .into(imgvCover)
|
||||
// Glide.with(this)
|
||||
// .load(feed!!.imageUrl)
|
||||
// .apply(RequestOptions()
|
||||
// .placeholder(R.color.image_readability_tint)
|
||||
// .error(R.color.image_readability_tint)
|
||||
// .transform(FastBlurTransformation())
|
||||
// .dontAnimate())
|
||||
// .into(imgvBackground)
|
||||
// }
|
||||
Logd(TAG, "Language is " + feed!!.language)
|
||||
Logd(TAG, "Author is " + feed!!.author)
|
||||
Logd(TAG, "URL is " + feed!!.download_url)
|
||||
|
||||
// TODO: need to generate blurred image for background
|
||||
imgvCover.load(feed!!.imageUrl) {
|
||||
placeholder(R.color.light_gray)
|
||||
error(R.mipmap.ic_launcher)
|
||||
|
@ -244,18 +212,21 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
refreshToolbarState()
|
||||
}
|
||||
|
||||
fun setFeed(feed_: Feed) {
|
||||
feed = feed_
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
disposable?.dispose()
|
||||
feed = null
|
||||
}
|
||||
|
||||
private fun refreshToolbarState() {
|
||||
toolbar.menu?.findItem(R.id.reconnect_local_folder)?.setVisible(feed != null && feed!!.isLocalFeed)
|
||||
toolbar.menu?.findItem(R.id.share_item)?.setVisible(feed != null && !feed!!.isLocalFeed)
|
||||
toolbar.menu?.findItem(R.id.visit_website_item)
|
||||
?.setVisible(feed != null && feed!!.link != null && IntentUtils.isCallable(requireContext(),
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(feed!!.link))))
|
||||
?.setVisible(feed != null && feed!!.link != null && IntentUtils.isCallable(requireContext(), Intent(Intent.ACTION_VIEW, Uri.parse(feed!!.link))))
|
||||
toolbar.menu?.findItem(R.id.edit_feed_url_item)?.setVisible(feed != null && !feed!!.isLocalFeed)
|
||||
}
|
||||
|
||||
|
@ -302,18 +273,37 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
@UnstableApi private fun reconnectLocalFolder(uri: Uri) {
|
||||
if (feed == null) return
|
||||
|
||||
Completable.fromAction {
|
||||
requireActivity().contentResolver
|
||||
.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
// Completable.fromAction {
|
||||
// requireActivity().contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
// val documentFile = DocumentFile.fromTreeUri(requireContext(), uri)
|
||||
// requireNotNull(documentFile) { "Unable to retrieve document tree" }
|
||||
// feed!!.download_url = Feed.PREFIX_LOCAL_FOLDER + uri.toString()
|
||||
// DBTasks.updateFeed(requireContext(), feed!!, true)
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ (activity as MainActivity).showSnackbarAbovePlayer(string.ok, Snackbar.LENGTH_SHORT) },
|
||||
// { error: Throwable -> (activity as MainActivity).showSnackbarAbovePlayer(error.localizedMessage, Snackbar.LENGTH_LONG) })
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
scope.launch {
|
||||
try {
|
||||
withContext(Dispatchers.IO) {
|
||||
requireActivity().contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
val documentFile = DocumentFile.fromTreeUri(requireContext(), uri)
|
||||
requireNotNull(documentFile) { "Unable to retrieve document tree" }
|
||||
feed!!.download_url = Feed.PREFIX_LOCAL_FOLDER + uri.toString()
|
||||
DBTasks.updateFeed(requireContext(), feed!!, true)
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ (activity as MainActivity).showSnackbarAbovePlayer(string.ok, Snackbar.LENGTH_SHORT) },
|
||||
{ error: Throwable -> (activity as MainActivity).showSnackbarAbovePlayer(error.localizedMessage, Snackbar.LENGTH_LONG) })
|
||||
withContext(Dispatchers.Main) {
|
||||
(activity as MainActivity).showSnackbarAbovePlayer(string.ok, Snackbar.LENGTH_SHORT)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
withContext(Dispatchers.Main) {
|
||||
(activity as MainActivity).showSnackbarAbovePlayer(e.localizedMessage, Snackbar.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AddLocalFolder : ActivityResultContracts.OpenDocumentTree() {
|
||||
|
@ -323,13 +313,11 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_FEED_ID = "ac.mdiq.podcini.extra.feedId"
|
||||
private const val TAG = "FeedInfoActivity"
|
||||
|
||||
fun newInstance(feed: Feed): FeedInfoFragment {
|
||||
val fragment = FeedInfoFragment()
|
||||
val arguments = Bundle()
|
||||
arguments.putLong(EXTRA_FEED_ID, feed.id)
|
||||
fragment.arguments = arguments
|
||||
fragment.setFeed(feed)
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,19 +47,12 @@ import com.google.android.material.snackbar.Snackbar
|
|||
import com.joanzapata.iconify.Iconify
|
||||
import com.leinardi.android.speeddial.SpeedDialActionItem
|
||||
import com.leinardi.android.speeddial.SpeedDialView
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.Semaphore
|
||||
|
||||
|
@ -84,9 +77,10 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
private var headerCreated = false
|
||||
private var feedID: Long = 0
|
||||
private var feed: Feed? = null
|
||||
private var disposable: Disposable? = null
|
||||
// private var disposable: Disposable? = null
|
||||
|
||||
private val ioScope = CoroutineScope(Dispatchers.IO)
|
||||
private val scope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -217,7 +211,9 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
_binding = null
|
||||
_speedDialBinding = null
|
||||
EventBus.getDefault().unregister(this)
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
ioScope.cancel()
|
||||
scope.cancel()
|
||||
adapter.endSelectMode()
|
||||
|
||||
tts?.stop()
|
||||
|
@ -483,20 +479,33 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
}
|
||||
|
||||
private fun showErrorDetails() {
|
||||
Maybe.fromCallable<DownloadResult>(
|
||||
Callable {
|
||||
// Maybe.fromCallable<DownloadResult>(
|
||||
// Callable {
|
||||
// val feedDownloadLog: List<DownloadResult> = DBReader.getFeedDownloadLog(feedID)
|
||||
// if (feedDownloadLog.isEmpty() || feedDownloadLog[0].isSuccessful) return@Callable null
|
||||
// feedDownloadLog[0]
|
||||
// })
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { downloadStatus: DownloadResult ->
|
||||
// DownloadLogDetailsDialog(requireContext(), downloadStatus).show()
|
||||
// },
|
||||
// { error: Throwable -> error.printStackTrace() },
|
||||
// { DownloadLogFragment().show(childFragmentManager, null) })
|
||||
|
||||
scope.launch {
|
||||
val downloadResult = withContext(Dispatchers.IO) {
|
||||
val feedDownloadLog: List<DownloadResult> = DBReader.getFeedDownloadLog(feedID)
|
||||
if (feedDownloadLog.isEmpty() || feedDownloadLog[0].isSuccessful) return@Callable null
|
||||
feedDownloadLog[0]
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ downloadStatus: DownloadResult ->
|
||||
DownloadLogDetailsDialog(requireContext(), downloadStatus).show()
|
||||
},
|
||||
{ error: Throwable -> error.printStackTrace() },
|
||||
{ DownloadLogFragment().show(childFragmentManager, null) })
|
||||
if (feedDownloadLog.isEmpty() || feedDownloadLog[0].isSuccessful) null else feedDownloadLog[0]
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (downloadResult != null) DownloadLogDetailsDialog(requireContext(), downloadResult).show()
|
||||
else DownloadLogFragment().show(childFragmentManager, null)
|
||||
}
|
||||
}.invokeOnCompletion { throwable ->
|
||||
throwable?.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
@UnstableApi private fun showFeedInfo() {
|
||||
|
@ -508,19 +517,6 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
|
||||
private fun loadFeedImage() {
|
||||
if (!feed?.imageUrl.isNullOrBlank()) {
|
||||
// binding.imgvBackground.load(feed!!.imageUrl) {
|
||||
// placeholder(R.color.image_readability_tint)
|
||||
// error(R.color.image_readability_tint)
|
||||
// }
|
||||
// Glide.with(this)
|
||||
// .load(feed!!.imageUrl)
|
||||
// .apply(RequestOptions()
|
||||
// .placeholder(R.color.image_readability_tint)
|
||||
// .error(R.color.image_readability_tint)
|
||||
// .transform(FastBlurTransformation())
|
||||
// .dontAnimate())
|
||||
// .into(binding.imgvBackground)
|
||||
|
||||
binding.header.imgvCover.load(feed!!.imageUrl) {
|
||||
placeholder(R.color.light_gray)
|
||||
error(R.mipmap.ic_launcher)
|
||||
|
@ -529,17 +525,54 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
}
|
||||
|
||||
@UnstableApi private fun loadItems() {
|
||||
disposable?.dispose()
|
||||
disposable = Observable.fromCallable<Feed?> { this.loadData() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ result: Feed? ->
|
||||
feed = result
|
||||
Logd(TAG, "loadItems subscribe called ${feed?.title}")
|
||||
if (feed != null) {
|
||||
// disposable?.dispose()
|
||||
// disposable = Observable.fromCallable<Feed?> { this.loadData() }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { result: Feed? ->
|
||||
// feed = result
|
||||
// Logd(TAG, "loadItems subscribe called ${feed?.title}")
|
||||
// if (feed != null) {
|
||||
// var hasNonMediaItems = false
|
||||
// for (item in feed!!.items) {
|
||||
// if (item.media == null) {
|
||||
// hasNonMediaItems = true
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// if (hasNonMediaItems) {
|
||||
// ioScope.launch {
|
||||
// if (!ttsReady) {
|
||||
// initializeTTS(requireContext())
|
||||
// semaphore.acquire()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// swipeActions.setFilter(feed?.itemFilter)
|
||||
// refreshHeaderView()
|
||||
// binding.progressBar.visibility = View.GONE
|
||||
// adapter.setDummyViews(0)
|
||||
// if (feed != null) adapter.updateItems(feed!!.items)
|
||||
// binding.header.counts.text = (feed?.items?.size?:0).toString()
|
||||
// updateToolbar()
|
||||
// }, { error: Throwable? ->
|
||||
// feed = null
|
||||
// refreshHeaderView()
|
||||
// adapter.setDummyViews(0)
|
||||
// adapter.updateItems(emptyList())
|
||||
// updateToolbar()
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
feed = withContext(Dispatchers.IO) {
|
||||
val feed_ = loadData()
|
||||
if (feed_ != null) {
|
||||
var hasNonMediaItems = false
|
||||
for (item in feed!!.items) {
|
||||
for (item in feed_!!.items) {
|
||||
if (item.media == null) {
|
||||
hasNonMediaItems = true
|
||||
break
|
||||
|
@ -554,6 +587,10 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
}
|
||||
}
|
||||
}
|
||||
feed_
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
Logd(TAG, "loadItems subscribe called ${feed?.title}")
|
||||
swipeActions.setFilter(feed?.itemFilter)
|
||||
refreshHeaderView()
|
||||
binding.progressBar.visibility = View.GONE
|
||||
|
@ -561,14 +598,16 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
if (feed != null) adapter.updateItems(feed!!.items)
|
||||
binding.header.counts.text = (feed?.items?.size?:0).toString()
|
||||
updateToolbar()
|
||||
}, { error: Throwable? ->
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
feed = null
|
||||
refreshHeaderView()
|
||||
adapter.setDummyViews(0)
|
||||
adapter.updateItems(emptyList())
|
||||
updateToolbar()
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
})
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadData(): Feed? {
|
||||
|
|
|
@ -16,6 +16,7 @@ import ac.mdiq.podcini.ui.dialog.AuthenticationDialog
|
|||
import ac.mdiq.podcini.ui.dialog.EpisodeFilterDialog
|
||||
import ac.mdiq.podcini.ui.dialog.FeedPreferenceSkipDialog
|
||||
import ac.mdiq.podcini.ui.dialog.TagSettingsDialog
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.settings.SkipIntroEndingChangedEvent
|
||||
import ac.mdiq.podcini.util.event.settings.SpeedPresetChangedEvent
|
||||
import ac.mdiq.podcini.util.event.settings.VolumeAdaptionChangedEvent
|
||||
|
@ -40,12 +41,7 @@ import androidx.preference.PreferenceFragmentCompat
|
|||
import androidx.preference.SwitchPreferenceCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.MaybeEmitter
|
||||
import io.reactivex.MaybeOnSubscribe
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutionException
|
||||
|
@ -54,13 +50,14 @@ class FeedSettingsFragment : Fragment() {
|
|||
private var _binding: FeedsettingsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
// private var disposable: Disposable? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = FeedsettingsBinding.inflate(inflater)
|
||||
|
||||
val feedId = requireArguments().getLong(EXTRA_FEED_ID)
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
|
||||
val toolbar = binding.toolbar
|
||||
toolbar.setNavigationOnClickListener { parentFragmentManager.popBackStack() }
|
||||
|
@ -69,16 +66,31 @@ class FeedSettingsFragment : Fragment() {
|
|||
.replace(R.id.settings_fragment_container, FeedSettingsPreferenceFragment.newInstance(feedId), "settings_fragment")
|
||||
.commitAllowingStateLoss()
|
||||
|
||||
disposable = Maybe.create(MaybeOnSubscribe { emitter: MaybeEmitter<Feed> ->
|
||||
val feed = DBReader.getFeed(feedId)
|
||||
if (feed != null) emitter.onSuccess(feed)
|
||||
else emitter.onComplete()
|
||||
} as MaybeOnSubscribe<Feed>)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ result: Feed -> toolbar.subtitle = result.title },
|
||||
{ error: Throwable? -> Log.d(TAG, Log.getStackTraceString(error)) },
|
||||
{})
|
||||
// disposable = Maybe.create(MaybeOnSubscribe { emitter: MaybeEmitter<Feed> ->
|
||||
// val feed = DBReader.getFeed(feedId)
|
||||
// if (feed != null) emitter.onSuccess(feed)
|
||||
// else emitter.onComplete()
|
||||
// } as MaybeOnSubscribe<Feed>)
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ result: Feed -> toolbar.subtitle = result.title },
|
||||
// { error: Throwable? -> Logd(TAG, Log.getStackTraceString(error)) },
|
||||
// {})
|
||||
|
||||
scope.launch {
|
||||
val feed = withContext(Dispatchers.IO) {
|
||||
DBReader.getFeed(feedId)
|
||||
}
|
||||
if (feed!= null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
toolbar.subtitle = feed.title
|
||||
}
|
||||
}
|
||||
}.invokeOnCompletion { throwable ->
|
||||
if (throwable!= null) {
|
||||
Logd(TAG, Log.getStackTraceString(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
@ -86,12 +98,14 @@ class FeedSettingsFragment : Fragment() {
|
|||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
disposable?.dispose()
|
||||
scope.cancel()
|
||||
// disposable?.dispose()
|
||||
}
|
||||
|
||||
class FeedSettingsPreferenceFragment : PreferenceFragmentCompat() {
|
||||
private var feed: Feed? = null
|
||||
private var disposable: Disposable? = null
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
// private var disposable: Disposable? = null
|
||||
private var feedPreferences: FeedPreferences? = null
|
||||
|
||||
var notificationPermissionDenied: Boolean = false
|
||||
|
@ -123,15 +137,48 @@ class FeedSettingsFragment : Fragment() {
|
|||
findPreference<Preference>(PREF_SCREEN)!!.isVisible = false
|
||||
|
||||
val feedId = requireArguments().getLong(EXTRA_FEED_ID)
|
||||
disposable = Maybe.create { emitter: MaybeEmitter<Feed?> ->
|
||||
val feed = DBReader.getFeed(feedId)
|
||||
if (feed != null) emitter.onSuccess(feed)
|
||||
else emitter.onComplete()
|
||||
// disposable = Maybe.create { emitter: MaybeEmitter<Feed?> ->
|
||||
// val feed = DBReader.getFeed(feedId)
|
||||
// if (feed != null) emitter.onSuccess(feed)
|
||||
// else emitter.onComplete()
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ result: Feed? ->
|
||||
// feed = result
|
||||
// feedPreferences = feed!!.preferences
|
||||
//
|
||||
// setupAutoDownloadGlobalPreference()
|
||||
// setupAutoDownloadPreference()
|
||||
// setupKeepUpdatedPreference()
|
||||
// setupAutoDeletePreference()
|
||||
// setupVolumeAdaptationPreferences()
|
||||
//// setupNewEpisodesAction()
|
||||
// setupAuthentificationPreference()
|
||||
// setupEpisodeFilterPreference()
|
||||
// setupPlaybackSpeedPreference()
|
||||
// setupFeedAutoSkipPreference()
|
||||
//// setupEpisodeNotificationPreference()
|
||||
// setupTags()
|
||||
//
|
||||
// updateAutoDeleteSummary()
|
||||
// updateVolumeAdaptationValue()
|
||||
// updateAutoDownloadEnabled()
|
||||
//// updateNewEpisodesAction()
|
||||
//
|
||||
// if (feed!!.isLocalFeed) {
|
||||
// findPreference<Preference>(PREF_AUTHENTICATION)!!.isVisible = false
|
||||
// findPreference<Preference>(PREF_CATEGORY_AUTO_DOWNLOAD)!!.isVisible = false
|
||||
// }
|
||||
// findPreference<Preference>(PREF_SCREEN)!!.isVisible = true
|
||||
// }, { error: Throwable? -> Logd(TAG, Log.getStackTraceString(error)) }, {})
|
||||
|
||||
scope.launch {
|
||||
feed = withContext(Dispatchers.IO) {
|
||||
DBReader.getFeed(feedId)
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ result: Feed? ->
|
||||
feed = result
|
||||
if (feed!= null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
feedPreferences = feed!!.preferences
|
||||
|
||||
setupAutoDownloadGlobalPreference()
|
||||
|
@ -139,30 +186,34 @@ class FeedSettingsFragment : Fragment() {
|
|||
setupKeepUpdatedPreference()
|
||||
setupAutoDeletePreference()
|
||||
setupVolumeAdaptationPreferences()
|
||||
// setupNewEpisodesAction()
|
||||
setupAuthentificationPreference()
|
||||
setupEpisodeFilterPreference()
|
||||
setupPlaybackSpeedPreference()
|
||||
setupFeedAutoSkipPreference()
|
||||
// setupEpisodeNotificationPreference()
|
||||
setupTags()
|
||||
|
||||
updateAutoDeleteSummary()
|
||||
updateVolumeAdaptationValue()
|
||||
updateAutoDownloadEnabled()
|
||||
// updateNewEpisodesAction()
|
||||
|
||||
if (feed!!.isLocalFeed) {
|
||||
findPreference<Preference>(PREF_AUTHENTICATION)!!.isVisible = false
|
||||
findPreference<Preference>(PREF_CATEGORY_AUTO_DOWNLOAD)!!.isVisible = false
|
||||
}
|
||||
findPreference<Preference>(PREF_SCREEN)!!.isVisible = true
|
||||
}, { error: Throwable? -> Log.d(TAG, Log.getStackTraceString(error)) }, {})
|
||||
}
|
||||
}
|
||||
}.invokeOnCompletion { throwable ->
|
||||
if (throwable!= null) {
|
||||
Logd(TAG, Log.getStackTraceString(throwable))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
disposable?.dispose()
|
||||
scope.cancel()
|
||||
// disposable?.dispose()
|
||||
}
|
||||
|
||||
private fun setupFeedAutoSkipPreference() {
|
||||
|
|
|
@ -6,15 +6,15 @@ import ac.mdiq.podcini.databinding.NavListBinding
|
|||
import ac.mdiq.podcini.preferences.UserPreferences
|
||||
import ac.mdiq.podcini.storage.DBReader
|
||||
import ac.mdiq.podcini.storage.NavDrawerData
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
||||
import ac.mdiq.podcini.ui.adapter.NavListAdapter
|
||||
import ac.mdiq.podcini.ui.activity.appstartintent.MainActivityStarter
|
||||
import ac.mdiq.podcini.ui.utils.ThemeUtils
|
||||
import ac.mdiq.podcini.ui.dialog.*
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.MenuItemUtils
|
||||
import ac.mdiq.podcini.ui.adapter.NavListAdapter
|
||||
import ac.mdiq.podcini.ui.dialog.DrawerPreferencesDialog
|
||||
import ac.mdiq.podcini.ui.dialog.SubscriptionsFilterDialog
|
||||
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
||||
import ac.mdiq.podcini.ui.utils.ThemeUtils
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.FeedListUpdateEvent
|
||||
import ac.mdiq.podcini.util.event.QueueEvent
|
||||
import ac.mdiq.podcini.util.event.UnreadItemsUpdateEvent
|
||||
|
@ -28,12 +28,13 @@ import android.graphics.Color
|
|||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.widget.ProgressBar
|
||||
import android.view.ContextMenu
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.util.Pair
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
|
@ -42,10 +43,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
|
@ -58,11 +56,9 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
|
||||
private var navDrawerData: NavDrawerData? = null
|
||||
private var flatItemList: List<NavDrawerData.FeedDrawerItem>? = null
|
||||
private var contextPressedItem: NavDrawerData.FeedDrawerItem? = null
|
||||
private var disposable: Disposable? = null
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
private lateinit var navAdapter: NavListAdapter
|
||||
private lateinit var progressBar: ProgressBar
|
||||
|
||||
private var openFolders: MutableSet<String> = HashSet()
|
||||
|
||||
|
@ -70,7 +66,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
_binding = NavListBinding.inflate(inflater)
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
setupDrawerRoundBackground(binding.root)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, insets: WindowInsetsCompat ->
|
||||
val bars: Insets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
|
@ -88,9 +84,8 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
|
||||
val preferences: SharedPreferences = requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
// TODO: what is this?
|
||||
openFolders = HashSet(preferences.getStringSet(PREF_OPEN_FOLDERS, HashSet<String>())) // Must not modify
|
||||
openFolders = HashSet(preferences.getStringSet(PREF_OPEN_FOLDERS, HashSet<String>())!!) // Must not modify
|
||||
|
||||
progressBar = binding.progressBar
|
||||
val navList = binding.navRecycler
|
||||
navAdapter = NavListAdapter(itemAccess, requireActivity())
|
||||
navAdapter.setHasStableIds(true)
|
||||
|
@ -129,64 +124,8 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
super.onDestroyView()
|
||||
_binding = null
|
||||
EventBus.getDefault().unregister(this)
|
||||
disposable?.dispose()
|
||||
|
||||
requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
.unregisterOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo)
|
||||
val inflater: MenuInflater = requireActivity().menuInflater
|
||||
if (contextPressedItem != null) {
|
||||
menu.setHeaderTitle(contextPressedItem!!.title)
|
||||
inflater.inflate(R.menu.nav_feed_context, menu)
|
||||
// episodes are not loaded, so we cannot check if the podcast has new or unplayed ones!
|
||||
}
|
||||
MenuItemUtils.setOnClickListeners(menu
|
||||
) { item: MenuItem -> this.onContextItemSelected(item) }
|
||||
}
|
||||
|
||||
override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
val pressedItem: NavDrawerData.FeedDrawerItem? = contextPressedItem
|
||||
contextPressedItem = null
|
||||
if (pressedItem == null) return false
|
||||
|
||||
return onFeedContextMenuClicked(pressedItem.feed, item)
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class) private fun onFeedContextMenuClicked(feed: Feed, item: MenuItem): Boolean {
|
||||
val itemId = item.itemId
|
||||
when (itemId) {
|
||||
R.id.edit_tags -> {
|
||||
if (feed.preferences != null) TagSettingsDialog.newInstance(listOf(feed.preferences!!)).show(childFragmentManager, TagSettingsDialog.TAG)
|
||||
return true
|
||||
}
|
||||
R.id.rename_item -> {
|
||||
RenameItemDialog(activity as Activity, feed).show()
|
||||
return true
|
||||
}
|
||||
R.id.remove_feed -> {
|
||||
RemoveFeedDialog.show(requireContext(), feed) {
|
||||
if (feed.id.toString() == getLastNavFragment(requireContext())) {
|
||||
(activity as MainActivity).loadFragment(UserPreferences.defaultPage, null)
|
||||
// Make sure fragment is hidden before actually starting to delete
|
||||
requireActivity().supportFragmentManager.executePendingTransactions()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
else -> return super.onContextItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onTagContextMenuClicked(drawerItem: NavDrawerData.FeedDrawerItem?, item: MenuItem): Boolean {
|
||||
val itemId = item.itemId
|
||||
if (itemId == R.id.rename_folder_item) {
|
||||
RenameItemDialog(activity as Activity, drawerItem).show()
|
||||
return true
|
||||
}
|
||||
return super.onContextItemSelected(item)
|
||||
scope.cancel()
|
||||
requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).unregisterOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
@ -194,7 +133,6 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
loadData()
|
||||
}
|
||||
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onFeedListChanged(event: FeedListUpdateEvent?) {
|
||||
loadData()
|
||||
|
@ -202,7 +140,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onQueueChanged(event: QueueEvent) {
|
||||
Log.d(TAG, "onQueueChanged($event)")
|
||||
Logd(TAG, "onQueueChanged($event)")
|
||||
// we are only interested in the number of queue items, not download status or position
|
||||
if (event.action == QueueEvent.Action.DELETED_MEDIA || event.action == QueueEvent.Action.SORTED || event.action == QueueEvent.Action.MOVED) return
|
||||
|
||||
|
@ -245,12 +183,18 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
override val numberOfNewItems: Int
|
||||
get() = navDrawerData?.numNewItems ?: 0
|
||||
|
||||
override val numberOfItems: Int
|
||||
get() = navDrawerData?.numItems ?: 0
|
||||
|
||||
override val numberOfDownloadedItems: Int
|
||||
get() = navDrawerData?.numDownloadedItems ?: 0
|
||||
|
||||
override val reclaimableItems: Int
|
||||
get() = navDrawerData?.reclaimableSpace ?: 0
|
||||
|
||||
override val numberOfFeeds: Int
|
||||
get() = navDrawerData?.numFeeds ?: 0
|
||||
|
||||
override val feedCounterSum: Int
|
||||
get() {
|
||||
if (navDrawerData == null) return 0
|
||||
|
@ -297,7 +241,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
}
|
||||
return true
|
||||
} else {
|
||||
contextPressedItem = flatItemList!![position - navAdapter.subscriptionOffset]
|
||||
// contextPressedItem = flatItemList!![position - navAdapter.subscriptionOffset]
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -308,22 +252,21 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
}
|
||||
|
||||
private fun loadData() {
|
||||
disposable = Observable.fromCallable {
|
||||
scope.launch {
|
||||
try {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
val data: NavDrawerData = DBReader.getNavDrawerData(UserPreferences.subscriptionsFilter)
|
||||
Pair(data, makeFlatDrawerData(data.items, 0))
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ result: Pair<NavDrawerData, List<NavDrawerData.FeedDrawerItem>> ->
|
||||
withContext(Dispatchers.Main) {
|
||||
navDrawerData = result.first
|
||||
flatItemList = result.second
|
||||
navAdapter.notifyDataSetChanged()
|
||||
progressBar.visibility = View.GONE // Stays hidden once there is something in the list
|
||||
}, { error: Throwable? ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
progressBar.visibility = View.GONE
|
||||
})
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeFlatDrawerData(items: List<NavDrawerData.FeedDrawerItem>, layer: Int): List<NavDrawerData.FeedDrawerItem> {
|
||||
|
@ -362,7 +305,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
)
|
||||
|
||||
fun saveLastNavFragment(context: Context, tag: String?) {
|
||||
Log.d(TAG, "saveLastNavFragment(tag: $tag)")
|
||||
Logd(TAG, "saveLastNavFragment(tag: $tag)")
|
||||
val prefs: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
val edit: SharedPreferences.Editor = prefs.edit()
|
||||
if (tag != null) edit.putString(PREF_LAST_FRAGMENT_TAG, tag)
|
||||
|
@ -374,7 +317,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
fun getLastNavFragment(context: Context): String {
|
||||
val prefs: SharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
val lastFragment: String = prefs.getString(PREF_LAST_FRAGMENT_TAG, SubscriptionFragment.TAG)?:""
|
||||
Log.d(TAG, "getLastNavFragment() -> $lastFragment")
|
||||
Logd(TAG, "getLastNavFragment() -> $lastFragment")
|
||||
return lastFragment
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,12 +54,12 @@ import androidx.media3.common.util.UnstableApi
|
|||
import coil.load
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.observers.DisposableMaybeObserver
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
@ -98,6 +98,8 @@ class OnlineFeedViewFragment : Fragment() {
|
|||
|
||||
private var dialog: Dialog? = null
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
private var download: Disposable? = null
|
||||
private var parser: Disposable? = null
|
||||
private var updater: Disposable? = null
|
||||
|
@ -197,6 +199,18 @@ class OnlineFeedViewFragment : Fragment() {
|
|||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
}
|
||||
})
|
||||
// scope.launch(Dispatchers.IO) {
|
||||
// try {
|
||||
// startFeedDownload(url)
|
||||
// } catch (e: FeedUrlNotFoundException) {
|
||||
// tryToRetrieveFeedUrlBySearch(e)
|
||||
// } catch (e: Throwable) {
|
||||
// withContext(Dispatchers.Main) {
|
||||
// showNoPodcastFoundError()
|
||||
// Log.e(TAG, Log.getStackTraceString(e))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private fun tryToRetrieveFeedUrlBySearch(error: FeedUrlNotFoundException) {
|
||||
|
@ -269,16 +283,32 @@ class OnlineFeedViewFragment : Fragment() {
|
|||
.withInitiatedByUser(true)
|
||||
.build()
|
||||
|
||||
download = Observable.fromCallable {
|
||||
// download = Observable.fromCallable {
|
||||
// feeds = DBReader.getFeedList()
|
||||
// downloader = HttpDownloader(request)
|
||||
// downloader?.call()
|
||||
// downloader?.result
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ status: DownloadResult? -> if (request.destination != null) checkDownloadResult(status, request.destination) },
|
||||
// { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
val status = withContext(Dispatchers.IO) {
|
||||
feeds = DBReader.getFeedList()
|
||||
downloader = HttpDownloader(request)
|
||||
downloader?.call()
|
||||
downloader?.result
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ status: DownloadResult? -> if (request.destination != null) checkDownloadResult(status, request.destination) },
|
||||
{ error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
withContext(Dispatchers.Main) {
|
||||
if (request.destination != null) checkDownloadResult(status, request.destination)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkDownloadResult(status: DownloadResult?, destination: String) {
|
||||
|
@ -301,15 +331,30 @@ class OnlineFeedViewFragment : Fragment() {
|
|||
|
||||
@UnstableApi @Subscribe
|
||||
fun onFeedListChanged(event: FeedListUpdateEvent?) {
|
||||
updater = Observable.fromCallable { DBReader.getFeedList() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ feeds: List<Feed>? ->
|
||||
// updater = Observable.fromCallable { DBReader.getFeedList() }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { feeds: List<Feed>? ->
|
||||
// this@OnlineFeedViewFragment.feeds = feeds
|
||||
// handleUpdatedFeedStatus()
|
||||
// }, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) }
|
||||
// )
|
||||
scope.launch {
|
||||
try {
|
||||
val feeds = withContext(Dispatchers.IO) {
|
||||
DBReader.getFeedList()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
this@OnlineFeedViewFragment.feeds = feeds
|
||||
handleUpdatedFeedStatus()
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) }
|
||||
)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@UnstableApi @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
|
@ -317,25 +362,40 @@ class OnlineFeedViewFragment : Fragment() {
|
|||
handleUpdatedFeedStatus()
|
||||
}
|
||||
|
||||
private fun parseFeed(destination: String) {
|
||||
@OptIn(UnstableApi::class) private fun parseFeed(destination: String) {
|
||||
Logd(TAG, "Parsing feed")
|
||||
parser = Maybe.fromCallable { doParseFeed(destination) }
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeWith(object : DisposableMaybeObserver<FeedHandlerResult?>() {
|
||||
@UnstableApi override fun onSuccess(result: FeedHandlerResult) {
|
||||
showFeedInformation(result.feed, result.alternateFeedUrls)
|
||||
// parser = Maybe.fromCallable { doParseFeed(destination) }
|
||||
// .subscribeOn(Schedulers.computation())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribeWith(object : DisposableMaybeObserver<FeedHandlerResult?>() {
|
||||
// @UnstableApi override fun onSuccess(result: FeedHandlerResult) {
|
||||
// showFeedInformation(result.feed, result.alternateFeedUrls)
|
||||
// }
|
||||
//
|
||||
// override fun onComplete() {
|
||||
// // Ignore null result: We showed the discovery dialog.
|
||||
// }
|
||||
//
|
||||
// override fun onError(error: Throwable) {
|
||||
// showErrorDialog(error.message, "")
|
||||
// Logd(TAG, "Feed parser exception: " + Log.getStackTraceString(error))
|
||||
// }
|
||||
// })
|
||||
scope.launch {
|
||||
try {
|
||||
val result = withContext(Dispatchers.Default) {
|
||||
doParseFeed(destination)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (result != null) showFeedInformation(result.feed, result.alternateFeedUrls)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
withContext(Dispatchers.Main) {
|
||||
showErrorDialog(e.message, "")
|
||||
Logd(TAG, "Feed parser exception: " + Log.getStackTraceString(e))
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// Ignore null result: We showed the discovery dialog.
|
||||
}
|
||||
|
||||
override fun onError(error: Throwable) {
|
||||
showErrorDialog(error.message, "")
|
||||
Logd(TAG, "Feed parser exception: " + Log.getStackTraceString(error))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,7 @@ import ac.mdiq.podcini.net.discovery.PodcastSearcher
|
|||
import ac.mdiq.podcini.net.discovery.PodcastSearcherRegistry
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.ui.adapter.OnlineFeedsAdapter
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
|
@ -46,7 +47,7 @@ class OnlineSearchFragment : Fragment() {
|
|||
super.onCreate(savedInstanceState)
|
||||
|
||||
for (info in PodcastSearcherRegistry.searchProviders) {
|
||||
Log.d(TAG, "searchProvider: $info")
|
||||
Logd(TAG, "searchProvider: $info")
|
||||
if (info.searcher.javaClass.getName() == requireArguments().getString(ARG_SEARCHER)) {
|
||||
searchProvider = info.searcher
|
||||
break
|
||||
|
@ -58,7 +59,7 @@ class OnlineSearchFragment : Fragment() {
|
|||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = FragmentItunesSearchBinding.inflate(inflater)
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
gridView = binding.gridView
|
||||
adapter = OnlineFeedsAdapter(requireContext(), ArrayList())
|
||||
gridView.setAdapter(adapter)
|
||||
|
|
|
@ -14,6 +14,7 @@ import ac.mdiq.podcini.storage.DBWriter
|
|||
import ac.mdiq.podcini.util.event.playback.PlaybackHistoryEvent
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.util.Log
|
||||
import androidx.annotation.OptIn
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
|
@ -30,7 +31,7 @@ class PlaybackHistoryFragment : BaseEpisodesListFragment() {
|
|||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val root = super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
toolbar.inflateMenu(R.menu.playback_history)
|
||||
toolbar.setTitle(R.string.playback_history_label)
|
||||
updateToolbar()
|
||||
|
|
|
@ -16,6 +16,7 @@ import ac.mdiq.podcini.ui.utils.ShownotesCleaner
|
|||
import ac.mdiq.podcini.ui.view.ShownotesWebView
|
||||
import ac.mdiq.podcini.util.ChapterUtils
|
||||
import ac.mdiq.podcini.util.DateFormatter
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.NetworkUtils.fetchHtmlSource
|
||||
import ac.mdiq.podcini.util.event.playback.PlaybackPositionEvent
|
||||
import android.animation.Animator
|
||||
|
@ -46,12 +47,7 @@ import coil.request.ErrorResult
|
|||
import coil.request.ImageRequest
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.MaybeEmitter
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.*
|
||||
import net.dankito.readability4j.Readability4J
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
|
@ -67,14 +63,14 @@ class PlayerDetailsFragment : Fragment() {
|
|||
private var _binding: PlayerDetailsFragmentBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private var prevItem: FeedItem? = null
|
||||
private var media: Playable? = null
|
||||
private var item: FeedItem? = null
|
||||
private var loadedMediaId: Any? = null
|
||||
private var displayedChapterIndex = -1
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
private var cleanedNotes: String? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var webViewLoader: Disposable? = null
|
||||
// private var webViewLoader: Disposable? = null
|
||||
private var controller: PlaybackController? = null
|
||||
|
||||
internal var showHomeText = false
|
||||
|
@ -82,7 +78,7 @@ class PlayerDetailsFragment : Fragment() {
|
|||
internal var readerhtml: String? = null
|
||||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
_binding = PlayerDetailsFragmentBinding.inflate(inflater)
|
||||
|
||||
binding.imgvCover.setOnClickListener { onPlayPause() }
|
||||
|
@ -94,7 +90,7 @@ class PlayerDetailsFragment : Fragment() {
|
|||
binding.butPrevChapter.setOnClickListener { seekToPrevChapter() }
|
||||
binding.butNextChapter.setOnClickListener { seekToNextChapter() }
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
shownoteView = binding.webview
|
||||
shownoteView.setTimecodeSelectedListener { time: Int? -> controller?.seekTo(time!!) }
|
||||
shownoteView.setPageFinishedListener {
|
||||
|
@ -123,7 +119,7 @@ class PlayerDetailsFragment : Fragment() {
|
|||
_binding = null
|
||||
controller?.release()
|
||||
controller = null
|
||||
Log.d(TAG, "Fragment destroyed")
|
||||
Logd(TAG, "Fragment destroyed")
|
||||
shownoteView.removeAllViews()
|
||||
shownoteView.destroy()
|
||||
}
|
||||
|
@ -132,60 +128,85 @@ class PlayerDetailsFragment : Fragment() {
|
|||
return shownoteView.onContextItemSelected(item)
|
||||
}
|
||||
|
||||
@UnstableApi private fun load() {
|
||||
Log.d(TAG, "load() called")
|
||||
webViewLoader?.dispose()
|
||||
// @UnstableApi private fun load0() {
|
||||
// webViewLoader?.dispose()
|
||||
//
|
||||
// val context = context ?: return
|
||||
// webViewLoader = Maybe.create { emitter: MaybeEmitter<String?> ->
|
||||
// if (item == null) {
|
||||
// media = controller?.getMedia()
|
||||
// if (media == null) {
|
||||
// emitter.onComplete()
|
||||
// return@create
|
||||
// }
|
||||
// if (media is FeedMedia) {
|
||||
// val feedMedia = media as FeedMedia
|
||||
// item = feedMedia.item
|
||||
// item?.setDescription(null)
|
||||
// showHomeText = false
|
||||
// homeText = null
|
||||
// }
|
||||
// }
|
||||
// if (item != null) {
|
||||
// media = item!!.media
|
||||
// if (item!!.description == null) DBReader.loadTextDetailsOfFeedItem(item!!)
|
||||
// if (prevItem?.itemIdentifier != item!!.itemIdentifier) cleanedNotes = null
|
||||
// if (cleanedNotes == null) {
|
||||
// Logd(TAG, "calling load description ${item!!.description==null} ${item!!.title}")
|
||||
// val shownotesCleaner = ShownotesCleaner(context, item?.description ?: "", media?.getDuration()?:0)
|
||||
// cleanedNotes = shownotesCleaner.processShownotes()
|
||||
// }
|
||||
// prevItem = item
|
||||
// emitter.onSuccess(cleanedNotes?:"")
|
||||
// } else emitter.onComplete()
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ data: String? ->
|
||||
// Logd(TAG, "subscribe: ${media?.getEpisodeTitle()}")
|
||||
// displayMediaInfo(media!!)
|
||||
// shownoteView.loadDataWithBaseURL("https://127.0.0.1", data!!, "text/html", "utf-8", "about:blank")
|
||||
// Logd(TAG, "Webview loaded")
|
||||
// }, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
// }
|
||||
|
||||
private fun load() {
|
||||
val context = context ?: return
|
||||
webViewLoader = Maybe.create { emitter: MaybeEmitter<String?> ->
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (item == null) {
|
||||
media = controller?.getMedia()
|
||||
if (media == null) {
|
||||
emitter.onComplete()
|
||||
return@create
|
||||
}
|
||||
if (media is FeedMedia) {
|
||||
if (media != null && media is FeedMedia) {
|
||||
val feedMedia = media as FeedMedia
|
||||
if (item?.itemIdentifier != feedMedia.item?.itemIdentifier) {
|
||||
item = feedMedia.item
|
||||
item?.setDescription(null)
|
||||
showHomeText = false
|
||||
homeText = null
|
||||
}
|
||||
}
|
||||
// Log.d(TAG, "webViewLoader ${item?.id} ${cleanedNotes==null} ${item!!.description==null} ${loadedMediaId == null} ${item?.media?.getIdentifier()} ${media?.getIdentifier()}")
|
||||
if (item != null) {
|
||||
if (cleanedNotes == null || item!!.description == null || loadedMediaId != media?.getIdentifier()) {
|
||||
Log.d(TAG, "calling load description ${cleanedNotes==null} ${item!!.description==null} ${item!!.media?.getIdentifier()} ${media?.getIdentifier()}")
|
||||
// printStackTrace()
|
||||
DBReader.loadTextDetailsOfFeedItem(item!!)
|
||||
loadedMediaId = media?.getIdentifier()
|
||||
media = item!!.media
|
||||
if (item!!.description == null) DBReader.loadTextDetailsOfFeedItem(item!!)
|
||||
if (prevItem?.itemIdentifier != item!!.itemIdentifier) cleanedNotes = null
|
||||
if (cleanedNotes == null) {
|
||||
Logd(TAG, "calling load description ${item!!.description==null} ${item!!.title}")
|
||||
val shownotesCleaner = ShownotesCleaner(context, item?.description ?: "", media?.getDuration()?:0)
|
||||
cleanedNotes = shownotesCleaner.processShownotes()
|
||||
}
|
||||
prevItem = item
|
||||
}
|
||||
emitter.onSuccess(cleanedNotes?:"")
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ data: String? ->
|
||||
shownoteView.loadDataWithBaseURL("https://127.0.0.1", data!!, "text/html", "utf-8", "about:blank")
|
||||
Log.d(TAG, "Webview loaded")
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
|
||||
loadMediaInfo()
|
||||
withContext(Dispatchers.Main) {
|
||||
Logd(TAG, "subscribe: ${media?.getEpisodeTitle()}")
|
||||
displayMediaInfo(media!!)
|
||||
shownoteView.loadDataWithBaseURL("https://127.0.0.1", cleanedNotes!!, "text/html", "utf-8", "about:blank")
|
||||
Logd(TAG, "Webview loaded")
|
||||
}
|
||||
}.invokeOnCompletion { throwable ->
|
||||
if (throwable!= null) {
|
||||
Log.e(TAG, Log.getStackTraceString(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
@UnstableApi private fun loadMediaInfo() {
|
||||
disposable?.dispose()
|
||||
disposable = Maybe.create<Playable> { emitter: MaybeEmitter<Playable?> ->
|
||||
media = controller?.getMedia()
|
||||
if (media != null) emitter.onSuccess(media!!)
|
||||
else emitter.onComplete()
|
||||
}.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ media: Playable ->
|
||||
this.media = media
|
||||
displayMediaInfo(media)
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
|
||||
fun buildHomeReaderText() {
|
||||
|
@ -220,15 +241,10 @@ class PlayerDetailsFragment : Fragment() {
|
|||
}
|
||||
|
||||
@UnstableApi private fun displayMediaInfo(media: Playable) {
|
||||
Logd(TAG, "displayMediaInfo ${item?.title} ${media.getEpisodeTitle()}")
|
||||
val pubDateStr = DateFormatter.formatAbbrev(context, media.getPubDate())
|
||||
binding.txtvPodcastTitle.text = StringUtils.stripToEmpty(media.getFeedTitle())
|
||||
if (item == null || item!!.media?.getIdentifier() != media.getIdentifier()) {
|
||||
if (media is FeedMedia) {
|
||||
if (item?.itemIdentifier != media.item?.itemIdentifier) {
|
||||
item = media.item
|
||||
showHomeText = false
|
||||
homeText = null
|
||||
}
|
||||
if (item != null) {
|
||||
val openFeed: Intent = MainActivity.getIntentToOpenFeed(requireContext(), item!!.feedId)
|
||||
binding.txtvPodcastTitle.setOnClickListener { startActivity(openFeed) }
|
||||
|
@ -236,11 +252,10 @@ class PlayerDetailsFragment : Fragment() {
|
|||
} else {
|
||||
binding.txtvPodcastTitle.setOnClickListener(null)
|
||||
}
|
||||
}
|
||||
binding.txtvPodcastTitle.setOnLongClickListener { copyText(media.getFeedTitle()) }
|
||||
binding.episodeDate.text = StringUtils.stripToEmpty(pubDateStr)
|
||||
binding.txtvEpisodeTitle.text = media.getEpisodeTitle()
|
||||
binding.txtvEpisodeTitle.setOnLongClickListener { copyText(media.getEpisodeTitle()) }
|
||||
binding.txtvEpisodeTitle.text = item?.title
|
||||
binding.txtvEpisodeTitle.setOnLongClickListener { copyText(item?.title?:"") }
|
||||
binding.txtvEpisodeTitle.setOnClickListener {
|
||||
val lines = binding.txtvEpisodeTitle.lineCount
|
||||
val animUnit = 1500
|
||||
|
@ -300,19 +315,7 @@ class PlayerDetailsFragment : Fragment() {
|
|||
|
||||
private fun displayCoverImage() {
|
||||
if (media == null) return
|
||||
// val options: RequestOptions = RequestOptions()
|
||||
// .dontAnimate()
|
||||
// .transform(FitCenter(), RoundedCorners((16 * resources.displayMetrics.density).toInt()))
|
||||
|
||||
// val cover: RequestBuilder<Drawable> = Glide.with(this)
|
||||
// .load(media!!.getImageLocation())
|
||||
// .error(Glide.with(this)
|
||||
// .load(ImageResourceUtils.getFallbackImageLocation(media!!))
|
||||
// .apply(options))
|
||||
// .apply(options)
|
||||
|
||||
if (displayedChapterIndex == -1 || media!!.getChapters().isEmpty() || media!!.getChapters()[displayedChapterIndex].imageUrl.isNullOrEmpty()) {
|
||||
// cover.into(binding.imgvCover)
|
||||
val imageLoader = binding.imgvCover.context.imageLoader
|
||||
val imageRequest = ImageRequest.Builder(requireContext())
|
||||
.data(media!!.getImageLocation())
|
||||
|
@ -335,12 +338,6 @@ class PlayerDetailsFragment : Fragment() {
|
|||
|
||||
} else {
|
||||
val imgLoc = EmbeddedChapterImage.getModelFor(media!!, displayedChapterIndex)
|
||||
// if (imgLoc != null) Glide.with(this)
|
||||
// .load(imgLoc)
|
||||
// .apply(options)
|
||||
// .thumbnail(cover)
|
||||
// .error(cover)
|
||||
// .into(binding.imgvCover)
|
||||
val imageLoader = binding.imgvCover.context.imageLoader
|
||||
val imageRequest = ImageRequest.Builder(requireContext())
|
||||
.data(imgLoc)
|
||||
|
@ -402,15 +399,15 @@ class PlayerDetailsFragment : Fragment() {
|
|||
}
|
||||
|
||||
@UnstableApi private fun savePreference() {
|
||||
Log.d(TAG, "Saving preferences")
|
||||
Logd(TAG, "Saving preferences")
|
||||
val prefs = requireActivity().getSharedPreferences(PREF, Activity.MODE_PRIVATE)
|
||||
val editor = prefs.edit()
|
||||
if (controller?.getMedia() != null) {
|
||||
Log.d(TAG, "Saving scroll position: " + binding.itemDescriptionFragment.scrollY)
|
||||
Logd(TAG, "Saving scroll position: " + binding.itemDescriptionFragment.scrollY)
|
||||
editor.putInt(PREF_SCROLL_Y, binding.itemDescriptionFragment.scrollY)
|
||||
editor.putString(PREF_PLAYABLE_ID, controller!!.getMedia()!!.getIdentifier().toString())
|
||||
} else {
|
||||
Log.d(TAG, "savePreferences was called while media or webview was null")
|
||||
Logd(TAG, "savePreferences was called while media or webview was null")
|
||||
editor.putInt(PREF_SCROLL_Y, -1)
|
||||
editor.putString(PREF_PLAYABLE_ID, "")
|
||||
}
|
||||
|
@ -420,7 +417,7 @@ class PlayerDetailsFragment : Fragment() {
|
|||
@UnstableApi private fun restoreFromPreference(): Boolean {
|
||||
if ((activity as MainActivity).bottomSheet.state != BottomSheetBehavior.STATE_EXPANDED) return false
|
||||
|
||||
Log.d(TAG, "Restoring from preferences")
|
||||
Logd(TAG, "Restoring from preferences")
|
||||
val activity: Activity? = activity
|
||||
if (activity != null) {
|
||||
val prefs = activity.getSharedPreferences(PREF, Activity.MODE_PRIVATE)
|
||||
|
@ -428,11 +425,11 @@ class PlayerDetailsFragment : Fragment() {
|
|||
val scrollY = prefs.getInt(PREF_SCROLL_Y, -1)
|
||||
if (scrollY != -1) {
|
||||
if (id == controller?.getMedia()?.getIdentifier()?.toString()) {
|
||||
Log.d(TAG, "Restored scroll Position: $scrollY")
|
||||
Logd(TAG, "Restored scroll Position: $scrollY")
|
||||
binding.itemDescriptionFragment.scrollTo(binding.itemDescriptionFragment.scrollX, scrollY)
|
||||
return true
|
||||
}
|
||||
Log.d(TAG, "reset scroll Position: 0")
|
||||
Logd(TAG, "reset scroll Position: 0")
|
||||
binding.itemDescriptionFragment.scrollTo(0, 0)
|
||||
|
||||
return true
|
||||
|
@ -455,7 +452,7 @@ class PlayerDetailsFragment : Fragment() {
|
|||
}
|
||||
|
||||
fun setItem(item_: FeedItem) {
|
||||
Log.d(TAG, "setItem ${item_.title}")
|
||||
Logd(TAG, "setItem ${item_.title}")
|
||||
if (item?.itemIdentifier != item_.itemIdentifier) {
|
||||
item = item_
|
||||
showHomeText = false
|
||||
|
@ -491,7 +488,7 @@ class PlayerDetailsFragment : Fragment() {
|
|||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
webViewLoader?.dispose()
|
||||
// webViewLoader?.dispose()
|
||||
}
|
||||
|
||||
@UnstableApi private fun copyText(text: String): Boolean {
|
||||
|
@ -504,7 +501,7 @@ class PlayerDetailsFragment : Fragment() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ItemDescriptionFragment"
|
||||
private const val TAG = "PlayerDetailsFragment"
|
||||
|
||||
private const val PREF = "ItemDescriptionFragmentPrefs"
|
||||
private const val PREF_SCROLL_Y = "prefScrollY"
|
||||
|
|
|
@ -6,27 +6,30 @@ import ac.mdiq.podcini.databinding.MultiSelectSpeedDialBinding
|
|||
import ac.mdiq.podcini.databinding.QueueFragmentBinding
|
||||
import ac.mdiq.podcini.feed.util.PlaybackSpeedUtils
|
||||
import ac.mdiq.podcini.net.download.FeedUpdateManager
|
||||
import ac.mdiq.podcini.util.event.playback.PlaybackPositionEvent
|
||||
import ac.mdiq.podcini.preferences.UserPreferences
|
||||
import ac.mdiq.podcini.storage.DBReader
|
||||
import ac.mdiq.podcini.storage.DBWriter
|
||||
import ac.mdiq.podcini.storage.model.feed.*
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
||||
import ac.mdiq.podcini.storage.model.feed.SortOrder
|
||||
import ac.mdiq.podcini.ui.actions.EpisodeMultiSelectActionHandler
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.FeedItemMenuHandler
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.MenuItemUtils
|
||||
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.ui.adapter.QueueRecyclerAdapter
|
||||
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
||||
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
|
||||
import ac.mdiq.podcini.ui.dialog.ItemSortDialog
|
||||
import ac.mdiq.podcini.ui.actions.EpisodeMultiSelectActionHandler
|
||||
import ac.mdiq.podcini.ui.actions.swipeactions.SwipeActions
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.FeedItemMenuHandler
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.MenuItemUtils
|
||||
import ac.mdiq.podcini.ui.view.EmptyViewHandler
|
||||
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
|
||||
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
|
||||
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
||||
import ac.mdiq.podcini.util.Converter
|
||||
import ac.mdiq.podcini.util.FeedItemUtil
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.*
|
||||
import ac.mdiq.podcini.util.event.playback.PlaybackPositionEvent
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.SharedPreferences
|
||||
|
@ -48,10 +51,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.leinardi.android.speeddial.SpeedDialActionItem
|
||||
import com.leinardi.android.speeddial.SpeedDialView
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
@ -81,7 +81,8 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
private var recyclerAdapter: QueueRecyclerAdapter? = null
|
||||
private var currentPlaying: EpisodeItemViewHolder? = null
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
// private var disposable: Disposable? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -93,7 +94,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
_binding = QueueFragmentBinding.inflate(inflater)
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
toolbar = binding.toolbar
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
toolbar.setOnLongClickListener {
|
||||
|
@ -194,12 +195,13 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
disposable?.dispose()
|
||||
scope.cancel()
|
||||
// disposable?.dispose()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: QueueEvent) {
|
||||
Log.d(TAG, "onEventMainThread() called with QueueEvent event = [$event]")
|
||||
Logd(TAG, "onEventMainThread() called with QueueEvent event = [$event]")
|
||||
if (recyclerAdapter == null) {
|
||||
loadItems(true)
|
||||
return
|
||||
|
@ -236,7 +238,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: FeedItemEvent) {
|
||||
Log.d(TAG, "onEventMainThread() called with FeedItemEvent event = [$event]")
|
||||
Logd(TAG, "onEventMainThread() called with FeedItemEvent event = [$event]")
|
||||
if (recyclerAdapter == null) {
|
||||
loadItems(true)
|
||||
return
|
||||
|
@ -258,7 +260,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: EpisodeDownloadEvent) {
|
||||
Log.d(TAG, "onEventMainThread() called with EpisodeDownloadEvent event = [$event]")
|
||||
Logd(TAG, "onEventMainThread() called with EpisodeDownloadEvent event = [$event]")
|
||||
for (downloadUrl in event.urls) {
|
||||
val pos: Int = FeedItemUtil.indexOfItemWithDownloadUrl(queue.toList(), downloadUrl)
|
||||
if (pos >= 0) recyclerAdapter?.notifyItemChangedCompat(pos)
|
||||
|
@ -271,7 +273,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
if (recyclerAdapter != null) {
|
||||
if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem) currentPlaying!!.notifyPlaybackPositionUpdated(event)
|
||||
else {
|
||||
Log.d(TAG, "onEventMainThread() search list")
|
||||
Logd(TAG, "onEventMainThread() search list")
|
||||
for (i in 0 until recyclerAdapter!!.itemCount) {
|
||||
val holder: EpisodeItemViewHolder? = recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||
|
@ -286,7 +288,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onPlayerStatusChanged(event: PlayerStatusEvent?) {
|
||||
Log.d(TAG, "onPlayerStatusChanged() called with event = [$event]")
|
||||
Logd(TAG, "onPlayerStatusChanged() called with event = [$event]")
|
||||
loadItems(false)
|
||||
refreshToolbarState()
|
||||
}
|
||||
|
@ -294,7 +296,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onUnreadItemsChanged(event: UnreadItemsUpdateEvent?) {
|
||||
// Sent when playback position is reset
|
||||
Log.d(TAG, "onUnreadItemsChanged() called with event = [$event]")
|
||||
Logd(TAG, "onUnreadItemsChanged() called with event = [$event]")
|
||||
loadItems(false)
|
||||
refreshToolbarState()
|
||||
}
|
||||
|
@ -426,7 +428,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
}
|
||||
|
||||
@UnstableApi override fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
Log.d(TAG, "onContextItemSelected() called with: item = [$item]")
|
||||
Logd(TAG, "onContextItemSelected() called with: item = [$item]")
|
||||
if (!isVisible || recyclerAdapter == null) return false
|
||||
|
||||
val selectedItem: FeedItem? = recyclerAdapter!!.longPressedItem
|
||||
|
@ -485,22 +487,38 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
}
|
||||
|
||||
private fun loadItems(restoreScrollPosition: Boolean) {
|
||||
Log.d(TAG, "loadItems() called")
|
||||
disposable?.dispose()
|
||||
Logd(TAG, "loadItems() called")
|
||||
// disposable?.dispose()
|
||||
|
||||
if (queue.isEmpty()) emptyView.hide()
|
||||
|
||||
disposable = Observable.fromCallable { DBReader.getQueue().toMutableList() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ items: MutableList<FeedItem> ->
|
||||
queue = items
|
||||
// disposable = Observable.fromCallable { DBReader.getQueue().toMutableList() }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ items: MutableList<FeedItem> ->
|
||||
// queue = items
|
||||
// progressBar.visibility = View.GONE
|
||||
// recyclerAdapter?.setDummyViews(0)
|
||||
// recyclerAdapter?.updateItems(queue)
|
||||
// if (restoreScrollPosition) recyclerView.restoreScrollPosition(TAG)
|
||||
// refreshInfoBar()
|
||||
// }, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
queue = withContext(Dispatchers.IO) { DBReader.getQueue().toMutableList() }
|
||||
withContext(Dispatchers.Main) {
|
||||
progressBar.visibility = View.GONE
|
||||
recyclerAdapter?.setDummyViews(0)
|
||||
recyclerAdapter?.updateItems(queue)
|
||||
if (restoreScrollPosition) recyclerView.restoreScrollPosition(TAG)
|
||||
refreshInfoBar()
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onStartSelectMode() {
|
||||
|
@ -562,7 +580,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
|
||||
val from = viewHolder.bindingAdapterPosition
|
||||
val to = target.bindingAdapterPosition
|
||||
Log.d(TAG, "move($from, $to) in memory")
|
||||
Logd(TAG, "move($from, $to) in memory")
|
||||
if (from >= queue.size || to >= queue.size || from < 0 || to < 0) return false
|
||||
|
||||
queue.add(to, queue.removeAt(from))
|
||||
|
@ -571,7 +589,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
}
|
||||
|
||||
@UnstableApi override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
|
||||
//SwipeActions
|
||||
super.onSwiped(viewHolder, direction)
|
||||
|
@ -592,7 +610,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
|
||||
@UnstableApi private fun reallyMoved(from: Int, to: Int) {
|
||||
// Write drag operation to database
|
||||
Log.d(TAG, "Write to database move($from, $to)")
|
||||
Logd(TAG, "Write to database move($from, $to)")
|
||||
DBWriter.moveQueueItem(from, to, true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import ac.mdiq.podcini.net.discovery.PodcastSearchResult
|
|||
import ac.mdiq.podcini.storage.DBReader
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.ui.adapter.FeedDiscoverAdapter
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.DiscoveryDefaultUpdateEvent
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
@ -21,10 +22,7 @@ import android.widget.*
|
|||
import androidx.annotation.OptIn
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
@ -34,7 +32,8 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
|||
private var _binding: QuickFeedDiscoveryBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
// private var disposable: Disposable? = null
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
private lateinit var adapter: FeedDiscoverAdapter
|
||||
private lateinit var discoverGridLayout: GridView
|
||||
|
@ -47,7 +46,7 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
|||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
_binding = QuickFeedDiscoveryBinding.inflate(inflater)
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
val discoverMore = binding.discoverMore
|
||||
discoverMore.setOnClickListener { (activity as MainActivity).loadChildFragment(DiscoveryFragment()) }
|
||||
|
||||
|
@ -84,7 +83,8 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
|||
super.onDestroy()
|
||||
_binding = null
|
||||
EventBus.getDefault().unregister(this)
|
||||
disposable?.dispose()
|
||||
scope.cancel()
|
||||
// disposable?.dispose()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
@ -124,13 +124,37 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
|||
return
|
||||
}
|
||||
|
||||
disposable = Observable.fromCallable {
|
||||
// disposable = Observable.fromCallable {
|
||||
// loader.loadToplist(countryCode, NUM_SUGGESTIONS, DBReader.getFeedList())
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { podcasts: List<PodcastSearchResult> ->
|
||||
// errorView.visibility = View.GONE
|
||||
// if (podcasts.isEmpty()) {
|
||||
// errorTextView.text = resources.getText(R.string.search_status_no_results)
|
||||
// errorView.visibility = View.VISIBLE
|
||||
// discoverGridLayout.visibility = View.INVISIBLE
|
||||
// } else {
|
||||
// discoverGridLayout.visibility = View.VISIBLE
|
||||
// adapter.updateData(podcasts)
|
||||
// }
|
||||
// }, { error: Throwable ->
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// errorTextView.text = error.localizedMessage
|
||||
// errorView.visibility = View.VISIBLE
|
||||
// discoverGridLayout.visibility = View.INVISIBLE
|
||||
// errorRetry.visibility = View.VISIBLE
|
||||
// errorRetry.setOnClickListener { loadToplist() }
|
||||
// })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
val podcasts = withContext(Dispatchers.IO) {
|
||||
loader.loadToplist(countryCode, NUM_SUGGESTIONS, DBReader.getFeedList())
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ podcasts: List<PodcastSearchResult> ->
|
||||
withContext(Dispatchers.Main) {
|
||||
errorView.visibility = View.GONE
|
||||
if (podcasts.isEmpty()) {
|
||||
errorTextView.text = resources.getText(R.string.search_status_no_results)
|
||||
|
@ -140,14 +164,17 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
|
|||
discoverGridLayout.visibility = View.VISIBLE
|
||||
adapter.updateData(podcasts)
|
||||
}
|
||||
}, { error: Throwable ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
errorTextView.text = error.localizedMessage
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
errorTextView.text = e.localizedMessage
|
||||
errorView.visibility = View.VISIBLE
|
||||
discoverGridLayout.visibility = View.INVISIBLE
|
||||
errorRetry.visibility = View.VISIBLE
|
||||
errorRetry.setOnClickListener { loadToplist() }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class) override fun onItemClick(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
|
||||
|
|
|
@ -5,24 +5,25 @@ import ac.mdiq.podcini.R
|
|||
import ac.mdiq.podcini.databinding.MultiSelectSpeedDialBinding
|
||||
import ac.mdiq.podcini.databinding.SearchFragmentBinding
|
||||
import ac.mdiq.podcini.net.discovery.CombinedSearcher
|
||||
import ac.mdiq.podcini.util.event.playback.PlaybackPositionEvent
|
||||
import ac.mdiq.podcini.storage.FeedSearcher
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter
|
||||
import ac.mdiq.podcini.ui.adapter.HorizontalFeedListAdapter
|
||||
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
||||
import ac.mdiq.podcini.ui.actions.EpisodeMultiSelectActionHandler
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.FeedItemMenuHandler
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.FeedMenuHandler
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.MenuItemUtils
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.ui.adapter.EpisodeItemListAdapter
|
||||
import ac.mdiq.podcini.ui.adapter.HorizontalFeedListAdapter
|
||||
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
||||
import ac.mdiq.podcini.ui.view.EmptyViewHandler
|
||||
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
|
||||
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
|
||||
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
|
||||
import ac.mdiq.podcini.util.FeedItemUtil
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.*
|
||||
import ac.mdiq.podcini.util.event.playback.PlaybackPositionEvent
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
|
@ -42,10 +43,7 @@ import com.google.android.material.chip.Chip
|
|||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.leinardi.android.speeddial.SpeedDialActionItem
|
||||
import com.leinardi.android.speeddial.SpeedDialView
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
@ -70,7 +68,8 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
private var results: MutableList<FeedItem> = mutableListOf()
|
||||
private var currentPlaying: EpisodeItemViewHolder? = null
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
// private var disposable: Disposable? = null
|
||||
private var lastQueryChange: Long = 0
|
||||
private var isOtherViewInFoucus = false
|
||||
|
||||
|
@ -82,13 +81,14 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
disposable?.dispose()
|
||||
scope.cancel()
|
||||
// disposable?.dispose()
|
||||
}
|
||||
|
||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = SearchFragmentBinding.inflate(inflater)
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
setupToolbar(binding.toolbar)
|
||||
speedDialBinding = MultiSelectSpeedDialBinding.bind(binding.root)
|
||||
progressBar = binding.progressBar
|
||||
|
@ -243,7 +243,7 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
|
||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(event: FeedItemEvent) {
|
||||
Log.d(TAG, "onEventMainThread() called with: event = [$event]")
|
||||
Logd(TAG, "onEventMainThread() called with: event = [$event]")
|
||||
|
||||
var i = 0
|
||||
val size: Int = event.items.size
|
||||
|
@ -272,7 +272,7 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem)
|
||||
currentPlaying!!.notifyPlaybackPositionUpdated(event)
|
||||
else {
|
||||
Log.d(TAG, "onEventMainThread() search list")
|
||||
Logd(TAG, "onEventMainThread() search list")
|
||||
for (i in 0 until adapter.itemCount) {
|
||||
val holder: EpisodeItemViewHolder? = recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||
|
@ -296,17 +296,37 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
}
|
||||
|
||||
@UnstableApi private fun search() {
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
|
||||
adapterFeeds.setEndButton(R.string.search_online) { this.searchOnline() }
|
||||
chip.visibility = if ((requireArguments().getLong(ARG_FEED, 0) == 0L)) View.GONE else View.VISIBLE
|
||||
disposable = Observable.fromCallable { this.performSearch() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ results: Pair<List<FeedItem>?, List<Feed?>?> ->
|
||||
// disposable = Observable.fromCallable { this.performSearch() }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ results: Pair<List<FeedItem>?, List<Feed?>?> ->
|
||||
// progressBar.visibility = View.GONE
|
||||
// if (results.first != null) {
|
||||
// this.results = results.first!!.toMutableList()
|
||||
// adapter.updateItems(results.first!!)
|
||||
// }
|
||||
// if (requireArguments().getLong(ARG_FEED, 0) == 0L) {
|
||||
// if (results.second != null) adapterFeeds.updateData(results.second!!.filterNotNull())
|
||||
// } else adapterFeeds.updateData(emptyList())
|
||||
//
|
||||
// if (searchView.query.toString().isEmpty()) emptyViewHandler.setMessage(R.string.type_to_search)
|
||||
// else emptyViewHandler.setMessage(getString(R.string.no_results_for_query) + searchView.query)
|
||||
//
|
||||
// }, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
val results = withContext(Dispatchers.IO) {
|
||||
performSearch()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
progressBar.visibility = View.GONE
|
||||
if (results.first != null) {
|
||||
this.results = results.first!!.toMutableList()
|
||||
this@SearchFragment.results = results.first!!.toMutableList()
|
||||
adapter.updateItems(results.first!!)
|
||||
}
|
||||
if (requireArguments().getLong(ARG_FEED, 0) == 0L) {
|
||||
|
@ -315,8 +335,11 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
|||
|
||||
if (searchView.query.toString().isEmpty()) emptyViewHandler.setMessage(R.string.type_to_search)
|
||||
else emptyViewHandler.setMessage(getString(R.string.no_results_for_query) + searchView.query)
|
||||
|
||||
}, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@UnstableApi private fun performSearch(): Pair<List<FeedItem>?, List<Feed?>?> {
|
||||
|
|
|
@ -8,16 +8,17 @@ import ac.mdiq.podcini.preferences.UserPreferences
|
|||
import ac.mdiq.podcini.storage.DBReader
|
||||
import ac.mdiq.podcini.storage.NavDrawerData
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.ui.actions.FeedMultiSelectActionHandler
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.FeedMenuHandler
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.MenuItemUtils
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
|
||||
import ac.mdiq.podcini.ui.adapter.SubscriptionsAdapter
|
||||
import ac.mdiq.podcini.ui.dialog.FeedSortDialog
|
||||
import ac.mdiq.podcini.ui.dialog.SubscriptionsFilterDialog
|
||||
import ac.mdiq.podcini.ui.actions.FeedMultiSelectActionHandler
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.FeedMenuHandler
|
||||
import ac.mdiq.podcini.ui.actions.menuhandler.MenuItemUtils
|
||||
import ac.mdiq.podcini.ui.view.EmptyViewHandler
|
||||
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.FeedListUpdateEvent
|
||||
import ac.mdiq.podcini.util.event.FeedTagsChangedEvent
|
||||
import ac.mdiq.podcini.util.event.FeedUpdateRunningEvent
|
||||
|
@ -39,10 +40,7 @@ import com.google.android.material.appbar.MaterialToolbar
|
|||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.leinardi.android.speeddial.SpeedDialActionItem
|
||||
import com.leinardi.android.speeddial.SpeedDialView
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
@ -72,7 +70,8 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
private var displayedFolder: String = ""
|
||||
private var displayUpArrow = false
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
// private var disposable: Disposable? = null
|
||||
private var feedList: List<NavDrawerData.FeedDrawerItem> = mutableListOf()
|
||||
private var feedListFiltered: List<NavDrawerData.FeedDrawerItem> = mutableListOf()
|
||||
|
||||
|
@ -86,7 +85,7 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = FragmentSubscriptionsBinding.inflate(inflater)
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
Logd(TAG, "fragment onCreateView")
|
||||
toolbar = binding.toolbar
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
toolbar.setOnLongClickListener {
|
||||
|
@ -201,7 +200,8 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
super.onDestroyView()
|
||||
_binding = null
|
||||
EventBus.getDefault().unregister(this)
|
||||
disposable?.dispose()
|
||||
scope.cancel()
|
||||
// disposable?.dispose()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
|
@ -272,17 +272,37 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
}
|
||||
|
||||
private fun loadSubscriptions() {
|
||||
disposable?.dispose()
|
||||
// disposable?.dispose()
|
||||
emptyView.hide()
|
||||
disposable = Observable.fromCallable {
|
||||
// disposable = Observable.fromCallable {
|
||||
// val data: NavDrawerData = DBReader.getNavDrawerData(UserPreferences.subscriptionsFilter)
|
||||
// val items: List<NavDrawerData.FeedDrawerItem> = data.items
|
||||
// items
|
||||
// }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe(
|
||||
// { result: List<NavDrawerData.FeedDrawerItem> ->
|
||||
// // We have fewer items. This can result in items being selected that are no longer visible.
|
||||
// if ( feedListFiltered.size > result.size) subscriptionAdapter.endSelectMode()
|
||||
// feedList = result
|
||||
// filterOnTag()
|
||||
// progressBar.visibility = View.GONE
|
||||
// subscriptionAdapter.setItems(feedListFiltered)
|
||||
// feedCount.text = feedListFiltered.size.toString() + " / " + feedList.size.toString()
|
||||
// emptyView.updateVisibility()
|
||||
// }, { error: Throwable? ->
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
val data: NavDrawerData = DBReader.getNavDrawerData(UserPreferences.subscriptionsFilter)
|
||||
val items: List<NavDrawerData.FeedDrawerItem> = data.items
|
||||
items
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ result: List<NavDrawerData.FeedDrawerItem> ->
|
||||
withContext(Dispatchers.Main) {
|
||||
// We have fewer items. This can result in items being selected that are no longer visible.
|
||||
if ( feedListFiltered.size > result.size) subscriptionAdapter.endSelectMode()
|
||||
feedList = result
|
||||
|
@ -291,9 +311,11 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
subscriptionAdapter.setItems(feedListFiltered)
|
||||
feedCount.text = feedListFiltered.size.toString() + " / " + feedList.size.toString()
|
||||
emptyView.updateVisibility()
|
||||
}, { error: Throwable? ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
})
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
|
||||
if (UserPreferences.subscriptionsFilter.isEnabled) feedsFilteredMsg.visibility = View.VISIBLE
|
||||
else feedsFilteredMsg.visibility = View.GONE
|
||||
|
|
|
@ -23,6 +23,7 @@ import ac.mdiq.podcini.ui.utils.PictureInPictureUtil
|
|||
import ac.mdiq.podcini.ui.utils.ShownotesCleaner
|
||||
import ac.mdiq.podcini.ui.view.ShownotesWebView
|
||||
import ac.mdiq.podcini.util.Converter.getDurationStringLong
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.TimeSpeedConverter
|
||||
import ac.mdiq.podcini.util.event.playback.BufferUpdateEvent
|
||||
import ac.mdiq.podcini.util.event.playback.PlaybackPositionEvent
|
||||
|
@ -41,13 +42,11 @@ import androidx.core.app.ActivityCompat.invalidateOptionsMenu
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import java.lang.Runnable
|
||||
|
||||
@UnstableApi
|
||||
class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
||||
|
@ -63,7 +62,9 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
private var lastScreenTap: Long = 0
|
||||
private val videoControlsHider = Handler(Looper.getMainLooper())
|
||||
private var showTimeLeft = false
|
||||
private var disposable: Disposable? = null
|
||||
|
||||
val scope = CoroutineScope(Dispatchers.Main)
|
||||
// private var disposable: Disposable? = null
|
||||
private var prog = 0f
|
||||
|
||||
private var itemsLoaded = false
|
||||
|
@ -92,7 +93,7 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
@OptIn(UnstableApi::class) private fun newPlaybackController(): PlaybackController {
|
||||
return object : PlaybackController(requireActivity()) {
|
||||
override fun updatePlayButtonShowsPlay(showPlay: Boolean) {
|
||||
Log.d(TAG, "updatePlayButtonShowsPlay called")
|
||||
Logd(TAG, "updatePlayButtonShowsPlay called")
|
||||
binding.playButton.setIsShowPlay(showPlay)
|
||||
if (showPlay) {
|
||||
(activity as AppCompatActivity).window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
|
@ -100,7 +101,7 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
(activity as AppCompatActivity).window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
setupVideoAspectRatio()
|
||||
if (videoSurfaceCreated && controller != null) {
|
||||
Log.d(TAG, "Videosurface already created, setting videosurface now")
|
||||
Logd(TAG, "Videosurface already created, setting videosurface now")
|
||||
setVideoSurface(binding.videoView.holder)
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +149,8 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
_binding = null
|
||||
controller?.release()
|
||||
controller = null // prevent leak
|
||||
disposable?.dispose()
|
||||
scope.cancel()
|
||||
// disposable?.dispose()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
@ -164,10 +166,10 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
private fun setupVideoAspectRatio() {
|
||||
if (videoSurfaceCreated && controller != null) {
|
||||
if (videoSize != null && videoSize!!.first > 0 && videoSize!!.second > 0) {
|
||||
Log.d(TAG, "Width,height of video: " + videoSize!!.first + ", " + videoSize!!.second)
|
||||
Logd(TAG, "Width,height of video: ${videoSize!!.first}, ${videoSize!!.second}")
|
||||
val videoWidth = resources.displayMetrics.widthPixels
|
||||
val videoHeight = (videoWidth.toFloat() / videoSize!!.first * videoSize!!.second).toInt()
|
||||
Log.d(TAG, "Width,height of video: " + videoWidth + ", " + videoHeight)
|
||||
Logd(TAG, "Width,height of video: $videoWidth, $videoHeight")
|
||||
binding.videoView.setVideoSize(videoWidth, videoHeight)
|
||||
// binding.videoView.setVideoSize(videoSize.first, videoSize.second)
|
||||
// binding.videoView.setVideoSize(-1, -1)
|
||||
|
@ -175,18 +177,18 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
Log.e(TAG, "Could not determine video size")
|
||||
val videoWidth = resources.displayMetrics.widthPixels
|
||||
val videoHeight = (videoWidth.toFloat() / 16 * 9).toInt()
|
||||
Log.d(TAG, "Width,height of video: " + videoWidth + ", " + videoHeight)
|
||||
Logd(TAG, "Width,height of video: $videoWidth, $videoHeight")
|
||||
binding.videoView.setVideoSize(videoWidth, videoHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class) private fun loadMediaInfo() {
|
||||
Log.d(TAG, "loadMediaInfo called")
|
||||
Logd(TAG, "loadMediaInfo called")
|
||||
if (controller?.getMedia() == null) return
|
||||
|
||||
if (MediaPlayerBase.status == PlayerStatus.PLAYING && !controller!!.isPlayingVideoLocally) {
|
||||
Log.d(TAG, "Closing, no longer video")
|
||||
Logd(TAG, "Closing, no longer video")
|
||||
destroyingDueToReload = true
|
||||
activity?.finish()
|
||||
MainActivityStarter(requireContext()).withOpenPlayer().start()
|
||||
|
@ -203,15 +205,35 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
}
|
||||
|
||||
@UnstableApi private fun load() {
|
||||
disposable?.dispose()
|
||||
Log.d(TAG, "load() called")
|
||||
// disposable?.dispose()
|
||||
Logd(TAG, "load() called")
|
||||
|
||||
disposable = Observable.fromCallable<FeedItem?> { this.loadInBackground() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ result: FeedItem? ->
|
||||
item = result
|
||||
Log.d(TAG, "load() item ${item?.id}")
|
||||
// disposable = Observable.fromCallable<FeedItem?> { this.loadInBackground() }
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ result: FeedItem? ->
|
||||
// item = result
|
||||
// Logd(TAG, "load() item ${item?.id}")
|
||||
// if (item != null) {
|
||||
// val isFav = item!!.isTagged(FeedItem.TAG_FAVORITE)
|
||||
// if (isFavorite != isFav) {
|
||||
// isFavorite = isFav
|
||||
// invalidateOptionsMenu(requireActivity())
|
||||
// }
|
||||
// }
|
||||
// onFragmentLoaded()
|
||||
// itemsLoaded = true
|
||||
// }, { error: Throwable? ->
|
||||
// Log.e(TAG, Log.getStackTraceString(error))
|
||||
// })
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
item = withContext(Dispatchers.IO) {
|
||||
loadInBackground()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
Logd(TAG, "load() item ${item?.id}")
|
||||
if (item != null) {
|
||||
val isFav = item!!.isTagged(FeedItem.TAG_FAVORITE)
|
||||
if (isFavorite != isFav) {
|
||||
|
@ -221,9 +243,12 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
}
|
||||
onFragmentLoaded()
|
||||
itemsLoaded = true
|
||||
}, { error: Throwable? ->
|
||||
Log.e(TAG, Log.getStackTraceString(error))
|
||||
})
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun loadInBackground(): FeedItem? {
|
||||
|
@ -244,7 +269,7 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
@UnstableApi
|
||||
private fun setupView() {
|
||||
showTimeLeft = shouldShowRemainingTime()
|
||||
Log.d(TAG, "setupView showTimeLeft: $showTimeLeft")
|
||||
Logd(TAG, "setupView showTimeLeft: $showTimeLeft")
|
||||
|
||||
binding.durationLabel.setOnClickListener {
|
||||
showTimeLeft = !showTimeLeft
|
||||
|
@ -262,7 +287,7 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
binding.durationLabel.text = length
|
||||
|
||||
setShowRemainTimeSetting(showTimeLeft)
|
||||
Log.d("timeleft on click", if (showTimeLeft) "true" else "false")
|
||||
Logd("timeleft on click", if (showTimeLeft) "true" else "false")
|
||||
}
|
||||
|
||||
binding.sbPosition.setOnSeekBarChangeListener(this)
|
||||
|
@ -394,14 +419,14 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
|
||||
@UnstableApi
|
||||
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||
Log.d(TAG, "Videoview holder created")
|
||||
Logd(TAG, "Videoview holder created")
|
||||
videoSurfaceCreated = true
|
||||
if (MediaPlayerBase.status == PlayerStatus.PLAYING) setVideoSurface(holder)
|
||||
setupVideoAspectRatio()
|
||||
}
|
||||
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||
Log.d(TAG, "Videosurface was destroyed")
|
||||
Logd(TAG, "Videosurface was destroyed")
|
||||
videoSurfaceCreated = false
|
||||
if (controller != null && !destroyingDueToReload && !(activity as VideoplayerActivity).switchToAudioOnly)
|
||||
notifyVideoSurfaceAbandoned()
|
||||
|
@ -441,7 +466,7 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
|
||||
private val hideVideoControls = Runnable {
|
||||
if (videoControlsShowing) {
|
||||
Log.d(TAG, "Hiding video controls")
|
||||
Logd(TAG, "Hiding video controls")
|
||||
hideVideoControls(true)
|
||||
if (videoMode == VideoplayerActivity.VideoMode.FULL_SCREEN_VIEW) (activity as? AppCompatActivity)?.supportActionBar?.hide()
|
||||
videoControlsShowing = false
|
||||
|
@ -504,7 +529,7 @@ class VideoEpisodeFragment : Fragment(), OnSeekBarChangeListener {
|
|||
}
|
||||
|
||||
private fun updateProgressbarPosition(position: Int, duration: Int) {
|
||||
Log.d(TAG, "updateProgressbarPosition($position, $duration)")
|
||||
Logd(TAG, "updateProgressbarPosition ($position, $duration)")
|
||||
val progress = (position.toFloat()) / duration
|
||||
binding.sbPosition.progress = (progress * binding.sbPosition.max).toInt()
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou
|
|||
|
||||
if (coverHolder.visibility == View.VISIBLE) {
|
||||
val imgLoc = ImageResourceUtils.getEpisodeListImageLocation(item)
|
||||
Logd(TAG, "imgLoc $imgLoc ${item.feed?.imageUrl} ${item.title}")
|
||||
// Logd(TAG, "imgLoc $imgLoc ${item.feed?.imageUrl} ${item.title}")
|
||||
if (!imgLoc.isNullOrBlank() && !imgLoc.contains(PREFIX_GENERATIVE_COVER)) CoverLoader(activity)
|
||||
.withUri(imgLoc)
|
||||
.withFallbackUri(item.feed?.imageUrl)
|
||||
|
|
|
@ -61,7 +61,7 @@ object ChapterUtils {
|
|||
val chaptersFromMediaFile = loadChaptersFromMediaFile(playable, context)
|
||||
val chaptersMergePhase1 = merge(chaptersFromDatabase, chaptersFromMediaFile)
|
||||
val chapters = merge(chaptersMergePhase1, chaptersFromPodcastIndex)
|
||||
Log.d(TAG, "loadChapters chapters size: ${chapters?.size?:0} ${playable.getEpisodeTitle()}")
|
||||
Logd(TAG, "loadChapters chapters size: ${chapters?.size?:0} ${playable.getEpisodeTitle()}")
|
||||
if (chapters == null) playable.setChapters(listOf()) // Do not try loading again. There are no chapters.
|
||||
else playable.setChapters(chapters)
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ object FeedItemPermutors {
|
|||
}
|
||||
|
||||
private fun feedTitle(item: FeedItem?): String {
|
||||
Log.d("permutors", "feedTitle ${item?.feed?.title}")
|
||||
Logd("permutors", "feedTitle ${item?.feed?.title}")
|
||||
return if (item?.feed?.title != null) item.feed!!.title!!.lowercase(Locale.getDefault()) else ""
|
||||
}
|
||||
|
||||
|
|
|
@ -24,10 +24,10 @@ object URIUtil {
|
|||
val url = URL(source)
|
||||
return URI(url.protocol, url.userInfo, url.host, url.port, url.path, url.query, url.ref)
|
||||
} catch (e: MalformedURLException) {
|
||||
Log.d(TAG, "source: $source")
|
||||
Logd(TAG, "source: $source")
|
||||
throw IllegalArgumentException(e)
|
||||
} catch (e: URISyntaxException) {
|
||||
Log.d(TAG, "source: $source")
|
||||
Logd(TAG, "source: $source")
|
||||
throw IllegalArgumentException(e)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ac.mdiq.podcini.util.error
|
||||
|
||||
import ac.mdiq.podcini.BuildConfig
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.error.CrashReportWriter.Companion.write
|
||||
import android.util.Log
|
||||
import io.reactivex.exceptions.UndeliverableException
|
||||
|
@ -13,7 +14,7 @@ object RxJavaErrorHandlerSetup {
|
|||
RxJavaPlugins.setErrorHandler { exception: Throwable? ->
|
||||
if (exception is UndeliverableException) {
|
||||
// Probably just disposed because the fragment was left
|
||||
Log.d(TAG, "Ignored exception: " + Log.getStackTraceString(exception))
|
||||
Logd(TAG, "Ignored exception: " + Log.getStackTraceString(exception))
|
||||
return@setErrorHandler
|
||||
}
|
||||
// Usually, undeliverable exceptions are wrapped in an UndeliverableException.
|
||||
|
|
|
@ -73,11 +73,4 @@
|
|||
android:scrollbarStyle="outsideOverlay"
|
||||
tools:listitem="@layout/nav_listitem" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
style="?android:attr/progressBarStyle" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
|
|
@ -9,19 +9,6 @@
|
|||
custom:showAsAction="always"
|
||||
android:title="@string/search_label"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/refresh_item"
|
||||
android:title="@string/refresh_label"
|
||||
android:menuCategory="container"
|
||||
custom:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/filter_items"
|
||||
android:icon="@drawable/ic_filter"
|
||||
android:menuCategory="container"
|
||||
android:title="@string/filter"
|
||||
custom:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_favorites"
|
||||
android:icon="@drawable/ic_star_border"
|
||||
|
@ -29,9 +16,23 @@
|
|||
android:title="@string/favorite_episodes_label"
|
||||
custom:showAsAction="always"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/filter_items"
|
||||
android:icon="@drawable/ic_filter"
|
||||
android:menuCategory="container"
|
||||
android:title="@string/filter"
|
||||
custom:showAsAction="always"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/episodes_sort"
|
||||
android:icon="@drawable/arrows_sort"
|
||||
android:title="@string/sort"
|
||||
custom:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/refresh_item"
|
||||
android:title="@string/refresh_label"
|
||||
android:menuCategory="container"
|
||||
custom:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
|
|
|
@ -7,6 +7,7 @@ import ac.mdiq.podcini.storage.model.feed.FeedMedia
|
|||
import ac.mdiq.podcini.storage.model.playback.MediaType
|
||||
import ac.mdiq.podcini.storage.model.playback.Playable
|
||||
import ac.mdiq.podcini.storage.model.playback.RemoteMedia
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.PlayerErrorEvent
|
||||
import ac.mdiq.podcini.util.event.playback.BufferUpdateEvent
|
||||
import android.annotation.SuppressLint
|
||||
|
@ -107,9 +108,9 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
private fun onRemoteMediaPlayerStatusUpdated() {
|
||||
val mediaStatus = remoteMediaClient!!.mediaStatus
|
||||
if (mediaStatus == null) {
|
||||
Log.d(TAG, "Received null MediaStatus")
|
||||
Logd(TAG, "Received null MediaStatus")
|
||||
return
|
||||
} else Log.d(TAG, "Received remote status/media update. New state=" + mediaStatus.playerState)
|
||||
} else Logd(TAG, "Received remote status/media update. New state=" + mediaStatus.playerState)
|
||||
|
||||
var state = mediaStatus.playerState
|
||||
val oldState = remoteState
|
||||
|
@ -117,7 +118,7 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
val mediaChanged = !CastUtils.matches(remoteMedia, media)
|
||||
var stateChanged = state != oldState
|
||||
if (!mediaChanged && !stateChanged) {
|
||||
Log.d(TAG, "Both media and state haven't changed, so nothing to do")
|
||||
Logd(TAG, "Both media and state haven't changed, so nothing to do")
|
||||
return
|
||||
}
|
||||
val currentMedia = if (mediaChanged) localVersion(remoteMedia) else media
|
||||
|
@ -220,7 +221,7 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
}
|
||||
|
||||
override fun playMediaObject(playable: Playable, stream: Boolean, startWhenPrepared: Boolean, prepareImmediately: Boolean) {
|
||||
Log.d(TAG, "playMediaObject() called")
|
||||
Logd(TAG, "playMediaObject() called")
|
||||
playMediaObject(playable, false, stream, startWhenPrepared, prepareImmediately)
|
||||
}
|
||||
|
||||
|
@ -233,7 +234,7 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
*/
|
||||
private fun playMediaObject(playable: Playable, forceReset: Boolean, stream: Boolean, startWhenPrepared: Boolean, prepareImmediately: Boolean) {
|
||||
if (!CastUtils.isCastable(playable, castContext.sessionManager.currentCastSession)) {
|
||||
Log.d(TAG, "media provided is not compatible with cast device")
|
||||
Logd(TAG, "media provided is not compatible with cast device")
|
||||
EventBus.getDefault().post(PlayerErrorEvent("Media not compatible with cast device"))
|
||||
var nextPlayable: Playable? = playable
|
||||
do {
|
||||
|
@ -248,7 +249,7 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
if (media != null) {
|
||||
if (!forceReset && media!!.getIdentifier() == playable.getIdentifier() && status == PlayerStatus.PLAYING) {
|
||||
// episode is already playing -> ignore method call
|
||||
Log.d(TAG, "Method call to playMediaObject was ignored: media file already playing.")
|
||||
Logd(TAG, "Method call to playMediaObject was ignored: media file already playing.")
|
||||
return
|
||||
} else {
|
||||
// set temporarily to pause in order to update list with current position
|
||||
|
@ -287,7 +288,7 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
|
||||
override fun prepare() {
|
||||
if (status == PlayerStatus.INITIALIZED) {
|
||||
Log.d(TAG, "Preparing media player")
|
||||
Logd(TAG, "Preparing media player")
|
||||
setPlayerStatus(PlayerStatus.PREPARING, media)
|
||||
var position = media!!.getPosition()
|
||||
if (position > 0) position = calculatePositionWithRewind(position, media!!.getLastPlayedTime())
|
||||
|
@ -300,9 +301,9 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
}
|
||||
|
||||
override fun reinit() {
|
||||
Log.d(TAG, "reinit() called")
|
||||
Logd(TAG, "reinit() called")
|
||||
if (media != null) playMediaObject(media!!, true, stream = false, startWhenPrepared = startWhenPrepared.get(), prepareImmediately = false)
|
||||
else Log.d(TAG, "Call to reinit was ignored: media was null")
|
||||
else Logd(TAG, "Call to reinit was ignored: media was null")
|
||||
}
|
||||
|
||||
override fun seekTo(t: Int) {
|
||||
|
@ -347,7 +348,7 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
}
|
||||
|
||||
override fun setVolume(volumeLeft: Float, volumeRight: Float) {
|
||||
Log.d(TAG, "Setting the Stream volume on Remote Media Player")
|
||||
Logd(TAG, "Setting the Stream volume on Remote Media Player")
|
||||
remoteMediaClient!!.setStreamVolume(volumeLeft.toDouble())
|
||||
}
|
||||
|
||||
|
@ -398,7 +399,7 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
}
|
||||
|
||||
override fun endPlayback(hasEnded: Boolean, wasSkipped: Boolean, shouldContinue: Boolean, toStoppedState: Boolean) {
|
||||
Log.d(TAG, "endPlayback() called")
|
||||
Logd(TAG, "endPlayback() called")
|
||||
val isPlaying = status == PlayerStatus.PLAYING
|
||||
if (status != PlayerStatus.INDETERMINATE) setPlayerStatus(PlayerStatus.INDETERMINATE, media)
|
||||
|
||||
|
@ -414,9 +415,9 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
|
||||
val playNextEpisode = isPlaying && nextMedia != null
|
||||
when {
|
||||
playNextEpisode -> Log.d(TAG, "Playback of next episode will start immediately.")
|
||||
nextMedia == null -> Log.d(TAG, "No more episodes available to play")
|
||||
else -> Log.d(TAG, "Loading next episode, but not playing automatically.")
|
||||
playNextEpisode -> Logd(TAG, "Playback of next episode will start immediately.")
|
||||
nextMedia == null -> Logd(TAG, "No more episodes available to play")
|
||||
else -> Logd(TAG, "Loading next episode, but not playing automatically.")
|
||||
}
|
||||
|
||||
if (nextMedia != null) {
|
||||
|
|
|
@ -61,8 +61,7 @@ open class DbCleanupTests {
|
|||
adapter.open()
|
||||
adapter.close()
|
||||
|
||||
val prefEdit = PreferenceManager
|
||||
.getDefaultSharedPreferences(context.applicationContext).edit()
|
||||
val prefEdit = PreferenceManager.getDefaultSharedPreferences(context.applicationContext).edit()
|
||||
prefEdit.putString(UserPreferences.PREF_EPISODE_CACHE_SIZE, EPISODE_CACHE_SIZE.toString())
|
||||
prefEdit.putString(UserPreferences.PREF_EPISODE_CLEANUP, cleanupAlgorithm.toString())
|
||||
prefEdit.putBoolean(UserPreferences.PREF_ENABLE_AUTODL, true)
|
||||
|
@ -113,10 +112,7 @@ open class DbCleanupTests {
|
|||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun populateItems(numItems: Int, feed: Feed, items: MutableList<FeedItem>,
|
||||
files: MutableList<File>, itemState: Int, addToQueue: Boolean,
|
||||
addToFavorites: Boolean
|
||||
) {
|
||||
fun populateItems(numItems: Int, feed: Feed, items: MutableList<FeedItem>, files: MutableList<File>, itemState: Int, addToQueue: Boolean, addToFavorites: Boolean) {
|
||||
for (i in 0 until numItems) {
|
||||
val itemDate = Date((numItems - i).toLong())
|
||||
var playbackCompletionDate: Date? = null
|
||||
|
@ -136,12 +132,8 @@ open class DbCleanupTests {
|
|||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
adapter.setCompleteFeed(feed)
|
||||
if (addToQueue) {
|
||||
adapter.setQueue(items)
|
||||
}
|
||||
if (addToFavorites) {
|
||||
adapter.setFavorites(items)
|
||||
}
|
||||
if (addToQueue) adapter.setQueue(items)
|
||||
if (addToFavorites) adapter.setFavorites(items)
|
||||
adapter.close()
|
||||
|
||||
Assert.assertTrue(feed.id != 0L)
|
||||
|
|
|
@ -36,6 +36,7 @@ import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.getInstance
|
|||
import ac.mdiq.podcini.preferences.UserPreferences
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.enqueueLocation
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.shouldDeleteRemoveFromQueue
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import org.awaitility.Awaitility
|
||||
import org.junit.After
|
||||
import org.junit.Assert
|
||||
|
@ -465,11 +466,8 @@ class DbWriterTest {
|
|||
for (i in 0 until feed.items.size) {
|
||||
val feedItem: FeedItem = feed.items[i]
|
||||
val c = adapter.getFeedItemCursor(feedItem.id.toString())
|
||||
if (i < 2) {
|
||||
Assert.assertEquals(0, c.count.toLong())
|
||||
} else {
|
||||
Assert.assertEquals(1, c.count.toLong())
|
||||
}
|
||||
if (i < 2) Assert.assertEquals(0, c.count.toLong())
|
||||
else Assert.assertEquals(1, c.count.toLong())
|
||||
c.close()
|
||||
}
|
||||
adapter.close()
|
||||
|
@ -699,22 +697,16 @@ class DbWriterTest {
|
|||
// Use array rather than List to make codes more succinct
|
||||
val itemIds = toItemIds(feed.items).toTypedArray<Long>()
|
||||
|
||||
removeQueueItem(context, false,
|
||||
itemIds[1], itemIds[3])[TIMEOUT, TimeUnit.SECONDS]
|
||||
assertQueueByItemIds("Average case - 2 items removed successfully",
|
||||
itemIds[0], itemIds[2])
|
||||
removeQueueItem(context, false, itemIds[1], itemIds[3])[TIMEOUT, TimeUnit.SECONDS]
|
||||
assertQueueByItemIds("Average case - 2 items removed successfully", itemIds[0], itemIds[2])
|
||||
|
||||
removeQueueItem(context, false)[TIMEOUT, TimeUnit.SECONDS]
|
||||
assertQueueByItemIds("Boundary case - no items supplied. queue should see no change",
|
||||
itemIds[0], itemIds[2])
|
||||
assertQueueByItemIds("Boundary case - no items supplied. queue should see no change", itemIds[0], itemIds[2])
|
||||
|
||||
removeQueueItem(context, false,
|
||||
itemIds[0], itemIds[4], -1L)[TIMEOUT, TimeUnit.SECONDS]
|
||||
assertQueueByItemIds("Boundary case - items not in queue ignored",
|
||||
itemIds[2])
|
||||
removeQueueItem(context, false, itemIds[0], itemIds[4], -1L)[TIMEOUT, TimeUnit.SECONDS]
|
||||
assertQueueByItemIds("Boundary case - items not in queue ignored", itemIds[2])
|
||||
|
||||
removeQueueItem(context, false,
|
||||
itemIds[2], -1L)[TIMEOUT, TimeUnit.SECONDS]
|
||||
removeQueueItem(context, false, itemIds[2], -1L)[TIMEOUT, TimeUnit.SECONDS]
|
||||
assertQueueByItemIds("Boundary case - invalid itemIds ignored") // the queue is empty
|
||||
}
|
||||
|
||||
|
@ -741,10 +733,9 @@ class DbWriterTest {
|
|||
}
|
||||
for (from in 0 until numItems) {
|
||||
for (to in 0 until numItems) {
|
||||
if (from == to) {
|
||||
continue
|
||||
}
|
||||
Log.d(TAG, String.format(Locale.US, "testMoveQueueItem: From=%d, To=%d", from, to))
|
||||
if (from == to) continue
|
||||
|
||||
Logd(TAG, String.format(Locale.US, "testMoveQueueItem: From=%d, To=%d", from, to))
|
||||
val fromID: Long = feed.items[from].id
|
||||
|
||||
adapter = getInstance()
|
||||
|
@ -775,8 +766,7 @@ class DbWriterTest {
|
|||
val feed = Feed("url", null, "title")
|
||||
feed.items = mutableListOf()
|
||||
for (i in 0 until numItems) {
|
||||
val item = FeedItem(0, "title $i", "id $i", "link $i",
|
||||
Date(), FeedItem.NEW, feed)
|
||||
val item = FeedItem(0, "title $i", "id $i", "link $i", Date(), FeedItem.NEW, feed)
|
||||
item.setMedia(FeedMedia(item, "", 0, ""))
|
||||
feed.items.add(item)
|
||||
}
|
||||
|
@ -807,8 +797,7 @@ class DbWriterTest {
|
|||
val feed = Feed("url", null, "title")
|
||||
feed.items = mutableListOf()
|
||||
for (i in 0 until numItems) {
|
||||
val item = FeedItem(0, "title $i", "id $i", "link $i",
|
||||
Date(), FeedItem.PLAYED, feed)
|
||||
val item = FeedItem(0, "title $i", "id $i", "link $i", Date(), FeedItem.PLAYED, feed)
|
||||
item.setMedia(FeedMedia(item, "", 0, ""))
|
||||
feed.items.add(item)
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue