From e08d508c413acc37c2dc618243b10619eafbefeb Mon Sep 17 00:00:00 2001 From: Xilin Jia <6257601+XilinJia@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:34:42 +0000 Subject: [PATCH] 4.4.1 commit --- app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 4 +- .../net/discovery/PodcastSearcherRegistry.kt | 11 +- .../podcini/playback/PlaybackController.kt | 8 +- .../service/download/HttpDownloader.kt | 171 ++++++++++-------- .../download/NewEpisodesNotification.kt | 3 +- .../PlaybackServiceNotificationBuilder.kt | 7 +- .../storage/model/playback/RemoteMedia.kt | 9 +- .../ui/activity/OnlineFeedViewActivity.kt | 98 +++++----- .../ui/activity/SelectSubscriptionActivity.kt | 2 +- .../podcini/ui/adapter/ChaptersListAdapter.kt | 15 +- .../ac/mdiq/podcini/ui/adapter/CoverLoader.kt | 2 +- .../podcini/ui/adapter/FeedDiscoverAdapter.kt | 2 +- .../ui/adapter/HorizontalFeedListAdapter.kt | 2 +- .../mdiq/podcini/ui/adapter/NavListAdapter.kt | 2 +- .../podcini/ui/adapter/OnlineFeedsAdapter.kt | 2 +- .../ui/adapter/SimpleIconListAdapter.kt | 2 +- .../adapter/actionbutton/PlayActionButton.kt | 2 + .../podcini/ui/dialog/SwipeActionsDialog.kt | 3 +- .../ui/fragment/BaseEpisodesListFragment.kt | 22 ++- .../ui/fragment/CompletedDownloadsFragment.kt | 21 ++- .../ui/fragment/EpisodeInfoFragment.kt | 39 ++-- .../ui/fragment/EpisodesListFragment.kt | 9 +- .../ui/fragment/ExternalPlayerFragment.kt | 51 ++++-- .../podcini/ui/fragment/FeedInfoFragment.kt | 34 ++-- .../ui/fragment/FeedItemlistFragment.kt | 65 ++++--- .../ui/fragment/OnlineSearchFragment.kt | 3 +- .../ui/fragment/PlayerDetailsFragment.kt | 5 +- .../mdiq/podcini/ui/fragment/QueueFragment.kt | 26 ++- .../podcini/ui/fragment/SearchFragment.kt | 19 +- .../ui/statistics/StatisticsListAdapter.kt | 2 +- .../view/viewholder/EpisodeItemViewHolder.kt | 6 +- app/src/main/res/values/strings.xml | 1 + changelog.md | 11 +- .../android/en-US/changelogs/3020117.txt | 9 + 35 files changed, 377 insertions(+), 295 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/3020117.txt diff --git a/app/build.gradle b/app/build.gradle index 4d38abbc..ac96a066 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -149,8 +149,8 @@ android { // Version code schema (not used): // "1.2.3-beta4" -> 1020304 // "1.2.3" -> 1020395 - versionCode 3020116 - versionName "4.4.0" + versionCode 3020117 + versionName "4.4.1" def commit = "" try { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aff26007..0b8835df 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,6 +30,9 @@ android:name="android.hardware.touchscreen" android:required="false"/> + + + = mutableListOf() get() { if (field.isEmpty()) { field = ArrayList() - field?.add(SearcherInfo(CombinedSearcher(), 1.0f)) - field?.add(SearcherInfo(GpodnetPodcastSearcher(), 0.0f)) - field?.add(SearcherInfo(FyydPodcastSearcher(), 1.0f)) - field?.add(SearcherInfo(ItunesPodcastSearcher(), 1.0f)) - field?.add(SearcherInfo(PodcastIndexPodcastSearcher(), 1.0f)) + field.add(SearcherInfo(CombinedSearcher(), 1.0f)) + field.add(SearcherInfo(GpodnetPodcastSearcher(), 0.0f)) + field.add(SearcherInfo(FyydPodcastSearcher(), 1.0f)) + field.add(SearcherInfo(ItunesPodcastSearcher(), 1.0f)) + field.add(SearcherInfo(PodcastIndexPodcastSearcher(), 1.0f)) } return field } diff --git a/app/src/main/java/ac/mdiq/podcini/playback/PlaybackController.kt b/app/src/main/java/ac/mdiq/podcini/playback/PlaybackController.kt index f7cce4df..db81ed51 100644 --- a/app/src/main/java/ac/mdiq/podcini/playback/PlaybackController.kt +++ b/app/src/main/java/ac/mdiq/podcini/playback/PlaybackController.kt @@ -67,9 +67,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) { @Synchronized private fun initServiceRunning() { - if (initialized) { - return - } + if (initialized) return initialized = true 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() { override fun onReceive(context: Context, intent: Intent) { -// Log.d(TAG, "Received statusUpdate Intent.") + Log.d(TAG, "Received statusUpdate Intent.") if (playbackService != null) { val info = playbackService!!.pSMPInfo status = info.playerStatus @@ -271,7 +269,7 @@ abstract class PlaybackController(private val activity: FragmentActivity) { PlaybackServiceStarter(activity, media!!) .callEvenIfRunning(true) .start() - Log.w(TAG, "Play/Pause button was pressed, but playbackservice was null!") + Log.w(TAG, "playbackservice was null, restarted!") // return } when (status) { diff --git a/app/src/main/java/ac/mdiq/podcini/service/download/HttpDownloader.kt b/app/src/main/java/ac/mdiq/podcini/service/download/HttpDownloader.kt index 0baf0967..db467ac7 100644 --- a/app/src/main/java/ac/mdiq/podcini/service/download/HttpDownloader.kt +++ b/app/src/main/java/ac/mdiq/podcini/service/download/HttpDownloader.kt @@ -84,88 +84,101 @@ class HttpDownloader(request: DownloadRequest) : Downloader(request) { isGzip = TextUtils.equals(contentEncodingHeader.lowercase(Locale.getDefault()), "gzip") } - Log.d(TAG, "Response code is " + response.code) - if (!response.isSuccessful && response.code == HttpURLConnection.HTTP_NOT_MODIFIED) { - Log.d(TAG, "Feed '" + downloadRequest.source + "' not modified since last update, Download canceled") - onCancelled() - return - } else if (!response.isSuccessful || response.body == null) { - callOnFailByResponseCode(response) - 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") + Log.d(TAG, "Response code is " + response.code)// 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 + // Log.d(TAG,"buffer: $buffer") + when { + !response.isSuccessful && response.code == HttpURLConnection.HTTP_NOT_MODIFIED -> { + Log.d(TAG, "Feed '" + downloadRequest.source + "' not modified since last update, Download canceled") + onCancelled() return } - val lastModified = response.header("Last-Modified") - if (lastModified != null) { - downloadRequest.setLastModified(lastModified) - } else { - downloadRequest.setLastModified(response.header("ETag")) + !response.isSuccessful || response.body == null -> { + callOnFailByResponseCode(response) + return + } + 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) { e.printStackTrace() diff --git a/app/src/main/java/ac/mdiq/podcini/service/download/NewEpisodesNotification.kt b/app/src/main/java/ac/mdiq/podcini/service/download/NewEpisodesNotification.kt index fb6d03ec..6f2afab4 100644 --- a/app/src/main/java/ac/mdiq/podcini/service/download/NewEpisodesNotification.kt +++ b/app/src/main/java/ac/mdiq/podcini/service/download/NewEpisodesNotification.kt @@ -139,12 +139,13 @@ class NewEpisodesNotification { private fun loadIcon(context: Context, feed: Feed): Bitmap? { val iconSize = (128 * context.resources.displayMetrics.density).toInt() return try { - Glide.with(context) + if (!feed.imageUrl.isNullOrBlank()) Glide.with(context) .asBitmap() .load(feed.imageUrl) .apply(RequestOptions().centerCrop()) .submit(iconSize, iconSize) .get() + else null } catch (tr: Throwable) { null } diff --git a/app/src/main/java/ac/mdiq/podcini/service/playback/PlaybackServiceNotificationBuilder.kt b/app/src/main/java/ac/mdiq/podcini/service/playback/PlaybackServiceNotificationBuilder.kt index cbf04693..fc70f98a 100644 --- a/app/src/main/java/ac/mdiq/podcini/service/playback/PlaybackServiceNotificationBuilder.kt +++ b/app/src/main/java/ac/mdiq/podcini/service/playback/PlaybackServiceNotificationBuilder.kt @@ -64,7 +64,7 @@ class PlaybackServiceNotificationBuilder(private val context: Context) { val options = RequestOptions().centerCrop() try { var imgLoc = playable?.getImageLocation() - if (imgLoc != null) { + if (!imgLoc.isNullOrBlank()) { cachedIcon = Glide.with(context) .asBitmap() .load(imgLoc) @@ -73,7 +73,7 @@ class PlaybackServiceNotificationBuilder(private val context: Context) { .get() } else if (playable != null) { imgLoc = ImageResourceUtils.getFallbackImageLocation(playable!!) - if (imgLoc != null) { + if (!imgLoc.isNullOrBlank()) { cachedIcon = Glide.with(context) .asBitmap() .load(imgLoc) @@ -92,8 +92,7 @@ class PlaybackServiceNotificationBuilder(private val context: Context) { private val defaultIcon: Bitmap? get() { if (Companion.defaultIcon == null) { - Companion.defaultIcon = getBitmap( - context, R.mipmap.ic_launcher) + Companion.defaultIcon = getBitmap(context, R.mipmap.ic_launcher) } return Companion.defaultIcon } diff --git a/app/src/main/java/ac/mdiq/podcini/storage/model/playback/RemoteMedia.kt b/app/src/main/java/ac/mdiq/podcini/storage/model/playback/RemoteMedia.kt index b5593af6..7edc0f70 100644 --- a/app/src/main/java/ac/mdiq/podcini/storage/model/playback/RemoteMedia.kt +++ b/app/src/main/java/ac/mdiq/podcini/storage/model/playback/RemoteMedia.kt @@ -20,7 +20,7 @@ import java.util.* class RemoteMedia : Playable { private var itemIdentifier: String? = null private val downloadUrl: String? = null - private val imageUrl: String? = null + private val imageUrl: String? private val notes: String? = null private val streamUrl: String? @@ -52,6 +52,7 @@ class RemoteMedia : Playable { this.episodeLink = episodeLink this.feedAuthor = feedAuthor this.imageLocation = imageUrl + this.imageUrl = imageUrl this.feedLink = feedLink this.mimeType = mimeType this.pubDate = pubDate @@ -67,9 +68,11 @@ class RemoteMedia : Playable { this.episodeLink = item.link this.feedAuthor = item.feed?.author if (!item.imageUrl.isNullOrEmpty()) { - this.imageLocation = item.imageUrl + this.imageLocation = item.imageLocation + this.imageUrl = item.imageUrl } else { this.imageLocation = item.feed?.imageUrl + this.imageUrl = item.feed?.imageUrl } this.feedLink = item.feed?.link this.mimeType = item.media?.mime_type @@ -181,7 +184,7 @@ class RemoteMedia : Playable { } override fun getImageLocation(): String? { - return imageUrl + return imageLocation } override fun describeContents(): Int { diff --git a/app/src/main/java/ac/mdiq/podcini/ui/activity/OnlineFeedViewActivity.kt b/app/src/main/java/ac/mdiq/podcini/ui/activity/OnlineFeedViewActivity.kt index ee3a149d..ad1df4fa 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/activity/OnlineFeedViewActivity.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/activity/OnlineFeedViewActivity.kt @@ -176,12 +176,9 @@ class OnlineFeedViewActivity : AppCompatActivity() { super.onStop() isPaused = true EventBus.getDefault().unregister(this) - if (downloader != null && !downloader!!.isFinished) { - downloader!!.cancel() - } - if (dialog != null && dialog!!.isShowing) { - dialog!!.dismiss() - } + if (downloader != null && !downloader!!.isFinished) downloader!!.cancel() + + if (dialog != null && dialog!!.isShowing) dialog!!.dismiss() } public override fun onDestroy() { @@ -329,7 +326,6 @@ class OnlineFeedViewActivity : AppCompatActivity() { .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(object : DisposableMaybeObserver() { - @UnstableApi override fun onSuccess(result: FeedHandlerResult) { showFeedInformation(result.feed, result.alternateFeedUrls) } @@ -398,7 +394,7 @@ class OnlineFeedViewActivity : AppCompatActivity() { binding.episodeLabel.setOnClickListener { showEpisodes(feed.items)} - if (StringUtils.isNotBlank(feed.imageUrl)) { + if (!feed.imageUrl.isNullOrBlank()) { Glide.with(this) .load(feed.imageUrl) .apply(RequestOptions() @@ -499,40 +495,44 @@ class OnlineFeedViewActivity : AppCompatActivity() { val dli = DownloadServiceInterface.get() if (dli == null || selectedDownloadUrl == null) return - if (dli.isDownloadingEpisode(selectedDownloadUrl!!)) { - binding.subscribeButton.isEnabled = false - binding.subscribeButton.setText(R.string.subscribing_label) - } else if (feedInFeedlist()) { - 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() + when { + dli.isDownloadingEpisode(selectedDownloadUrl!!) -> { + binding.subscribeButton.isEnabled = false + binding.subscribeButton.setText(R.string.subscribing_label) } - } else { - binding.subscribeButton.isEnabled = true - binding.subscribeButton.setText(R.string.subscribe_label) - if (isEnableAutodownload) { - binding.autoDownloadCheckBox.visibility = View.VISIBLE + feedInFeedlist() -> { + binding.subscribeButton.isEnabled = true + binding.subscribeButton.setText(R.string.open) + 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 -> { + 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) finish() } - if (dialog != null && dialog!!.isShowing) { - dialog!!.dismiss() - } + if (dialog != null && dialog!!.isShowing) dialog!!.dismiss() dialog = builder.show() } } @@ -615,17 +613,13 @@ class OnlineFeedViewActivity : AppCompatActivity() { val urlsMap: Map try { urlsMap = fd.findLinks(feedFile, baseUrl) - if (urlsMap.isEmpty()) { - return false - } + if (urlsMap.isEmpty()) return false } catch (e: IOException) { e.printStackTrace() return false } - if (isPaused || isFinishing) { - return false - } + if (isPaused || isFinishing) return false val titles: MutableList = ArrayList() @@ -657,9 +651,7 @@ class OnlineFeedViewActivity : AppCompatActivity() { .setAdapter(adapter, onClickListener) runOnUiThread { - if (dialog != null && dialog!!.isShowing) { - dialog!!.dismiss() - } + if (dialog != null && dialog!!.isShowing) dialog!!.dismiss() dialog = ab.show() } return true diff --git a/app/src/main/java/ac/mdiq/podcini/ui/activity/SelectSubscriptionActivity.kt b/app/src/main/java/ac/mdiq/podcini/ui/activity/SelectSubscriptionActivity.kt index a01e1622..84db520a 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/activity/SelectSubscriptionActivity.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/activity/SelectSubscriptionActivity.kt @@ -106,7 +106,7 @@ class SelectSubscriptionActivity : AppCompatActivity() { private fun getBitmapFromUrl(feed: Feed) { val iconSize = (128 * resources.displayMetrics.density).toInt() - Glide.with(this) + if (!feed.imageUrl.isNullOrBlank()) Glide.with(this) .asBitmap() .load(feed.imageUrl) .apply(RequestOptions.overrideOf(iconSize, iconSize)) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/ChaptersListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/ChaptersListAdapter.kt index 453fe2e4..8a15df62 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/ChaptersListAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/ChaptersListAdapter.kt @@ -89,12 +89,15 @@ class ChaptersListAdapter(private val context: Context, private val callback: Ca if (sc.imageUrl.isNullOrEmpty()) { Glide.with(context).clear(holder.image) } else { - if (media != null) Glide.with(context) - .load(EmbeddedChapterImage.getModelFor(media!!, position)) - .apply(RequestOptions() - .dontAnimate() - .transform(FitCenter(), RoundedCorners((4 * context.resources.displayMetrics.density).toInt()))) - .into(holder.image) + if (media != null) { + val imgUrl = EmbeddedChapterImage.getModelFor(media!!,position) + if (imgUrl != null) Glide.with(context) + .load(imgUrl) + .apply(RequestOptions() + .dontAnimate() + .transform(FitCenter(), RoundedCorners((4 * context.resources.displayMetrics.density).toInt()))) + .into(holder.image) + } } } else { holder.image.visibility = View.GONE diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/CoverLoader.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/CoverLoader.kt index face01d1..1a3516f6 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/CoverLoader.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/CoverLoader.kt @@ -77,7 +77,7 @@ class CoverLoader(activity: MainActivity) { .load(uri) .apply(options) - if (fallbackUri != null) { + if (!fallbackUri.isNullOrBlank()) { builder = builder.error(Glide.with(imgvCover!!) .`as`(Drawable::class.java) .load(fallbackUri) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/FeedDiscoverAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/FeedDiscoverAdapter.kt index 32a2586e..756cde52 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/FeedDiscoverAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/FeedDiscoverAdapter.kt @@ -53,7 +53,7 @@ class FeedDiscoverAdapter(mainActivity: MainActivity) : BaseAdapter() { val podcast: PodcastSearchResult? = getItem(position) holder.imageView!!.contentDescription = podcast?.title - Glide.with(mainActivityRef.get()!!) + if (!podcast?.imageUrl.isNullOrBlank()) Glide.with(mainActivityRef.get()!!) .load(podcast?.imageUrl) .apply(RequestOptions() .placeholder(R.color.light_gray) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/HorizontalFeedListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/HorizontalFeedListAdapter.kt index 671d4601..ec9dc93b 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/HorizontalFeedListAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/HorizontalFeedListAdapter.kt @@ -77,7 +77,7 @@ open class HorizontalFeedListAdapter(mainActivity: MainActivity) : false } - Glide.with(mainActivityRef.get()!!) + if (!podcast.imageUrl.isNullOrBlank()) Glide.with(mainActivityRef.get()!!) .load(podcast.imageUrl) .apply(RequestOptions() .placeholder(R.color.light_gray) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/NavListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/NavListAdapter.kt index 9391979a..625cb43d 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/NavListAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/NavListAdapter.kt @@ -287,7 +287,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) : val feed = drawerItem.feed val context = activity.get() ?: return - Glide.with(context) + if (!feed.imageUrl.isNullOrBlank()) Glide.with(context) .load(feed.imageUrl) .apply(RequestOptions() .placeholder(R.color.light_gray) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/OnlineFeedsAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/OnlineFeedsAdapter.kt index 499b3866..68ddd603 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/OnlineFeedsAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/OnlineFeedsAdapter.kt @@ -64,7 +64,7 @@ class OnlineFeedsAdapter(private val context: Context, objects: List(private val cont val binding = SimpleIconListItemBinding.bind(view!!) binding.title.text = item.title binding.subtitle.text = item.subtitle - Glide.with(context) + if (item.imageUrl.isNotBlank()) Glide.with(context) .load(item.imageUrl) .apply(RequestOptions() .diskCacheStrategy(DiskCacheStrategy.NONE) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/PlayActionButton.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/PlayActionButton.kt index ee6c1a49..f2b75406 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/PlayActionButton.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/PlayActionButton.kt @@ -8,6 +8,7 @@ import ac.mdiq.podcini.storage.DBTasks import ac.mdiq.podcini.playback.PlaybackServiceStarter import ac.mdiq.podcini.storage.model.feed.FeedItem import ac.mdiq.podcini.storage.model.playback.MediaType +import android.util.Log class PlayActionButton(item: FeedItem) : ItemActionButton(item) { override fun getLabel(): Int { @@ -18,6 +19,7 @@ class PlayActionButton(item: FeedItem) : ItemActionButton(item) { } @UnstableApi override fun onClick(context: Context) { val media = item.media ?: return + Log.d("PlayActionButton", "onClick called") if (!media.fileExists()) { DBTasks.notifyMissingFeedMediaFile(context, media) return diff --git a/app/src/main/java/ac/mdiq/podcini/ui/dialog/SwipeActionsDialog.kt b/app/src/main/java/ac/mdiq/podcini/ui/dialog/SwipeActionsDialog.kt index b262960c..5d9c3682 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/dialog/SwipeActionsDialog.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/dialog/SwipeActionsDialog.kt @@ -121,8 +121,7 @@ class SwipeActionsDialog(private val context: Context, private val tag: String) for (i in keys.indices) { val action = keys[i] - val item = SwipeactionsPickerItemBinding.inflate(LayoutInflater.from( - context)) + val item = SwipeactionsPickerItemBinding.inflate(LayoutInflater.from(context)) item.swipeActionLabel.text = action.getTitle(context) val icon = DrawableCompat.wrap(AppCompatResources.getDrawable(context, action.getActionIcon())!!) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/BaseEpisodesListFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/BaseEpisodesListFragment.kt index c3a9ebca..dcaa6e4e 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/BaseEpisodesListFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/BaseEpisodesListFragment.kt @@ -71,6 +71,8 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect lateinit var listAdapter: EpisodeItemListAdapter protected lateinit var txtvInformation: TextView + private var currentPlaying: EpisodeItemViewHolder? = null + @JvmField var episodes: MutableList = ArrayList() @@ -327,7 +329,7 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect @Subscribe(threadMode = ThreadMode.MAIN) 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) { val pos: Int = FeedItemUtil.indexOfItemWithId(episodes, item.id) if (pos >= 0) { @@ -344,11 +346,19 @@ abstract class BaseEpisodesListFragment : Fragment(), SelectableAdapter.OnSelect @UnstableApi @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(event: PlaybackPositionEvent) { - for (i in 0 until listAdapter.itemCount) { - val holder: EpisodeItemViewHolder? = recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder - if (holder != null && holder.isCurrentlyPlayingItem) { - holder.notifyPlaybackPositionUpdated(event) - break +// Log.d(TAG, "onEventMainThread() called with PlaybackPositionEvent event = [$event]") + if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem) + currentPlaying!!.notifyPlaybackPositionUpdated(event) + else { + 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 + } } } } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/CompletedDownloadsFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/CompletedDownloadsFragment.kt index f75ea3dd..7f450242 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/CompletedDownloadsFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/CompletedDownloadsFragment.kt @@ -70,7 +70,7 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis private var disposable: Disposable? = null private var displayUpArrow = false - + private var currentPlaying: EpisodeItemViewHolder? = null @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -272,12 +272,19 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis @UnstableApi @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(event: PlaybackPositionEvent) { -// if (event == null) return - for (i in 0 until adapter.itemCount) { - val holder: EpisodeItemViewHolder? = recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder - if (holder != null && holder.isCurrentlyPlayingItem) { - holder.notifyPlaybackPositionUpdated(event) - break +// Log.d(TAG, "onEventMainThread() called with PlaybackPositionEvent event = [$event]") + if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem) + currentPlaying!!.notifyPlaybackPositionUpdated(event) + else { + Log.d(TAG, "onEventMainThread() search list") + 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() diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt index 37f1086f..b60e89e1 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt @@ -35,6 +35,7 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import android.widget.Button import android.widget.ImageView import android.widget.ProgressBar import android.widget.TextView @@ -189,7 +190,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { val balloon: Balloon = Balloon.Builder(requireContext()) .setArrowOrientation(ArrowOrientation.TOP) .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) .setMarginLeft(8) .setMarginRight(8) @@ -199,10 +200,10 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { .setDismissWhenTouchOutside(true) .setLifecycleOwner(this) .build() - val binding_ = PopupBubbleViewBinding.bind(balloon.getContentView()) - val positiveButton = binding_.balloonButtonPositive - val negativeButton = binding_.balloonButtonNegative - val message: TextView = binding_.balloonMessage + val ballonView = balloon.getContentView() + val positiveButton = ballonView.findViewById(R.id.balloon_button_positive) as Button + val negativeButton = ballonView.findViewById(R.id.balloon_button_negative) as Button + val message: TextView = ballonView.findViewById(R.id.balloon_message) as TextView message.setText(if (offerStreaming) R.string.on_demand_config_stream_text else R.string.on_demand_config_download_text) positiveButton.setOnClickListener { @@ -297,13 +298,15 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { RoundedCorners((8 * resources.displayMetrics.density).toInt())) .dontAnimate() - Glide.with(this) - .load(item!!.imageLocation) - .error(Glide.with(this) - .load(ImageResourceUtils.getFallbackImageLocation(item!!)) - .apply(options)) - .apply(options) - .into(imgvCover) + val imgLocFB = ImageResourceUtils.getFallbackImageLocation(item!!) + if (!item!!.imageLocation.isNullOrBlank() || !imgLocFB.isNullOrBlank()) + Glide.with(this) + .load(item!!.imageLocation) + .error(Glide.with(this) + .load(imgLocFB) + .apply(options)) + .apply(options) + .into(imgvCover) updateButtons() } @@ -398,15 +401,9 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { @UnstableApi @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) fun onEventMainThread(event: EpisodeDownloadEvent) { - if (item == null || item!!.media == null) { - return - } - if (!event.urls.contains(item!!.media!!.download_url)) { - return - } - if (itemsLoaded && activity != null) { - updateButtons() - } + if (item == null || item!!.media == null) return + if (!event.urls.contains(item!!.media!!.download_url)) return + if (itemsLoaded && activity != null) updateButtons() } @UnstableApi @Subscribe(threadMode = ThreadMode.MAIN) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodesListFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodesListFragment.kt index 1f5d9ff9..138c8c32 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodesListFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodesListFragment.kt @@ -1,15 +1,9 @@ package ac.mdiq.podcini.ui.fragment 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.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.ItemSortDialog -import ac.mdiq.podcini.util.event.FeedListUpdateEvent import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -18,8 +12,6 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.OptIn import androidx.media3.common.util.UnstableApi -import org.apache.commons.lang3.StringUtils -import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import kotlin.math.min @@ -49,6 +41,7 @@ class EpisodesListFragment : BaseEpisodesListFragment() { } override fun loadData(): List { + if (episodeList.isEmpty()) return listOf() return episodeList.subList(0, min(episodeList.size-1, page * EPISODES_PER_PAGE)) } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/ExternalPlayerFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/ExternalPlayerFragment.kt index d736a276..64657f60 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/ExternalPlayerFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/ExternalPlayerFragment.kt @@ -76,6 +76,8 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener { private var showTimeLeft = false + private var currentMedia: Playable? = null + private var controller: PlaybackController? = null private var disposable: Disposable? = null @@ -192,6 +194,7 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener { } override fun loadMediaInfo() { + Log.d(TAG, "setupPlaybackController loadMediaInfo called") this@ExternalPlayerFragment.loadMediaInfo() } @@ -284,19 +287,24 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener { @UnstableApi private fun loadMediaInfo() { - Log.d(TAG, "Loading media info") + Log.d(TAG, "loadMediaInfo called") if (controller == null) { Log.w(TAG, "loadMediaInfo was called while PlaybackController was null!") return } - - disposable?.dispose() - disposable = Maybe.fromCallable { controller?.getMedia() } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ media: Playable? -> this.updateUi(media) }, - { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) }, - { (activity as MainActivity).setPlayerVisible(false) }) + val theMedia = controller?.getMedia() + if (currentMedia == null || theMedia?.getIdentifier() != currentMedia?.getIdentifier()) { + Log.d(TAG, "reloading media info") + disposable?.dispose() + disposable = Maybe.fromCallable { theMedia } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .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) { @@ -354,9 +362,8 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener { @UnstableApi private fun updateUi(media: Playable?) { - if (media == null) { - return - } + if (media == null) return + episodeTitle.text = media.getEpisodeTitle() (activity as MainActivity).setPlayerVisible(true) onPositionObserverUpdate(PlaybackPositionEvent(media.getPosition(), media.getDuration())) @@ -367,13 +374,19 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener { .fitCenter() .dontAnimate() - Glide.with(this) - .load(getEpisodeListImageLocation(media)) - .error(Glide.with(this) - .load(getFallbackImageLocation(media)) - .apply(options)) - .apply(options) - .into(imgvCover) + val imgLoc = getEpisodeListImageLocation(media) + val imgLocFB = getFallbackImageLocation(media) + if (!imgLoc.isNullOrBlank()) + Glide.with(this) + .load(imgLoc) + .apply(options) + .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) { (activity as MainActivity).bottomSheet.setLocked(true) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt index ed0c11d6..4071a601 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt @@ -186,22 +186,24 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { Log.d(TAG, "Language is " + feed!!.language) Log.d(TAG, "Author is " + feed!!.author) Log.d(TAG, "URL is " + feed!!.download_url) - 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) + 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) + } txtvTitle.text = feed!!.title txtvTitle.setMaxLines(6) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedItemlistFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedItemlistFragment.kt index b0f94c89..15d1551b 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedItemlistFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedItemlistFragment.kt @@ -77,7 +77,9 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba private lateinit var adapter: FeedItemListAdapter private lateinit var swipeActions: SwipeActions private lateinit var nextPageLoader: MoreContentListFooterUtil - + + private var currentPlaying: EpisodeItemViewHolder? = null + private var displayUpArrow = false private var headerCreated = false private var feedID: Long = 0 @@ -314,7 +316,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba @Subscribe(threadMode = ThreadMode.MAIN) 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()) { return } @@ -334,7 +336,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) 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()) { return } @@ -348,13 +350,19 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba @UnstableApi @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(event: PlaybackPositionEvent) { - Log.d(TAG, "onEventMainThread() called with: event = [$event]") - for (i in 0 until adapter.itemCount) { - val holder: EpisodeItemViewHolder? = - binding.recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder - if (holder != null && holder.isCurrentlyPlayingItem) { - holder.notifyPlaybackPositionUpdated(event) - break +// Log.d(TAG, "onEventMainThread() called with PlaybackPositionEvent event = [$event]") + if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem) + currentPlaying!!.notifyPlaybackPositionUpdated(event) + else { + Log.d(TAG, "onEventMainThread() search list") + for (i in 0 until adapter.itemCount) { + 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() { - if (feed == null) return - 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) + if (!feed?.imageUrl.isNullOrBlank()) { + 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) - Glide.with(this) - .load(feed!!.imageUrl) - .apply(RequestOptions() - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .fitCenter() - .dontAnimate()) - .into(binding.header.imgvCover) + Glide.with(this) + .load(feed!!.imageUrl) + .apply(RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .fitCenter() + .dontAnimate()) + .into(binding.header.imgvCover) + } } @UnstableApi private fun loadItems() { @@ -657,7 +666,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba } 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 KEY_UP_ARROW = "up_arrow" diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt index 1b0c5046..65fbb5d4 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt @@ -98,8 +98,7 @@ class OnlineSearchFragment : Fragment() { firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int - ) { - } + ) {} }) return binding.root } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/PlayerDetailsFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/PlayerDetailsFragment.kt index ea2450f6..58ff544c 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/PlayerDetailsFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/PlayerDetailsFragment.kt @@ -279,8 +279,9 @@ class PlayerDetailsFragment : Fragment() { if (displayedChapterIndex == -1 || media!!.getChapters().isEmpty() || media!!.getChapters()[displayedChapterIndex].imageUrl.isNullOrEmpty()) { cover.into(binding.imgvCover) } else { - Glide.with(this) - .load(EmbeddedChapterImage.getModelFor(media!!, displayedChapterIndex)) + val imgLoc = EmbeddedChapterImage.getModelFor(media!!, displayedChapterIndex) + if (imgLoc != null) Glide.with(this) + .load(imgLoc) .apply(options) .thumbnail(cover) .error(cover) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/QueueFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/QueueFragment.kt index 69c97edd..011be583 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/QueueFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/QueueFragment.kt @@ -81,6 +81,8 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda private var queue: MutableList = mutableListOf() private var recyclerAdapter: QueueRecyclerAdapter? = null + private var currentPlaying: EpisodeItemViewHolder? = null + private var disposable: Disposable? = null override fun onCreate(savedInstanceState: Bundle?) { @@ -205,7 +207,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(event: QueueEvent) { - Log.d(TAG, "onEventMainThread() called with: event = [$event]") + Log.d(TAG, "onEventMainThread() called with QueueEvent event = [$event]") if (recyclerAdapter == null) { loadItems(true) return @@ -242,7 +244,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(event: FeedItemEvent) { - Log.d(TAG, "onEventMainThread() called with: event = [$event]") + Log.d(TAG, "onEventMainThread() called with FeedItemEvent event = [$event]") if (recyclerAdapter == null) { loadItems(true) return @@ -264,6 +266,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]") for (downloadUrl in event.urls) { val pos: Int = FeedItemUtil.indexOfItemWithDownloadUrl(queue.toList(), downloadUrl) if (pos >= 0) { @@ -274,12 +277,20 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda @UnstableApi @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(event: PlaybackPositionEvent) { +// Log.d(TAG, "onEventMainThread() called with PlaybackPositionEvent event = [$event]") if (recyclerAdapter != null) { - for (i in 0 until recyclerAdapter!!.itemCount) { - val holder: EpisodeItemViewHolder? = recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder - if (holder != null && holder.isCurrentlyPlayingItem) { - holder.notifyPlaybackPositionUpdated(event) - break + if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem) + currentPlaying!!.notifyPlaybackPositionUpdated(event) + else { + Log.d(TAG, "onEventMainThread() search list") + 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) fun onPlayerStatusChanged(event: PlayerStatusEvent?) { + Log.d(TAG, "onPlayerStatusChanged() called with event = [$event]") loadItems(false) refreshToolbarState() } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/SearchFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/SearchFragment.kt index 132024f0..f11f8fae 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/SearchFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/SearchFragment.kt @@ -70,6 +70,7 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener { private lateinit var automaticSearchDebouncer: Handler private var results: MutableList = mutableListOf() + private var currentPlaying: EpisodeItemViewHolder? = null private var disposable: Disposable? = null private var lastQueryChange: Long = 0 @@ -291,12 +292,18 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener { @UnstableApi @Subscribe(threadMode = ThreadMode.MAIN) fun onEventMainThread(event: PlaybackPositionEvent) { - for (i in 0 until adapter.itemCount) { - val holder: EpisodeItemViewHolder? = - recyclerView.findViewHolderForAdapterPosition(i) as? EpisodeItemViewHolder - if (holder != null && holder.isCurrentlyPlayingItem) { - holder.notifyPlaybackPositionUpdated(event) - break + if (currentPlaying != null && currentPlaying!!.isCurrentlyPlayingItem) + currentPlaying!!.notifyPlaybackPositionUpdated(event) + else { + Log.d(FeedItemlistFragment.TAG, "onEventMainThread() search list") + 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 + } } } } diff --git a/app/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsListAdapter.kt index 9254e67a..89ed60c2 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsListAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsListAdapter.kt @@ -51,7 +51,7 @@ abstract class StatisticsListAdapter protected constructor(@JvmField protected v } else { val holder = h as StatisticsHolder val statsItem = statisticsData!![position - 1] - Glide.with(context) + if (!statsItem.feed.imageUrl.isNullOrBlank()) Glide.with(context) .load(statsItem.feed.imageUrl) .apply(RequestOptions() .placeholder(R.color.light_gray) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt b/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt index 7b90185d..e04bb404 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt @@ -120,12 +120,14 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou } if (coverHolder.visibility == View.VISIBLE) { - CoverLoader(activity) - .withUri(ImageResourceUtils.getEpisodeListImageLocation(item)) + val imgLoc = ImageResourceUtils.getEpisodeListImageLocation(item) + if (!imgLoc.isNullOrBlank()) CoverLoader(activity) + .withUri(imgLoc) .withFallbackUri(item.feed?.imageUrl) .withPlaceholderView(placeholder) .withCoverView(cover) .load() + else cover.setImageResource(R.mipmap.ic_launcher) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1b4afc49..1f836b3a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -187,6 +187,7 @@ Filtered {fa-exclamation-circle} Last refresh failed. Tap to view details. Open podcast + Open Please wait until the data is loaded Updates disabled diff --git a/changelog.md b/changelog.md index 3a44b2d3..48447a72 100644 --- a/changelog.md +++ b/changelog.md @@ -155,4 +155,13 @@ * 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 * bug fixes on passing Glide with null addresses -* null safety enhancements in code \ No newline at end of file +* 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 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/3020117.txt b/fastlane/metadata/android/en-US/changelogs/3020117.txt new file mode 100644 index 00000000..824105d3 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/3020117.txt @@ -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