4.4.1 commit
This commit is contained in:
parent
c3f70fd1d4
commit
e08d508c41
|
@ -149,8 +149,8 @@ android {
|
||||||
// Version code schema (not used):
|
// Version code schema (not used):
|
||||||
// "1.2.3-beta4" -> 1020304
|
// "1.2.3-beta4" -> 1020304
|
||||||
// "1.2.3" -> 1020395
|
// "1.2.3" -> 1020395
|
||||||
versionCode 3020116
|
versionCode 3020117
|
||||||
versionName "4.4.0"
|
versionName "4.4.1"
|
||||||
|
|
||||||
def commit = ""
|
def commit = ""
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -30,6 +30,9 @@
|
||||||
android:name="android.hardware.touchscreen"
|
android:name="android.hardware.touchscreen"
|
||||||
android:required="false"/>
|
android:required="false"/>
|
||||||
|
|
||||||
|
<!-- this is now taken out from application -->
|
||||||
|
<!-- android:usesCleartextTraffic="true"-->
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="ac.mdiq.podcini.PodciniApp"
|
android:name="ac.mdiq.podcini.PodciniApp"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
@ -38,7 +41,6 @@
|
||||||
android:backupAgent=".storage.backup.OpmlBackupAgent"
|
android:backupAgent=".storage.backup.OpmlBackupAgent"
|
||||||
android:restoreAnyVersion="true"
|
android:restoreAnyVersion="true"
|
||||||
android:theme="@style/Theme.Podcini.Splash"
|
android:theme="@style/Theme.Podcini.Splash"
|
||||||
android:usesCleartextTraffic="true"
|
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:logo="@mipmap/ic_launcher"
|
android:logo="@mipmap/ic_launcher"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
|
|
|
@ -3,17 +3,16 @@ package ac.mdiq.podcini.net.discovery
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
|
||||||
object PodcastSearcherRegistry {
|
object PodcastSearcherRegistry {
|
||||||
@Suppress("UNNECESSARY_SAFE_CALL")
|
|
||||||
@get:Synchronized
|
@get:Synchronized
|
||||||
var searchProviders: MutableList<SearcherInfo> = mutableListOf()
|
var searchProviders: MutableList<SearcherInfo> = mutableListOf()
|
||||||
get() {
|
get() {
|
||||||
if (field.isEmpty()) {
|
if (field.isEmpty()) {
|
||||||
field = ArrayList()
|
field = ArrayList()
|
||||||
field?.add(SearcherInfo(CombinedSearcher(), 1.0f))
|
field.add(SearcherInfo(CombinedSearcher(), 1.0f))
|
||||||
field?.add(SearcherInfo(GpodnetPodcastSearcher(), 0.0f))
|
field.add(SearcherInfo(GpodnetPodcastSearcher(), 0.0f))
|
||||||
field?.add(SearcherInfo(FyydPodcastSearcher(), 1.0f))
|
field.add(SearcherInfo(FyydPodcastSearcher(), 1.0f))
|
||||||
field?.add(SearcherInfo(ItunesPodcastSearcher(), 1.0f))
|
field.add(SearcherInfo(ItunesPodcastSearcher(), 1.0f))
|
||||||
field?.add(SearcherInfo(PodcastIndexPodcastSearcher(), 1.0f))
|
field.add(SearcherInfo(PodcastIndexPodcastSearcher(), 1.0f))
|
||||||
}
|
}
|
||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,9 +67,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun initServiceRunning() {
|
private fun initServiceRunning() {
|
||||||
if (initialized) {
|
if (initialized) return
|
||||||
return
|
|
||||||
}
|
|
||||||
initialized = true
|
initialized = true
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
@ -171,7 +169,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
||||||
|
|
||||||
private val statusUpdate: BroadcastReceiver = object : BroadcastReceiver() {
|
private val statusUpdate: BroadcastReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
// Log.d(TAG, "Received statusUpdate Intent.")
|
Log.d(TAG, "Received statusUpdate Intent.")
|
||||||
if (playbackService != null) {
|
if (playbackService != null) {
|
||||||
val info = playbackService!!.pSMPInfo
|
val info = playbackService!!.pSMPInfo
|
||||||
status = info.playerStatus
|
status = info.playerStatus
|
||||||
|
@ -271,7 +269,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
||||||
PlaybackServiceStarter(activity, media!!)
|
PlaybackServiceStarter(activity, media!!)
|
||||||
.callEvenIfRunning(true)
|
.callEvenIfRunning(true)
|
||||||
.start()
|
.start()
|
||||||
Log.w(TAG, "Play/Pause button was pressed, but playbackservice was null!")
|
Log.w(TAG, "playbackservice was null, restarted!")
|
||||||
// return
|
// return
|
||||||
}
|
}
|
||||||
when (status) {
|
when (status) {
|
||||||
|
|
|
@ -84,88 +84,101 @@ class HttpDownloader(request: DownloadRequest) : Downloader(request) {
|
||||||
isGzip = TextUtils.equals(contentEncodingHeader.lowercase(Locale.getDefault()), "gzip")
|
isGzip = TextUtils.equals(contentEncodingHeader.lowercase(Locale.getDefault()), "gzip")
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "Response code is " + response.code)
|
Log.d(TAG, "Response code is " + response.code)// check if size specified in the response header is the same as the size of the
|
||||||
if (!response.isSuccessful && response.code == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
// written file. This check cannot be made if compression was used
|
||||||
Log.d(TAG, "Feed '" + downloadRequest.source + "' not modified since last update, Download canceled")
|
// Log.d(TAG,"buffer: $buffer")
|
||||||
onCancelled()
|
when {
|
||||||
return
|
!response.isSuccessful && response.code == HttpURLConnection.HTTP_NOT_MODIFIED -> {
|
||||||
} else if (!response.isSuccessful || response.body == null) {
|
Log.d(TAG, "Feed '" + downloadRequest.source + "' not modified since last update, Download canceled")
|
||||||
callOnFailByResponseCode(response)
|
onCancelled()
|
||||||
return
|
|
||||||
} else if (downloadRequest.feedfileType == FeedMedia.FEEDFILETYPE_FEEDMEDIA && isContentTypeTextAndSmallerThan100kb(response)) {
|
|
||||||
onFail(DownloadError.ERROR_FILE_TYPE, null)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
checkIfRedirect(response)
|
|
||||||
|
|
||||||
connection = BufferedInputStream(responseBody!!.byteStream())
|
|
||||||
|
|
||||||
val contentRangeHeader = if ((fileExists)) response.header("Content-Range") else null
|
|
||||||
if (fileExists && response.code == HttpURLConnection.HTTP_PARTIAL && !contentRangeHeader.isNullOrEmpty()) {
|
|
||||||
val start = contentRangeHeader.substring("bytes ".length, contentRangeHeader.indexOf("-"))
|
|
||||||
downloadRequest.soFar = start.toLong()
|
|
||||||
Log.d(TAG, "Starting download at position " + downloadRequest.soFar)
|
|
||||||
|
|
||||||
out = RandomAccessFile(destination, "rw")
|
|
||||||
out.seek(downloadRequest.soFar)
|
|
||||||
} else {
|
|
||||||
var success = destination.delete()
|
|
||||||
success = success or destination.createNewFile()
|
|
||||||
if (!success) {
|
|
||||||
throw IOException("Unable to recreate partially downloaded file")
|
|
||||||
}
|
|
||||||
out = RandomAccessFile(destination, "rw")
|
|
||||||
}
|
|
||||||
|
|
||||||
val buffer = ByteArray(BUFFER_SIZE)
|
|
||||||
var count = 0
|
|
||||||
downloadRequest.setStatusMsg(R.string.download_running)
|
|
||||||
Log.d(TAG, "Getting size of download")
|
|
||||||
downloadRequest.size = responseBody.contentLength() + downloadRequest.soFar
|
|
||||||
Log.d(TAG, "Size is " + downloadRequest.size)
|
|
||||||
if (downloadRequest.size < 0) {
|
|
||||||
downloadRequest.size = DownloadResult.SIZE_UNKNOWN.toLong()
|
|
||||||
}
|
|
||||||
|
|
||||||
val freeSpace = freeSpaceAvailable
|
|
||||||
Log.d(TAG, "Free space is $freeSpace")
|
|
||||||
if (downloadRequest.size != DownloadResult.SIZE_UNKNOWN.toLong() && downloadRequest.size > freeSpace) {
|
|
||||||
onFail(DownloadError.ERROR_NOT_ENOUGH_SPACE, null)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "Starting download")
|
|
||||||
try {
|
|
||||||
while (!cancelled && (connection.read(buffer).also { count = it }) != -1) {
|
|
||||||
// Log.d(TAG,"buffer: $buffer")
|
|
||||||
out.write(buffer, 0, count)
|
|
||||||
downloadRequest.soFar += count
|
|
||||||
val progressPercent = (100.0 * downloadRequest.soFar / downloadRequest.size).toInt()
|
|
||||||
downloadRequest.progressPercent = progressPercent
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.e(TAG, Log.getStackTraceString(e))
|
|
||||||
}
|
|
||||||
if (cancelled) {
|
|
||||||
onCancelled()
|
|
||||||
} else {
|
|
||||||
// check if size specified in the response header is the same as the size of the
|
|
||||||
// written file. This check cannot be made if compression was used
|
|
||||||
if (!isGzip && downloadRequest.size != DownloadResult.SIZE_UNKNOWN.toLong() && downloadRequest.soFar != downloadRequest.size) {
|
|
||||||
onFail(DownloadError.ERROR_IO_WRONG_SIZE, "Download completed but size: "
|
|
||||||
+ downloadRequest.soFar + " does not equal expected size " + downloadRequest.size)
|
|
||||||
return
|
|
||||||
} else if (downloadRequest.size > 0 && downloadRequest.soFar == 0L) {
|
|
||||||
onFail(DownloadError.ERROR_IO_ERROR, "Download completed, but nothing was read")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val lastModified = response.header("Last-Modified")
|
!response.isSuccessful || response.body == null -> {
|
||||||
if (lastModified != null) {
|
callOnFailByResponseCode(response)
|
||||||
downloadRequest.setLastModified(lastModified)
|
return
|
||||||
} else {
|
}
|
||||||
downloadRequest.setLastModified(response.header("ETag"))
|
downloadRequest.feedfileType == FeedMedia.FEEDFILETYPE_FEEDMEDIA && isContentTypeTextAndSmallerThan100kb(response) -> {
|
||||||
|
onFail(DownloadError.ERROR_FILE_TYPE, null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
checkIfRedirect(response)
|
||||||
|
|
||||||
|
connection = BufferedInputStream(responseBody!!.byteStream())
|
||||||
|
|
||||||
|
val contentRangeHeader = if ((fileExists)) response.header("Content-Range") else null
|
||||||
|
if (fileExists && response.code == HttpURLConnection.HTTP_PARTIAL && !contentRangeHeader.isNullOrEmpty()) {
|
||||||
|
val start = contentRangeHeader.substring("bytes ".length, contentRangeHeader.indexOf("-"))
|
||||||
|
downloadRequest.soFar = start.toLong()
|
||||||
|
Log.d(TAG, "Starting download at position " + downloadRequest.soFar)
|
||||||
|
|
||||||
|
out = RandomAccessFile(destination, "rw")
|
||||||
|
out.seek(downloadRequest.soFar)
|
||||||
|
} else {
|
||||||
|
var success = destination.delete()
|
||||||
|
success = success or destination.createNewFile()
|
||||||
|
if (!success) {
|
||||||
|
throw IOException("Unable to recreate partially downloaded file")
|
||||||
|
}
|
||||||
|
out = RandomAccessFile(destination, "rw")
|
||||||
|
}
|
||||||
|
|
||||||
|
val buffer = ByteArray(BUFFER_SIZE)
|
||||||
|
var count = 0
|
||||||
|
downloadRequest.setStatusMsg(R.string.download_running)
|
||||||
|
Log.d(TAG, "Getting size of download")
|
||||||
|
downloadRequest.size = responseBody.contentLength() + downloadRequest.soFar
|
||||||
|
Log.d(TAG, "Size is " + downloadRequest.size)
|
||||||
|
if (downloadRequest.size < 0) {
|
||||||
|
downloadRequest.size = DownloadResult.SIZE_UNKNOWN.toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
val freeSpace = freeSpaceAvailable
|
||||||
|
Log.d(TAG, "Free space is $freeSpace")
|
||||||
|
if (downloadRequest.size != DownloadResult.SIZE_UNKNOWN.toLong() && downloadRequest.size > freeSpace) {
|
||||||
|
onFail(DownloadError.ERROR_NOT_ENOUGH_SPACE, null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Starting download")
|
||||||
|
try {
|
||||||
|
while (!cancelled && (connection.read(buffer).also { count = it }) != -1) {
|
||||||
|
// Log.d(TAG,"buffer: $buffer")
|
||||||
|
out.write(buffer, 0, count)
|
||||||
|
downloadRequest.soFar += count
|
||||||
|
val progressPercent = (100.0 * downloadRequest.soFar / downloadRequest.size).toInt()
|
||||||
|
downloadRequest.progressPercent = progressPercent
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(TAG, Log.getStackTraceString(e))
|
||||||
|
}
|
||||||
|
if (cancelled) {
|
||||||
|
onCancelled()
|
||||||
|
} else {
|
||||||
|
// check if size specified in the response header is the same as the size of the
|
||||||
|
// written file. This check cannot be made if compression was used
|
||||||
|
when {
|
||||||
|
!isGzip && downloadRequest.size != DownloadResult.SIZE_UNKNOWN.toLong() && downloadRequest.soFar != downloadRequest.size -> {
|
||||||
|
onFail(DownloadError.ERROR_IO_WRONG_SIZE, "Download completed but size: "
|
||||||
|
+ downloadRequest.soFar + " does not equal expected size " + downloadRequest.size)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
downloadRequest.size > 0 && downloadRequest.soFar == 0L -> {
|
||||||
|
onFail(DownloadError.ERROR_IO_ERROR, "Download completed, but nothing was read")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val lastModified = response.header("Last-Modified")
|
||||||
|
if (lastModified != null) {
|
||||||
|
downloadRequest.setLastModified(lastModified)
|
||||||
|
} else {
|
||||||
|
downloadRequest.setLastModified(response.header("ETag"))
|
||||||
|
}
|
||||||
|
onSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onSuccess()
|
|
||||||
}
|
}
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
|
|
@ -139,12 +139,13 @@ class NewEpisodesNotification {
|
||||||
private fun loadIcon(context: Context, feed: Feed): Bitmap? {
|
private fun loadIcon(context: Context, feed: Feed): Bitmap? {
|
||||||
val iconSize = (128 * context.resources.displayMetrics.density).toInt()
|
val iconSize = (128 * context.resources.displayMetrics.density).toInt()
|
||||||
return try {
|
return try {
|
||||||
Glide.with(context)
|
if (!feed.imageUrl.isNullOrBlank()) Glide.with(context)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.load(feed.imageUrl)
|
.load(feed.imageUrl)
|
||||||
.apply(RequestOptions().centerCrop())
|
.apply(RequestOptions().centerCrop())
|
||||||
.submit(iconSize, iconSize)
|
.submit(iconSize, iconSize)
|
||||||
.get()
|
.get()
|
||||||
|
else null
|
||||||
} catch (tr: Throwable) {
|
} catch (tr: Throwable) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ class PlaybackServiceNotificationBuilder(private val context: Context) {
|
||||||
val options = RequestOptions().centerCrop()
|
val options = RequestOptions().centerCrop()
|
||||||
try {
|
try {
|
||||||
var imgLoc = playable?.getImageLocation()
|
var imgLoc = playable?.getImageLocation()
|
||||||
if (imgLoc != null) {
|
if (!imgLoc.isNullOrBlank()) {
|
||||||
cachedIcon = Glide.with(context)
|
cachedIcon = Glide.with(context)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.load(imgLoc)
|
.load(imgLoc)
|
||||||
|
@ -73,7 +73,7 @@ class PlaybackServiceNotificationBuilder(private val context: Context) {
|
||||||
.get()
|
.get()
|
||||||
} else if (playable != null) {
|
} else if (playable != null) {
|
||||||
imgLoc = ImageResourceUtils.getFallbackImageLocation(playable!!)
|
imgLoc = ImageResourceUtils.getFallbackImageLocation(playable!!)
|
||||||
if (imgLoc != null) {
|
if (!imgLoc.isNullOrBlank()) {
|
||||||
cachedIcon = Glide.with(context)
|
cachedIcon = Glide.with(context)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.load(imgLoc)
|
.load(imgLoc)
|
||||||
|
@ -92,8 +92,7 @@ class PlaybackServiceNotificationBuilder(private val context: Context) {
|
||||||
private val defaultIcon: Bitmap?
|
private val defaultIcon: Bitmap?
|
||||||
get() {
|
get() {
|
||||||
if (Companion.defaultIcon == null) {
|
if (Companion.defaultIcon == null) {
|
||||||
Companion.defaultIcon = getBitmap(
|
Companion.defaultIcon = getBitmap(context, R.mipmap.ic_launcher)
|
||||||
context, R.mipmap.ic_launcher)
|
|
||||||
}
|
}
|
||||||
return Companion.defaultIcon
|
return Companion.defaultIcon
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import java.util.*
|
||||||
class RemoteMedia : Playable {
|
class RemoteMedia : Playable {
|
||||||
private var itemIdentifier: String? = null
|
private var itemIdentifier: String? = null
|
||||||
private val downloadUrl: String? = null
|
private val downloadUrl: String? = null
|
||||||
private val imageUrl: String? = null
|
private val imageUrl: String?
|
||||||
private val notes: String? = null
|
private val notes: String? = null
|
||||||
|
|
||||||
private val streamUrl: String?
|
private val streamUrl: String?
|
||||||
|
@ -52,6 +52,7 @@ class RemoteMedia : Playable {
|
||||||
this.episodeLink = episodeLink
|
this.episodeLink = episodeLink
|
||||||
this.feedAuthor = feedAuthor
|
this.feedAuthor = feedAuthor
|
||||||
this.imageLocation = imageUrl
|
this.imageLocation = imageUrl
|
||||||
|
this.imageUrl = imageUrl
|
||||||
this.feedLink = feedLink
|
this.feedLink = feedLink
|
||||||
this.mimeType = mimeType
|
this.mimeType = mimeType
|
||||||
this.pubDate = pubDate
|
this.pubDate = pubDate
|
||||||
|
@ -67,9 +68,11 @@ class RemoteMedia : Playable {
|
||||||
this.episodeLink = item.link
|
this.episodeLink = item.link
|
||||||
this.feedAuthor = item.feed?.author
|
this.feedAuthor = item.feed?.author
|
||||||
if (!item.imageUrl.isNullOrEmpty()) {
|
if (!item.imageUrl.isNullOrEmpty()) {
|
||||||
this.imageLocation = item.imageUrl
|
this.imageLocation = item.imageLocation
|
||||||
|
this.imageUrl = item.imageUrl
|
||||||
} else {
|
} else {
|
||||||
this.imageLocation = item.feed?.imageUrl
|
this.imageLocation = item.feed?.imageUrl
|
||||||
|
this.imageUrl = item.feed?.imageUrl
|
||||||
}
|
}
|
||||||
this.feedLink = item.feed?.link
|
this.feedLink = item.feed?.link
|
||||||
this.mimeType = item.media?.mime_type
|
this.mimeType = item.media?.mime_type
|
||||||
|
@ -181,7 +184,7 @@ class RemoteMedia : Playable {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getImageLocation(): String? {
|
override fun getImageLocation(): String? {
|
||||||
return imageUrl
|
return imageLocation
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun describeContents(): Int {
|
override fun describeContents(): Int {
|
||||||
|
|
|
@ -176,12 +176,9 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
isPaused = true
|
isPaused = true
|
||||||
EventBus.getDefault().unregister(this)
|
EventBus.getDefault().unregister(this)
|
||||||
if (downloader != null && !downloader!!.isFinished) {
|
if (downloader != null && !downloader!!.isFinished) downloader!!.cancel()
|
||||||
downloader!!.cancel()
|
|
||||||
}
|
if (dialog != null && dialog!!.isShowing) dialog!!.dismiss()
|
||||||
if (dialog != null && dialog!!.isShowing) {
|
|
||||||
dialog!!.dismiss()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun onDestroy() {
|
public override fun onDestroy() {
|
||||||
|
@ -329,7 +326,6 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
||||||
.subscribeOn(Schedulers.computation())
|
.subscribeOn(Schedulers.computation())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribeWith(object : DisposableMaybeObserver<FeedHandlerResult?>() {
|
.subscribeWith(object : DisposableMaybeObserver<FeedHandlerResult?>() {
|
||||||
|
|
||||||
@UnstableApi override fun onSuccess(result: FeedHandlerResult) {
|
@UnstableApi override fun onSuccess(result: FeedHandlerResult) {
|
||||||
showFeedInformation(result.feed, result.alternateFeedUrls)
|
showFeedInformation(result.feed, result.alternateFeedUrls)
|
||||||
}
|
}
|
||||||
|
@ -398,7 +394,7 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
||||||
|
|
||||||
binding.episodeLabel.setOnClickListener { showEpisodes(feed.items)}
|
binding.episodeLabel.setOnClickListener { showEpisodes(feed.items)}
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(feed.imageUrl)) {
|
if (!feed.imageUrl.isNullOrBlank()) {
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.load(feed.imageUrl)
|
.load(feed.imageUrl)
|
||||||
.apply(RequestOptions()
|
.apply(RequestOptions()
|
||||||
|
@ -499,40 +495,44 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
||||||
val dli = DownloadServiceInterface.get()
|
val dli = DownloadServiceInterface.get()
|
||||||
if (dli == null || selectedDownloadUrl == null) return
|
if (dli == null || selectedDownloadUrl == null) return
|
||||||
|
|
||||||
if (dli.isDownloadingEpisode(selectedDownloadUrl!!)) {
|
when {
|
||||||
binding.subscribeButton.isEnabled = false
|
dli.isDownloadingEpisode(selectedDownloadUrl!!) -> {
|
||||||
binding.subscribeButton.setText(R.string.subscribing_label)
|
binding.subscribeButton.isEnabled = false
|
||||||
} else if (feedInFeedlist()) {
|
binding.subscribeButton.setText(R.string.subscribing_label)
|
||||||
binding.subscribeButton.isEnabled = true
|
|
||||||
binding.subscribeButton.setText(R.string.open_podcast)
|
|
||||||
if (didPressSubscribe) {
|
|
||||||
didPressSubscribe = false
|
|
||||||
|
|
||||||
val feed1 = DBReader.getFeed(feedId)?: return
|
|
||||||
val feedPreferences = feed1.preferences
|
|
||||||
if (feedPreferences != null) {
|
|
||||||
if (isEnableAutodownload) {
|
|
||||||
val autoDownload = binding.autoDownloadCheckBox.isChecked
|
|
||||||
feedPreferences.autoDownload = autoDownload
|
|
||||||
|
|
||||||
val preferences = getSharedPreferences(PREFS, MODE_PRIVATE)
|
|
||||||
val editor = preferences.edit()
|
|
||||||
editor.putBoolean(PREF_LAST_AUTO_DOWNLOAD, autoDownload)
|
|
||||||
editor.apply()
|
|
||||||
}
|
|
||||||
if (username != null) {
|
|
||||||
feedPreferences.username = username
|
|
||||||
feedPreferences.password = password
|
|
||||||
}
|
|
||||||
DBWriter.setFeedPreferences(feedPreferences)
|
|
||||||
}
|
|
||||||
openFeed()
|
|
||||||
}
|
}
|
||||||
} else {
|
feedInFeedlist() -> {
|
||||||
binding.subscribeButton.isEnabled = true
|
binding.subscribeButton.isEnabled = true
|
||||||
binding.subscribeButton.setText(R.string.subscribe_label)
|
binding.subscribeButton.setText(R.string.open)
|
||||||
if (isEnableAutodownload) {
|
if (didPressSubscribe) {
|
||||||
binding.autoDownloadCheckBox.visibility = View.VISIBLE
|
didPressSubscribe = false
|
||||||
|
|
||||||
|
val feed1 = DBReader.getFeed(feedId)?: return
|
||||||
|
val feedPreferences = feed1.preferences
|
||||||
|
if (feedPreferences != null) {
|
||||||
|
if (isEnableAutodownload) {
|
||||||
|
val autoDownload = binding.autoDownloadCheckBox.isChecked
|
||||||
|
feedPreferences.autoDownload = autoDownload
|
||||||
|
|
||||||
|
val preferences = getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||||
|
val editor = preferences.edit()
|
||||||
|
editor.putBoolean(PREF_LAST_AUTO_DOWNLOAD, autoDownload)
|
||||||
|
editor.apply()
|
||||||
|
}
|
||||||
|
if (username != null) {
|
||||||
|
feedPreferences.username = username
|
||||||
|
feedPreferences.password = password
|
||||||
|
}
|
||||||
|
DBWriter.setFeedPreferences(feedPreferences)
|
||||||
|
}
|
||||||
|
openFeed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
binding.subscribeButton.isEnabled = true
|
||||||
|
binding.subscribeButton.setText(R.string.subscribe_label)
|
||||||
|
if (isEnableAutodownload) {
|
||||||
|
binding.autoDownloadCheckBox.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -579,9 +579,7 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
||||||
setResult(RESULT_ERROR)
|
setResult(RESULT_ERROR)
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
if (dialog != null && dialog!!.isShowing) {
|
if (dialog != null && dialog!!.isShowing) dialog!!.dismiss()
|
||||||
dialog!!.dismiss()
|
|
||||||
}
|
|
||||||
dialog = builder.show()
|
dialog = builder.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -615,17 +613,13 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
||||||
val urlsMap: Map<String, String>
|
val urlsMap: Map<String, String>
|
||||||
try {
|
try {
|
||||||
urlsMap = fd.findLinks(feedFile, baseUrl)
|
urlsMap = fd.findLinks(feedFile, baseUrl)
|
||||||
if (urlsMap.isEmpty()) {
|
if (urlsMap.isEmpty()) return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPaused || isFinishing) {
|
if (isPaused || isFinishing) return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
val titles: MutableList<String?> = ArrayList()
|
val titles: MutableList<String?> = ArrayList()
|
||||||
|
|
||||||
|
@ -657,9 +651,7 @@ class OnlineFeedViewActivity : AppCompatActivity() {
|
||||||
.setAdapter(adapter, onClickListener)
|
.setAdapter(adapter, onClickListener)
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (dialog != null && dialog!!.isShowing) {
|
if (dialog != null && dialog!!.isShowing) dialog!!.dismiss()
|
||||||
dialog!!.dismiss()
|
|
||||||
}
|
|
||||||
dialog = ab.show()
|
dialog = ab.show()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -106,7 +106,7 @@ class SelectSubscriptionActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private fun getBitmapFromUrl(feed: Feed) {
|
private fun getBitmapFromUrl(feed: Feed) {
|
||||||
val iconSize = (128 * resources.displayMetrics.density).toInt()
|
val iconSize = (128 * resources.displayMetrics.density).toInt()
|
||||||
Glide.with(this)
|
if (!feed.imageUrl.isNullOrBlank()) Glide.with(this)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.load(feed.imageUrl)
|
.load(feed.imageUrl)
|
||||||
.apply(RequestOptions.overrideOf(iconSize, iconSize))
|
.apply(RequestOptions.overrideOf(iconSize, iconSize))
|
||||||
|
|
|
@ -89,12 +89,15 @@ class ChaptersListAdapter(private val context: Context, private val callback: Ca
|
||||||
if (sc.imageUrl.isNullOrEmpty()) {
|
if (sc.imageUrl.isNullOrEmpty()) {
|
||||||
Glide.with(context).clear(holder.image)
|
Glide.with(context).clear(holder.image)
|
||||||
} else {
|
} else {
|
||||||
if (media != null) Glide.with(context)
|
if (media != null) {
|
||||||
.load(EmbeddedChapterImage.getModelFor(media!!, position))
|
val imgUrl = EmbeddedChapterImage.getModelFor(media!!,position)
|
||||||
.apply(RequestOptions()
|
if (imgUrl != null) Glide.with(context)
|
||||||
.dontAnimate()
|
.load(imgUrl)
|
||||||
.transform(FitCenter(), RoundedCorners((4 * context.resources.displayMetrics.density).toInt())))
|
.apply(RequestOptions()
|
||||||
.into(holder.image)
|
.dontAnimate()
|
||||||
|
.transform(FitCenter(), RoundedCorners((4 * context.resources.displayMetrics.density).toInt())))
|
||||||
|
.into(holder.image)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
holder.image.visibility = View.GONE
|
holder.image.visibility = View.GONE
|
||||||
|
|
|
@ -77,7 +77,7 @@ class CoverLoader(activity: MainActivity) {
|
||||||
.load(uri)
|
.load(uri)
|
||||||
.apply(options)
|
.apply(options)
|
||||||
|
|
||||||
if (fallbackUri != null) {
|
if (!fallbackUri.isNullOrBlank()) {
|
||||||
builder = builder.error(Glide.with(imgvCover!!)
|
builder = builder.error(Glide.with(imgvCover!!)
|
||||||
.`as`(Drawable::class.java)
|
.`as`(Drawable::class.java)
|
||||||
.load(fallbackUri)
|
.load(fallbackUri)
|
||||||
|
|
|
@ -53,7 +53,7 @@ class FeedDiscoverAdapter(mainActivity: MainActivity) : BaseAdapter() {
|
||||||
val podcast: PodcastSearchResult? = getItem(position)
|
val podcast: PodcastSearchResult? = getItem(position)
|
||||||
holder.imageView!!.contentDescription = podcast?.title
|
holder.imageView!!.contentDescription = podcast?.title
|
||||||
|
|
||||||
Glide.with(mainActivityRef.get()!!)
|
if (!podcast?.imageUrl.isNullOrBlank()) Glide.with(mainActivityRef.get()!!)
|
||||||
.load(podcast?.imageUrl)
|
.load(podcast?.imageUrl)
|
||||||
.apply(RequestOptions()
|
.apply(RequestOptions()
|
||||||
.placeholder(R.color.light_gray)
|
.placeholder(R.color.light_gray)
|
||||||
|
|
|
@ -77,7 +77,7 @@ open class HorizontalFeedListAdapter(mainActivity: MainActivity) :
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
Glide.with(mainActivityRef.get()!!)
|
if (!podcast.imageUrl.isNullOrBlank()) Glide.with(mainActivityRef.get()!!)
|
||||||
.load(podcast.imageUrl)
|
.load(podcast.imageUrl)
|
||||||
.apply(RequestOptions()
|
.apply(RequestOptions()
|
||||||
.placeholder(R.color.light_gray)
|
.placeholder(R.color.light_gray)
|
||||||
|
|
|
@ -287,7 +287,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
||||||
val feed = drawerItem.feed
|
val feed = drawerItem.feed
|
||||||
val context = activity.get() ?: return
|
val context = activity.get() ?: return
|
||||||
|
|
||||||
Glide.with(context)
|
if (!feed.imageUrl.isNullOrBlank()) Glide.with(context)
|
||||||
.load(feed.imageUrl)
|
.load(feed.imageUrl)
|
||||||
.apply(RequestOptions()
|
.apply(RequestOptions()
|
||||||
.placeholder(R.color.light_gray)
|
.placeholder(R.color.light_gray)
|
||||||
|
|
|
@ -64,7 +64,7 @@ class OnlineFeedsAdapter(private val context: Context, objects: List<PodcastSear
|
||||||
} else viewHolder.updateView.visibility = View.INVISIBLE
|
} else viewHolder.updateView.visibility = View.INVISIBLE
|
||||||
|
|
||||||
//Update the empty imageView with the image from the feed
|
//Update the empty imageView with the image from the feed
|
||||||
Glide.with(context)
|
if (!podcast.imageUrl.isNullOrBlank()) Glide.with(context)
|
||||||
.load(podcast.imageUrl)
|
.load(podcast.imageUrl)
|
||||||
.apply(RequestOptions()
|
.apply(RequestOptions()
|
||||||
.placeholder(R.color.light_gray)
|
.placeholder(R.color.light_gray)
|
||||||
|
|
|
@ -27,7 +27,7 @@ class SimpleIconListAdapter<T : SimpleIconListAdapter.ListItem>(private val cont
|
||||||
val binding = SimpleIconListItemBinding.bind(view!!)
|
val binding = SimpleIconListItemBinding.bind(view!!)
|
||||||
binding.title.text = item.title
|
binding.title.text = item.title
|
||||||
binding.subtitle.text = item.subtitle
|
binding.subtitle.text = item.subtitle
|
||||||
Glide.with(context)
|
if (item.imageUrl.isNotBlank()) Glide.with(context)
|
||||||
.load(item.imageUrl)
|
.load(item.imageUrl)
|
||||||
.apply(RequestOptions()
|
.apply(RequestOptions()
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import ac.mdiq.podcini.storage.DBTasks
|
||||||
import ac.mdiq.podcini.playback.PlaybackServiceStarter
|
import ac.mdiq.podcini.playback.PlaybackServiceStarter
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
import ac.mdiq.podcini.storage.model.playback.MediaType
|
import ac.mdiq.podcini.storage.model.playback.MediaType
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
class PlayActionButton(item: FeedItem) : ItemActionButton(item) {
|
class PlayActionButton(item: FeedItem) : ItemActionButton(item) {
|
||||||
override fun getLabel(): Int {
|
override fun getLabel(): Int {
|
||||||
|
@ -18,6 +19,7 @@ class PlayActionButton(item: FeedItem) : ItemActionButton(item) {
|
||||||
}
|
}
|
||||||
@UnstableApi override fun onClick(context: Context) {
|
@UnstableApi override fun onClick(context: Context) {
|
||||||
val media = item.media ?: return
|
val media = item.media ?: return
|
||||||
|
Log.d("PlayActionButton", "onClick called")
|
||||||
if (!media.fileExists()) {
|
if (!media.fileExists()) {
|
||||||
DBTasks.notifyMissingFeedMediaFile(context, media)
|
DBTasks.notifyMissingFeedMediaFile(context, media)
|
||||||
return
|
return
|
||||||
|
|
|
@ -121,8 +121,7 @@ class SwipeActionsDialog(private val context: Context, private val tag: String)
|
||||||
|
|
||||||
for (i in keys.indices) {
|
for (i in keys.indices) {
|
||||||
val action = keys[i]
|
val action = keys[i]
|
||||||
val item = SwipeactionsPickerItemBinding.inflate(LayoutInflater.from(
|
val item = SwipeactionsPickerItemBinding.inflate(LayoutInflater.from(context))
|
||||||
context))
|
|
||||||
item.swipeActionLabel.text = action.getTitle(context)
|
item.swipeActionLabel.text = action.getTitle(context)
|
||||||
|
|
||||||
val icon = DrawableCompat.wrap(AppCompatResources.getDrawable(context, action.getActionIcon())!!)
|
val icon = DrawableCompat.wrap(AppCompatResources.getDrawable(context, action.getActionIcon())!!)
|
||||||
|
|
|
@ -71,6 +71,8 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
||||||
lateinit var listAdapter: EpisodeItemListAdapter
|
lateinit var listAdapter: EpisodeItemListAdapter
|
||||||
protected lateinit var txtvInformation: TextView
|
protected lateinit var txtvInformation: TextView
|
||||||
|
|
||||||
|
private var currentPlaying: EpisodeItemViewHolder? = null
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var episodes: MutableList<FeedItem> = ArrayList()
|
var episodes: MutableList<FeedItem> = ArrayList()
|
||||||
|
|
||||||
|
@ -327,7 +329,7 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: FeedItemEvent) {
|
fun onEventMainThread(event: FeedItemEvent) {
|
||||||
Log.d(TAG, "onEventMainThread() called with: event = [$event]")
|
Log.d(TAG, "onEventMainThread() called with FeedItemEvent event = [$event]")
|
||||||
for (item in event.items) {
|
for (item in event.items) {
|
||||||
val pos: Int = FeedItemUtil.indexOfItemWithId(episodes, item.id)
|
val pos: Int = FeedItemUtil.indexOfItemWithId(episodes, item.id)
|
||||||
if (pos >= 0) {
|
if (pos >= 0) {
|
||||||
|
@ -344,11 +346,19 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect
|
||||||
|
|
||||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||||
for (i in 0 until listAdapter.itemCount) {
|
// Log.d(TAG, "onEventMainThread() called with PlaybackPositionEvent event = [$event]")
|
||||||
val holder: EpisodeItemViewHolder? = recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem)
|
||||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
currentPlaying!!.notifyPlaybackPositionUpdated(event)
|
||||||
holder.notifyPlaybackPositionUpdated(event)
|
else {
|
||||||
break
|
Log.d(TAG, "onEventMainThread() search list")
|
||||||
|
for (i in 0 until listAdapter.itemCount) {
|
||||||
|
val holder: EpisodeItemViewHolder? =
|
||||||
|
recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||||
|
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||||
|
currentPlaying = holder
|
||||||
|
holder.notifyPlaybackPositionUpdated(event)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
|
||||||
|
|
||||||
private var disposable: Disposable? = null
|
private var disposable: Disposable? = null
|
||||||
private var displayUpArrow = false
|
private var displayUpArrow = false
|
||||||
|
private var currentPlaying: EpisodeItemViewHolder? = null
|
||||||
|
|
||||||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
|
@ -272,12 +272,19 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
|
||||||
|
|
||||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||||
// if (event == null) return
|
// Log.d(TAG, "onEventMainThread() called with PlaybackPositionEvent event = [$event]")
|
||||||
for (i in 0 until adapter.itemCount) {
|
if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem)
|
||||||
val holder: EpisodeItemViewHolder? = recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
currentPlaying!!.notifyPlaybackPositionUpdated(event)
|
||||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
else {
|
||||||
holder.notifyPlaybackPositionUpdated(event)
|
Log.d(TAG, "onEventMainThread() search list")
|
||||||
break
|
for (i in 0 until adapter.itemCount) {
|
||||||
|
val holder: EpisodeItemViewHolder? =
|
||||||
|
recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||||
|
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||||
|
currentPlaying = holder
|
||||||
|
holder.notifyPlaybackPositionUpdated(event)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refreshInfoBar()
|
refreshInfoBar()
|
||||||
|
|
|
@ -35,6 +35,7 @@ import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Button
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
@ -189,7 +190,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
val balloon: Balloon = Balloon.Builder(requireContext())
|
val balloon: Balloon = Balloon.Builder(requireContext())
|
||||||
.setArrowOrientation(ArrowOrientation.TOP)
|
.setArrowOrientation(ArrowOrientation.TOP)
|
||||||
.setArrowOrientationRules(ArrowOrientationRules.ALIGN_FIXED)
|
.setArrowOrientationRules(ArrowOrientationRules.ALIGN_FIXED)
|
||||||
.setArrowPosition(0.25f + (if ((isLocaleRtl xor offerStreaming)) 0f else 0.5f))
|
.setArrowPosition(0.25f + (if (isLocaleRtl xor offerStreaming) 0f else 0.5f))
|
||||||
.setWidthRatio(1.0f)
|
.setWidthRatio(1.0f)
|
||||||
.setMarginLeft(8)
|
.setMarginLeft(8)
|
||||||
.setMarginRight(8)
|
.setMarginRight(8)
|
||||||
|
@ -199,10 +200,10 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
.setDismissWhenTouchOutside(true)
|
.setDismissWhenTouchOutside(true)
|
||||||
.setLifecycleOwner(this)
|
.setLifecycleOwner(this)
|
||||||
.build()
|
.build()
|
||||||
val binding_ = PopupBubbleViewBinding.bind(balloon.getContentView())
|
val ballonView = balloon.getContentView()
|
||||||
val positiveButton = binding_.balloonButtonPositive
|
val positiveButton = ballonView.findViewById(R.id.balloon_button_positive) as Button
|
||||||
val negativeButton = binding_.balloonButtonNegative
|
val negativeButton = ballonView.findViewById(R.id.balloon_button_negative) as Button
|
||||||
val message: TextView = binding_.balloonMessage
|
val message: TextView = ballonView.findViewById(R.id.balloon_message) as TextView
|
||||||
message.setText(if (offerStreaming) R.string.on_demand_config_stream_text
|
message.setText(if (offerStreaming) R.string.on_demand_config_stream_text
|
||||||
else R.string.on_demand_config_download_text)
|
else R.string.on_demand_config_download_text)
|
||||||
positiveButton.setOnClickListener {
|
positiveButton.setOnClickListener {
|
||||||
|
@ -297,13 +298,15 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
RoundedCorners((8 * resources.displayMetrics.density).toInt()))
|
RoundedCorners((8 * resources.displayMetrics.density).toInt()))
|
||||||
.dontAnimate()
|
.dontAnimate()
|
||||||
|
|
||||||
Glide.with(this)
|
val imgLocFB = ImageResourceUtils.getFallbackImageLocation(item!!)
|
||||||
.load(item!!.imageLocation)
|
if (!item!!.imageLocation.isNullOrBlank() || !imgLocFB.isNullOrBlank())
|
||||||
.error(Glide.with(this)
|
Glide.with(this)
|
||||||
.load(ImageResourceUtils.getFallbackImageLocation(item!!))
|
.load(item!!.imageLocation)
|
||||||
.apply(options))
|
.error(Glide.with(this)
|
||||||
.apply(options)
|
.load(imgLocFB)
|
||||||
.into(imgvCover)
|
.apply(options))
|
||||||
|
.apply(options)
|
||||||
|
.into(imgvCover)
|
||||||
updateButtons()
|
updateButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,15 +401,9 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
|
|
||||||
@UnstableApi @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
@UnstableApi @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: EpisodeDownloadEvent) {
|
fun onEventMainThread(event: EpisodeDownloadEvent) {
|
||||||
if (item == null || item!!.media == null) {
|
if (item == null || item!!.media == null) return
|
||||||
return
|
if (!event.urls.contains(item!!.media!!.download_url)) return
|
||||||
}
|
if (itemsLoaded && activity != null) updateButtons()
|
||||||
if (!event.urls.contains(item!!.media!!.download_url)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (itemsLoaded && activity != null) {
|
|
||||||
updateButtons()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
package ac.mdiq.podcini.ui.fragment
|
package ac.mdiq.podcini.ui.fragment
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.allEpisodesSortOrder
|
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.prefFilterAllEpisodes
|
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
||||||
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.AllEpisodesFilterDialog.AllEpisodesFilterChangedEvent
|
||||||
import ac.mdiq.podcini.ui.dialog.ItemSortDialog
|
|
||||||
import ac.mdiq.podcini.util.event.FeedListUpdateEvent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
@ -18,8 +12,6 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import org.apache.commons.lang3.StringUtils
|
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
@ -49,6 +41,7 @@ class EpisodesListFragment : BaseEpisodesListFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadData(): List<FeedItem> {
|
override fun loadData(): List<FeedItem> {
|
||||||
|
if (episodeList.isEmpty()) return listOf()
|
||||||
return episodeList.subList(0, min(episodeList.size-1, page * EPISODES_PER_PAGE))
|
return episodeList.subList(0, min(episodeList.size-1, page * EPISODES_PER_PAGE))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,8 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
|
|
||||||
private var showTimeLeft = false
|
private var showTimeLeft = false
|
||||||
|
|
||||||
|
private var currentMedia: Playable? = null
|
||||||
|
|
||||||
private var controller: PlaybackController? = null
|
private var controller: PlaybackController? = null
|
||||||
private var disposable: Disposable? = null
|
private var disposable: Disposable? = null
|
||||||
|
|
||||||
|
@ -192,6 +194,7 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadMediaInfo() {
|
override fun loadMediaInfo() {
|
||||||
|
Log.d(TAG, "setupPlaybackController loadMediaInfo called")
|
||||||
this@ExternalPlayerFragment.loadMediaInfo()
|
this@ExternalPlayerFragment.loadMediaInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,19 +287,24 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
private fun loadMediaInfo() {
|
private fun loadMediaInfo() {
|
||||||
Log.d(TAG, "Loading media info")
|
Log.d(TAG, "loadMediaInfo called")
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
Log.w(TAG, "loadMediaInfo was called while PlaybackController was null!")
|
Log.w(TAG, "loadMediaInfo was called while PlaybackController was null!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
val theMedia = controller?.getMedia()
|
||||||
disposable?.dispose()
|
if (currentMedia == null || theMedia?.getIdentifier() != currentMedia?.getIdentifier()) {
|
||||||
disposable = Maybe.fromCallable<Playable?> { controller?.getMedia() }
|
Log.d(TAG, "reloading media info")
|
||||||
.subscribeOn(Schedulers.io())
|
disposable?.dispose()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
disposable = Maybe.fromCallable<Playable?> { theMedia }
|
||||||
.subscribe({ media: Playable? -> this.updateUi(media) },
|
.subscribeOn(Schedulers.io())
|
||||||
{ error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) },
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
{ (activity as MainActivity).setPlayerVisible(false) })
|
.subscribe({ media: Playable? ->
|
||||||
|
currentMedia = media
|
||||||
|
this.updateUi(media) },
|
||||||
|
{ error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) },
|
||||||
|
{ (activity as MainActivity).setPlayerVisible(false) })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(UnstableApi::class) override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
@OptIn(UnstableApi::class) override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||||
|
@ -354,9 +362,8 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
private fun updateUi(media: Playable?) {
|
private fun updateUi(media: Playable?) {
|
||||||
if (media == null) {
|
if (media == null) return
|
||||||
return
|
|
||||||
}
|
|
||||||
episodeTitle.text = media.getEpisodeTitle()
|
episodeTitle.text = media.getEpisodeTitle()
|
||||||
(activity as MainActivity).setPlayerVisible(true)
|
(activity as MainActivity).setPlayerVisible(true)
|
||||||
onPositionObserverUpdate(PlaybackPositionEvent(media.getPosition(), media.getDuration()))
|
onPositionObserverUpdate(PlaybackPositionEvent(media.getPosition(), media.getDuration()))
|
||||||
|
@ -367,13 +374,19 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
.fitCenter()
|
.fitCenter()
|
||||||
.dontAnimate()
|
.dontAnimate()
|
||||||
|
|
||||||
Glide.with(this)
|
val imgLoc = getEpisodeListImageLocation(media)
|
||||||
.load(getEpisodeListImageLocation(media))
|
val imgLocFB = getFallbackImageLocation(media)
|
||||||
.error(Glide.with(this)
|
if (!imgLoc.isNullOrBlank())
|
||||||
.load(getFallbackImageLocation(media))
|
Glide.with(this)
|
||||||
.apply(options))
|
.load(imgLoc)
|
||||||
.apply(options)
|
.apply(options)
|
||||||
.into(imgvCover)
|
.into(imgvCover)
|
||||||
|
else if (!imgLocFB.isNullOrBlank())
|
||||||
|
Glide.with(this)
|
||||||
|
.load(imgLocFB)
|
||||||
|
.apply(options)
|
||||||
|
.into(imgvCover)
|
||||||
|
else imgvCover.setImageResource(R.mipmap.ic_launcher)
|
||||||
|
|
||||||
if (controller?.isPlayingVideoLocally == true) {
|
if (controller?.isPlayingVideoLocally == true) {
|
||||||
(activity as MainActivity).bottomSheet.setLocked(true)
|
(activity as MainActivity).bottomSheet.setLocked(true)
|
||||||
|
|
|
@ -186,22 +186,24 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
||||||
Log.d(TAG, "Language is " + feed!!.language)
|
Log.d(TAG, "Language is " + feed!!.language)
|
||||||
Log.d(TAG, "Author is " + feed!!.author)
|
Log.d(TAG, "Author is " + feed!!.author)
|
||||||
Log.d(TAG, "URL is " + feed!!.download_url)
|
Log.d(TAG, "URL is " + feed!!.download_url)
|
||||||
Glide.with(this)
|
if (!feed?.imageUrl.isNullOrBlank()) {
|
||||||
.load(feed!!.imageUrl)
|
Glide.with(this)
|
||||||
.apply(RequestOptions()
|
.load(feed!!.imageUrl)
|
||||||
.placeholder(R.color.light_gray)
|
.apply(RequestOptions()
|
||||||
.error(R.color.light_gray)
|
.placeholder(R.color.light_gray)
|
||||||
.fitCenter()
|
.error(R.color.light_gray)
|
||||||
.dontAnimate())
|
.fitCenter()
|
||||||
.into(imgvCover)
|
.dontAnimate())
|
||||||
Glide.with(this)
|
.into(imgvCover)
|
||||||
.load(feed!!.imageUrl)
|
Glide.with(this)
|
||||||
.apply(RequestOptions()
|
.load(feed!!.imageUrl)
|
||||||
.placeholder(R.color.image_readability_tint)
|
.apply(RequestOptions()
|
||||||
.error(R.color.image_readability_tint)
|
.placeholder(R.color.image_readability_tint)
|
||||||
.transform(FastBlurTransformation())
|
.error(R.color.image_readability_tint)
|
||||||
.dontAnimate())
|
.transform(FastBlurTransformation())
|
||||||
.into(imgvBackground)
|
.dontAnimate())
|
||||||
|
.into(imgvBackground)
|
||||||
|
}
|
||||||
|
|
||||||
txtvTitle.text = feed!!.title
|
txtvTitle.text = feed!!.title
|
||||||
txtvTitle.setMaxLines(6)
|
txtvTitle.setMaxLines(6)
|
||||||
|
|
|
@ -77,7 +77,9 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
||||||
private lateinit var adapter: FeedItemListAdapter
|
private lateinit var adapter: FeedItemListAdapter
|
||||||
private lateinit var swipeActions: SwipeActions
|
private lateinit var swipeActions: SwipeActions
|
||||||
private lateinit var nextPageLoader: MoreContentListFooterUtil
|
private lateinit var nextPageLoader: MoreContentListFooterUtil
|
||||||
|
|
||||||
|
private var currentPlaying: EpisodeItemViewHolder? = null
|
||||||
|
|
||||||
private var displayUpArrow = false
|
private var displayUpArrow = false
|
||||||
private var headerCreated = false
|
private var headerCreated = false
|
||||||
private var feedID: Long = 0
|
private var feedID: Long = 0
|
||||||
|
@ -314,7 +316,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: FeedItemEvent) {
|
fun onEventMainThread(event: FeedItemEvent) {
|
||||||
Log.d(TAG, "onEventMainThread() called with: event = [$event]")
|
Log.d(TAG, "onEventMainThread() called with FeedItemEvent event = [$event]")
|
||||||
if (feed == null || feed!!.items.isEmpty()) {
|
if (feed == null || feed!!.items.isEmpty()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -334,7 +336,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
||||||
|
|
||||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: EpisodeDownloadEvent) {
|
fun onEventMainThread(event: EpisodeDownloadEvent) {
|
||||||
Log.d(TAG, "onEventMainThread() called with: event = [$event]")
|
Log.d(TAG, "onEventMainThread() called with EpisodeDownloadEvent event = [$event]")
|
||||||
if (feed == null || feed!!.items.isEmpty()) {
|
if (feed == null || feed!!.items.isEmpty()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -348,13 +350,19 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
||||||
|
|
||||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||||
Log.d(TAG, "onEventMainThread() called with: event = [$event]")
|
// Log.d(TAG, "onEventMainThread() called with PlaybackPositionEvent event = [$event]")
|
||||||
for (i in 0 until adapter.itemCount) {
|
if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem)
|
||||||
val holder: EpisodeItemViewHolder? =
|
currentPlaying!!.notifyPlaybackPositionUpdated(event)
|
||||||
binding.recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
else {
|
||||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
Log.d(TAG, "onEventMainThread() search list")
|
||||||
holder.notifyPlaybackPositionUpdated(event)
|
for (i in 0 until adapter.itemCount) {
|
||||||
break
|
val holder: EpisodeItemViewHolder? =
|
||||||
|
binding.recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||||
|
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||||
|
currentPlaying = holder
|
||||||
|
holder.notifyPlaybackPositionUpdated(event)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -521,24 +529,25 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFeedImage() {
|
private fun loadFeedImage() {
|
||||||
if (feed == null) return
|
if (!feed?.imageUrl.isNullOrBlank()) {
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.load(feed!!.imageUrl)
|
.load(feed!!.imageUrl)
|
||||||
.apply(RequestOptions()
|
.apply(RequestOptions()
|
||||||
.placeholder(R.color.image_readability_tint)
|
.placeholder(R.color.image_readability_tint)
|
||||||
.error(R.color.image_readability_tint)
|
.error(R.color.image_readability_tint)
|
||||||
.transform(FastBlurTransformation())
|
.transform(FastBlurTransformation())
|
||||||
.dontAnimate())
|
.dontAnimate())
|
||||||
.into(binding.imgvBackground)
|
.into(binding.imgvBackground)
|
||||||
|
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.load(feed!!.imageUrl)
|
.load(feed!!.imageUrl)
|
||||||
.apply(RequestOptions()
|
.apply(RequestOptions()
|
||||||
.placeholder(R.color.light_gray)
|
.placeholder(R.color.light_gray)
|
||||||
.error(R.color.light_gray)
|
.error(R.color.light_gray)
|
||||||
.fitCenter()
|
.fitCenter()
|
||||||
.dontAnimate())
|
.dontAnimate())
|
||||||
.into(binding.header.imgvCover)
|
.into(binding.header.imgvCover)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi private fun loadItems() {
|
@UnstableApi private fun loadItems() {
|
||||||
|
@ -657,7 +666,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG: String = "ItemlistFragment"
|
const val TAG: String = "FeedItemlistFragment"
|
||||||
private const val ARGUMENT_FEED_ID = "argument.ac.mdiq.podcini.feed_id"
|
private const val ARGUMENT_FEED_ID = "argument.ac.mdiq.podcini.feed_id"
|
||||||
private const val KEY_UP_ARROW = "up_arrow"
|
private const val KEY_UP_ARROW = "up_arrow"
|
||||||
|
|
||||||
|
|
|
@ -98,8 +98,7 @@ class OnlineSearchFragment : Fragment() {
|
||||||
firstVisibleItem: Int,
|
firstVisibleItem: Int,
|
||||||
visibleItemCount: Int,
|
visibleItemCount: Int,
|
||||||
totalItemCount: Int
|
totalItemCount: Int
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,8 +279,9 @@ class PlayerDetailsFragment : Fragment() {
|
||||||
if (displayedChapterIndex == -1 || media!!.getChapters().isEmpty() || media!!.getChapters()[displayedChapterIndex].imageUrl.isNullOrEmpty()) {
|
if (displayedChapterIndex == -1 || media!!.getChapters().isEmpty() || media!!.getChapters()[displayedChapterIndex].imageUrl.isNullOrEmpty()) {
|
||||||
cover.into(binding.imgvCover)
|
cover.into(binding.imgvCover)
|
||||||
} else {
|
} else {
|
||||||
Glide.with(this)
|
val imgLoc = EmbeddedChapterImage.getModelFor(media!!, displayedChapterIndex)
|
||||||
.load(EmbeddedChapterImage.getModelFor(media!!, displayedChapterIndex))
|
if (imgLoc != null) Glide.with(this)
|
||||||
|
.load(imgLoc)
|
||||||
.apply(options)
|
.apply(options)
|
||||||
.thumbnail(cover)
|
.thumbnail(cover)
|
||||||
.error(cover)
|
.error(cover)
|
||||||
|
|
|
@ -81,6 +81,8 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
||||||
private var queue: MutableList<FeedItem> = mutableListOf()
|
private var queue: MutableList<FeedItem> = mutableListOf()
|
||||||
|
|
||||||
private var recyclerAdapter: QueueRecyclerAdapter? = null
|
private var recyclerAdapter: QueueRecyclerAdapter? = null
|
||||||
|
private var currentPlaying: EpisodeItemViewHolder? = null
|
||||||
|
|
||||||
private var disposable: Disposable? = null
|
private var disposable: Disposable? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
@ -205,7 +207,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: QueueEvent) {
|
fun onEventMainThread(event: QueueEvent) {
|
||||||
Log.d(TAG, "onEventMainThread() called with: event = [$event]")
|
Log.d(TAG, "onEventMainThread() called with QueueEvent event = [$event]")
|
||||||
if (recyclerAdapter == null) {
|
if (recyclerAdapter == null) {
|
||||||
loadItems(true)
|
loadItems(true)
|
||||||
return
|
return
|
||||||
|
@ -242,7 +244,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: FeedItemEvent) {
|
fun onEventMainThread(event: FeedItemEvent) {
|
||||||
Log.d(TAG, "onEventMainThread() called with: event = [$event]")
|
Log.d(TAG, "onEventMainThread() called with FeedItemEvent event = [$event]")
|
||||||
if (recyclerAdapter == null) {
|
if (recyclerAdapter == null) {
|
||||||
loadItems(true)
|
loadItems(true)
|
||||||
return
|
return
|
||||||
|
@ -264,6 +266,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
||||||
|
|
||||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: EpisodeDownloadEvent) {
|
fun onEventMainThread(event: EpisodeDownloadEvent) {
|
||||||
|
Log.d(TAG, "onEventMainThread() called with EpisodeDownloadEvent event = [$event]")
|
||||||
for (downloadUrl in event.urls) {
|
for (downloadUrl in event.urls) {
|
||||||
val pos: Int = FeedItemUtil.indexOfItemWithDownloadUrl(queue.toList(), downloadUrl)
|
val pos: Int = FeedItemUtil.indexOfItemWithDownloadUrl(queue.toList(), downloadUrl)
|
||||||
if (pos >= 0) {
|
if (pos >= 0) {
|
||||||
|
@ -274,12 +277,20 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
||||||
|
|
||||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||||
|
// Log.d(TAG, "onEventMainThread() called with PlaybackPositionEvent event = [$event]")
|
||||||
if (recyclerAdapter != null) {
|
if (recyclerAdapter != null) {
|
||||||
for (i in 0 until recyclerAdapter!!.itemCount) {
|
if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem)
|
||||||
val holder: EpisodeItemViewHolder? = recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
currentPlaying!!.notifyPlaybackPositionUpdated(event)
|
||||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
else {
|
||||||
holder.notifyPlaybackPositionUpdated(event)
|
Log.d(TAG, "onEventMainThread() search list")
|
||||||
break
|
for (i in 0 until recyclerAdapter!!.itemCount) {
|
||||||
|
val holder: EpisodeItemViewHolder? =
|
||||||
|
recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||||
|
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||||
|
currentPlaying = holder
|
||||||
|
holder.notifyPlaybackPositionUpdated(event)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,6 +298,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onPlayerStatusChanged(event: PlayerStatusEvent?) {
|
fun onPlayerStatusChanged(event: PlayerStatusEvent?) {
|
||||||
|
Log.d(TAG, "onPlayerStatusChanged() called with event = [$event]")
|
||||||
loadItems(false)
|
loadItems(false)
|
||||||
refreshToolbarState()
|
refreshToolbarState()
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
||||||
private lateinit var automaticSearchDebouncer: Handler
|
private lateinit var automaticSearchDebouncer: Handler
|
||||||
|
|
||||||
private var results: MutableList<FeedItem> = mutableListOf()
|
private var results: MutableList<FeedItem> = mutableListOf()
|
||||||
|
private var currentPlaying: EpisodeItemViewHolder? = null
|
||||||
|
|
||||||
private var disposable: Disposable? = null
|
private var disposable: Disposable? = null
|
||||||
private var lastQueryChange: Long = 0
|
private var lastQueryChange: Long = 0
|
||||||
|
@ -291,12 +292,18 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
|
||||||
|
|
||||||
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: PlaybackPositionEvent) {
|
fun onEventMainThread(event: PlaybackPositionEvent) {
|
||||||
for (i in 0 until adapter.itemCount) {
|
if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem)
|
||||||
val holder: EpisodeItemViewHolder? =
|
currentPlaying!!.notifyPlaybackPositionUpdated(event)
|
||||||
recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
else {
|
||||||
if (holder != null && holder.isCurrentlyPlayingItem) {
|
Log.d(FeedItemlistFragment.TAG, "onEventMainThread() search list")
|
||||||
holder.notifyPlaybackPositionUpdated(event)
|
for (i in 0 until adapter.itemCount) {
|
||||||
break
|
val holder: EpisodeItemViewHolder? =
|
||||||
|
recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder
|
||||||
|
if (holder != null && holder.isCurrentlyPlayingItem) {
|
||||||
|
currentPlaying = holder
|
||||||
|
holder.notifyPlaybackPositionUpdated(event)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ abstract class StatisticsListAdapter protected constructor(@JvmField protected v
|
||||||
} else {
|
} else {
|
||||||
val holder = h as StatisticsHolder
|
val holder = h as StatisticsHolder
|
||||||
val statsItem = statisticsData!![position - 1]
|
val statsItem = statisticsData!![position - 1]
|
||||||
Glide.with(context)
|
if (!statsItem.feed.imageUrl.isNullOrBlank()) Glide.with(context)
|
||||||
.load(statsItem.feed.imageUrl)
|
.load(statsItem.feed.imageUrl)
|
||||||
.apply(RequestOptions()
|
.apply(RequestOptions()
|
||||||
.placeholder(R.color.light_gray)
|
.placeholder(R.color.light_gray)
|
||||||
|
|
|
@ -120,12 +120,14 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coverHolder.visibility == View.VISIBLE) {
|
if (coverHolder.visibility == View.VISIBLE) {
|
||||||
CoverLoader(activity)
|
val imgLoc = ImageResourceUtils.getEpisodeListImageLocation(item)
|
||||||
.withUri(ImageResourceUtils.getEpisodeListImageLocation(item))
|
if (!imgLoc.isNullOrBlank()) CoverLoader(activity)
|
||||||
|
.withUri(imgLoc)
|
||||||
.withFallbackUri(item.feed?.imageUrl)
|
.withFallbackUri(item.feed?.imageUrl)
|
||||||
.withPlaceholderView(placeholder)
|
.withPlaceholderView(placeholder)
|
||||||
.withCoverView(cover)
|
.withCoverView(cover)
|
||||||
.load()
|
.load()
|
||||||
|
else cover.setImageResource(R.mipmap.ic_launcher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,7 @@
|
||||||
<string name="filtered_label">Filtered</string>
|
<string name="filtered_label">Filtered</string>
|
||||||
<string name="refresh_failed_msg">{fa-exclamation-circle} Last refresh failed. Tap to view details.</string>
|
<string name="refresh_failed_msg">{fa-exclamation-circle} Last refresh failed. Tap to view details.</string>
|
||||||
<string name="open_podcast">Open podcast</string>
|
<string name="open_podcast">Open podcast</string>
|
||||||
|
<string name="open">Open</string>
|
||||||
<string name="please_wait_for_data">Please wait until the data is loaded</string>
|
<string name="please_wait_for_data">Please wait until the data is loaded</string>
|
||||||
<string name="updates_disabled_label">Updates disabled</string>
|
<string name="updates_disabled_label">Updates disabled</string>
|
||||||
<plurals name="updated_feeds_batch_label">
|
<plurals name="updated_feeds_batch_label">
|
||||||
|
|
11
changelog.md
11
changelog.md
|
@ -155,4 +155,13 @@
|
||||||
* revamped online feed view activity
|
* revamped online feed view activity
|
||||||
* episodes (50 most recent) of any online feed can be viewed and played (streamed) directly without subscribing to the feed
|
* episodes (50 most recent) of any online feed can be viewed and played (streamed) directly without subscribing to the feed
|
||||||
* bug fixes on passing Glide with null addresses
|
* bug fixes on passing Glide with null addresses
|
||||||
* null safety enhancements in code
|
* null safety enhancements in code
|
||||||
|
|
||||||
|
## 4.4.1
|
||||||
|
|
||||||
|
* fixed bug of app crash on stream episode customization
|
||||||
|
* disabled usesCleartextTraffic, connection to http sites appear OK, report if you find an issue
|
||||||
|
* enforced non-null load location for most Glide calls
|
||||||
|
* avoided redundant media loadings and ui updates when a new audio starts
|
||||||
|
* eliminated frequent list search during audio play, a serious energy waste
|
||||||
|
* icons in online episode list, when unavailable, are set to app logo
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
Version 4.4.1 brings several changes:
|
||||||
|
|
||||||
|
* fixed bug of app crash on stream episode customization
|
||||||
|
* disabled usesCleartextTraffic, connection to http sites appear OK, report if you find an issue
|
||||||
|
* enforced non-null load location for most Glide calls
|
||||||
|
* avoided redundant media loadings and ui updates when a new audio starts
|
||||||
|
* eliminated frequent list search during audio play, a serious energy waste
|
||||||
|
* icons in online episode list, when unavailable, are set to app logo
|
Loading…
Reference in New Issue