4.3.4 commit

This commit is contained in:
Xilin Jia 2024-03-25 15:18:29 +00:00
parent c5bf432283
commit cc4cdb281a
45 changed files with 286 additions and 207 deletions

View File

@ -149,8 +149,8 @@ android {
// Version code schema (not used):
// "1.2.3-beta4" -> 1020304
// "1.2.3" -> 1020395
versionCode 3020114
versionName "4.3.3"
versionCode 3020115
versionName "4.3.4"
def commit = ""
try {

View File

@ -62,12 +62,12 @@ class HttpDownloaderTest {
private fun download(url: String?, title: String, expectedResult: Boolean, deleteExisting: Boolean = true,
username: String? = null, password: String? = null
): ac.mdiq.podcini.service.download.Downloader {
): Downloader {
val feedFile: FeedFile = setupFeedFile(url, title, deleteExisting)
val request = DownloadRequest(
feedFile.getFile_url()!!, url!!, title, 0, feedFile.getTypeAsInt(),
username, password, null, false)
val downloader: ac.mdiq.podcini.service.download.Downloader = HttpDownloader(request)
val downloader: Downloader = HttpDownloader(request)
downloader.call()
val status = downloader.result
Assert.assertNotNull(status)
@ -101,7 +101,7 @@ class HttpDownloaderTest {
fun testCancel() {
val url = httpServer!!.baseUrl + "/delay/3"
val feedFile = setupFeedFile(url, "delay", true)
val downloader: ac.mdiq.podcini.service.download.Downloader = HttpDownloader(DownloadRequest(
val downloader: Downloader = HttpDownloader(DownloadRequest(
feedFile.getFile_url()!!, url, "delay", 0,
feedFile.getTypeAsInt(), null, null, null, false))
val t: Thread = object : Thread() {

View File

@ -1,5 +1,6 @@
package ac.mdiq.podcini.feed.parser.namespace
import ac.mdiq.podcini.feed.parser.HandlerState
import android.util.Log
import ac.mdiq.podcini.storage.model.feed.FeedFunding
import ac.mdiq.podcini.storage.model.feed.FeedItem
@ -13,7 +14,7 @@ import ac.mdiq.podcini.feed.parser.util.SyndStringUtils.trimAllWhitespace
import org.xml.sax.Attributes
class Atom : Namespace() {
override fun handleElementStart(localName: String, state: ac.mdiq.podcini.feed.parser.HandlerState, attributes: Attributes): SyndElement {
override fun handleElementStart(localName: String, state: HandlerState, attributes: Attributes): SyndElement {
if (ENTRY == localName) {
state.currentItem = FeedItem()
state.items.add(state.currentItem!!)
@ -88,7 +89,7 @@ class Atom : Namespace() {
return SyndElement(localName, this)
}
override fun handleElementEnd(localName: String, state: ac.mdiq.podcini.feed.parser.HandlerState) {
override fun handleElementEnd(localName: String, state: HandlerState) {
if (ENTRY == localName) {
if (state.currentItem != null &&
state.tempObjects.containsKey(Itunes.DURATION)) {

View File

@ -1,5 +1,6 @@
package ac.mdiq.podcini.feed.parser.namespace
import ac.mdiq.podcini.feed.parser.HandlerState
import android.util.Log
import androidx.core.text.HtmlCompat
import ac.mdiq.podcini.feed.parser.element.SyndElement
@ -7,7 +8,7 @@ import ac.mdiq.podcini.feed.parser.util.DurationParser.inMillis
import org.xml.sax.Attributes
class Itunes : Namespace() {
override fun handleElementStart(localName: String, state: ac.mdiq.podcini.feed.parser.HandlerState,
override fun handleElementStart(localName: String, state: HandlerState,
attributes: Attributes): SyndElement {
if (IMAGE == localName) {
val url: String? = attributes.getValue(IMAGE_HREF)
@ -25,7 +26,7 @@ class Itunes : Namespace() {
return SyndElement(localName, this)
}
override fun handleElementEnd(localName: String, state: ac.mdiq.podcini.feed.parser.HandlerState) {
override fun handleElementEnd(localName: String, state: HandlerState) {
if (state.contentBuf == null) {
return
}

View File

@ -1,5 +1,6 @@
package ac.mdiq.podcini.feed.parser.namespace
import ac.mdiq.podcini.feed.parser.HandlerState
import android.util.Log
import ac.mdiq.podcini.storage.model.feed.Chapter
import ac.mdiq.podcini.feed.parser.element.SyndElement
@ -7,7 +8,7 @@ import ac.mdiq.podcini.feed.parser.util.DateUtils.parseTimeString
import org.xml.sax.Attributes
class SimpleChapters : Namespace() {
override fun handleElementStart(localName: String, state: ac.mdiq.podcini.feed.parser.HandlerState, attributes: Attributes): SyndElement {
override fun handleElementStart(localName: String, state: HandlerState, attributes: Attributes): SyndElement {
val currentItem = state.currentItem
if (currentItem != null) {
if (localName == CHAPTERS) {
@ -29,7 +30,7 @@ class SimpleChapters : Namespace() {
return SyndElement(localName, this)
}
override fun handleElementEnd(localName: String, state: ac.mdiq.podcini.feed.parser.HandlerState) {
override fun handleElementEnd(localName: String, state: HandlerState) {
}
companion object {

View File

@ -267,17 +267,20 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
fun playPause() {
if (media == null) return
if (playbackService == null) {
PlaybackServiceStarter(activity, media!!).start()
// PlaybackServiceStarter(activity, media!!).start()
PlaybackServiceStarter(activity, media!!)
.callEvenIfRunning(true)
.start()
Log.w(TAG, "Play/Pause button was pressed, but playbackservice was null!")
return
// return
}
when (status) {
PlayerStatus.PLAYING -> playbackService!!.pause(true, false)
PlayerStatus.PAUSED, PlayerStatus.PREPARED -> playbackService!!.resume()
PlayerStatus.PLAYING -> playbackService?.pause(true, false)
PlayerStatus.PAUSED, PlayerStatus.PREPARED -> playbackService?.resume()
PlayerStatus.PREPARING -> playbackService!!.isStartWhenPrepared = !playbackService!!.isStartWhenPrepared
PlayerStatus.INITIALIZED -> {
playbackService!!.isStartWhenPrepared = true
playbackService!!.prepare()
if (playbackService != null) playbackService!!.isStartWhenPrepared = true
playbackService?.prepare()
}
else -> {
PlaybackServiceStarter(activity, media!!)

View File

@ -1,29 +1,28 @@
package ac.mdiq.podcini.preferences
import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.ThemePreferenceBinding
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.cardview.widget.CardView
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.google.android.material.elevation.SurfaceColors
import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.ThemePreferenceBinding
class ThemePreference : Preference {
var viewBinding: ThemePreferenceBinding? = null
var binding: ThemePreferenceBinding? = null
constructor(context: Context) : super(context!!) {
constructor(context: Context) : super(context) {
layoutResource = R.layout.theme_preference
}
constructor(context: Context, attrs: AttributeSet?) : super(context!!, attrs) {
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
layoutResource = R.layout.theme_preference
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
viewBinding = ThemePreferenceBinding.bind(holder.itemView)
binding = ThemePreferenceBinding.bind(holder.itemView)
updateUi()
}
@ -33,7 +32,7 @@ class ThemePreference : Preference {
val surfaceColorActive = SurfaceColors.getColorForElevation(context, 32 * density)
val activeTheme = UserPreferences.theme
card.setCardBackgroundColor(if (theme == activeTheme) surfaceColorActive else surfaceColor)
card.setOnClickListener { v: View? ->
card.setOnClickListener {
UserPreferences.theme = theme
if (onPreferenceChangeListener != null) {
onPreferenceChangeListener!!.onPreferenceChange(this, UserPreferences.theme)
@ -43,8 +42,8 @@ class ThemePreference : Preference {
}
fun updateUi() {
updateThemeCard(viewBinding!!.themeSystemCard, UserPreferences.ThemePreference.SYSTEM)
updateThemeCard(viewBinding!!.themeLightCard, UserPreferences.ThemePreference.LIGHT)
updateThemeCard(viewBinding!!.themeDarkCard, UserPreferences.ThemePreference.DARK)
updateThemeCard(binding!!.themeSystemCard, UserPreferences.ThemePreference.SYSTEM)
updateThemeCard(binding!!.themeLightCard, UserPreferences.ThemePreference.LIGHT)
updateThemeCard(binding!!.themeDarkCard, UserPreferences.ThemePreference.DARK)
}
}

View File

@ -1,5 +1,6 @@
package ac.mdiq.podcini.service.download.handler
import ac.mdiq.podcini.feed.parser.FeedHandlerResult
import android.util.Log
import ac.mdiq.podcini.util.InvalidFeedException
import ac.mdiq.podcini.storage.model.download.DownloadError
@ -16,7 +17,7 @@ import java.util.*
import java.util.concurrent.Callable
import javax.xml.parsers.ParserConfigurationException
class FeedParserTask(private val request: DownloadRequest) : Callable<ac.mdiq.podcini.feed.parser.FeedHandlerResult?> {
class FeedParserTask(private val request: DownloadRequest) : Callable<FeedHandlerResult?> {
var downloadStatus: DownloadResult
private set
var isSuccessful: Boolean = true
@ -29,7 +30,7 @@ class FeedParserTask(private val request: DownloadRequest) : Callable<ac.mdiq.po
"Unknown error: Status not set")
}
override fun call(): ac.mdiq.podcini.feed.parser.FeedHandlerResult? {
override fun call(): FeedHandlerResult? {
val feed = Feed(request.source, request.lastModified)
feed.file_url = request.destination
feed.id = request.feedfileId
@ -42,7 +43,7 @@ class FeedParserTask(private val request: DownloadRequest) : Callable<ac.mdiq.po
var reasonDetailed: String? = null
val feedHandler = ac.mdiq.podcini.feed.parser.FeedHandler()
var result: ac.mdiq.podcini.feed.parser.FeedHandlerResult? = null
var result: FeedHandlerResult? = null
try {
result = feedHandler.parseFeed(feed)
Log.d(TAG, feed.title + " parsed")

View File

@ -25,6 +25,7 @@ import ac.mdiq.podcini.playback.base.PlaybackServiceMediaPlayer
import ac.mdiq.podcini.playback.base.PlayerStatus
import ac.mdiq.podcini.playback.base.RewindAfterPauseUtils
import ac.mdiq.podcini.preferences.UserPreferences
import ac.mdiq.podcini.util.event.PlayerErrorEvent
import org.greenrobot.eventbus.EventBus
import java.io.File
import java.io.IOException
@ -197,11 +198,11 @@ class LocalPSMP(context: Context, callback: PSMPCallback) : PlaybackServiceMedia
} catch (e: IOException) {
e.printStackTrace()
setPlayerStatus(PlayerStatus.ERROR, null)
EventBus.getDefault().postSticky(ac.mdiq.podcini.util.event.PlayerErrorEvent(e.localizedMessage ?: ""))
EventBus.getDefault().postSticky(PlayerErrorEvent(e.localizedMessage ?: ""))
} catch (e: IllegalStateException) {
e.printStackTrace()
setPlayerStatus(PlayerStatus.ERROR, null)
EventBus.getDefault().postSticky(ac.mdiq.podcini.util.event.PlayerErrorEvent(e.localizedMessage ?: ""))
EventBus.getDefault().postSticky(PlayerErrorEvent(e.localizedMessage ?: ""))
}
}
@ -727,7 +728,7 @@ class LocalPSMP(context: Context, callback: PSMPCallback) : PlaybackServiceMedia
}
})
mp.setOnErrorListener(Consumer { message: String ->
EventBus.getDefault().postSticky(ac.mdiq.podcini.util.event.PlayerErrorEvent(message))
EventBus.getDefault().postSticky(PlayerErrorEvent(message))
})
}

View File

@ -71,6 +71,8 @@ import ac.mdiq.podcini.util.FeedUtil.shouldAutoDeleteItemsOnThatFeed
import ac.mdiq.podcini.util.IntentUtils.sendLocalBroadcast
import ac.mdiq.podcini.util.NetworkUtils.isStreamingAllowed
import ac.mdiq.podcini.util.event.MessageEvent
import ac.mdiq.podcini.util.event.PlayerErrorEvent
import ac.mdiq.podcini.util.event.settings.SkipIntroEndingChangedEvent
import ac.mdiq.podcini.util.event.settings.SpeedPresetChangedEvent
import ac.mdiq.podcini.util.event.settings.VolumeAdaptionChangedEvent
import android.Manifest
@ -924,7 +926,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
@Subscribe(threadMode = ThreadMode.MAIN)
@Suppress("unused")
fun playerError(event: ac.mdiq.podcini.util.event.PlayerErrorEvent?) {
fun playerError(event: PlayerErrorEvent?) {
if (mediaPlayer?.playerStatus == PlayerStatus.PLAYING) {
mediaPlayer!!.pause(true, false)
}
@ -1548,7 +1550,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
@Subscribe(threadMode = ThreadMode.MAIN)
@Suppress("unused")
fun skipIntroEndingPresetChanged(event: ac.mdiq.podcini.util.event.settings.SkipIntroEndingChangedEvent) {
fun skipIntroEndingPresetChanged(event: SkipIntroEndingChangedEvent) {
if (playable is FeedMedia) {
if ((playable as FeedMedia).item?.feed?.id == event.feedId) {
if (event.skipEnding != 0) {

View File

@ -310,7 +310,7 @@ object DBReader {
*/
@JvmStatic
fun getEpisodes(offset: Int, limit: Int, filter: FeedItemFilter?, sortOrder: SortOrder?): List<FeedItem> {
Log.d(TAG, "getRecentlyPublishedEpisodes() called with: offset=$offset, limit=$limit")
Log.d(TAG, "getEpisodes called with: offset=$offset, limit=$limit")
val adapter = getInstance()
adapter.open()
try {

View File

@ -8,6 +8,8 @@ object FeedItemSortQuery {
fun generateFrom(sortOrder: SortOrder?): String {
var sortQuery = ""
sortQuery = when (sortOrder) {
SortOrder.FEED_TITLE_A_Z -> PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_FEED + " " + "ASC"
SortOrder.FEED_TITLE_Z_A -> PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_FEED + " " + "DESC"
SortOrder.EPISODE_TITLE_A_Z -> PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_TITLE + " " + "ASC"
SortOrder.EPISODE_TITLE_Z_A -> PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_TITLE + " " + "DESC"
SortOrder.DATE_OLD_NEW -> PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_PUBDATE + " " + "ASC"

View File

@ -2,6 +2,7 @@ package ac.mdiq.podcini.storage.model.feed
import android.text.TextUtils
import ac.mdiq.podcini.storage.model.feed.FeedFunding.Companion.extractPaymentLinks
import android.util.Log
import java.util.*
import kotlin.collections.ArrayList
@ -107,10 +108,15 @@ class Feed : FeedFile {
*/
var sortOrder: SortOrder? = null
set(sortOrder) {
require(!(sortOrder != null && sortOrder.scope != SortOrder.Scope.INTRA_FEED)) {
("The specified sortOrder " + sortOrder
if (!(sortOrder != null && sortOrder.scope != SortOrder.Scope.INTRA_FEED)) {
Log.w("Feed sortOrder", "The specified sortOrder " + sortOrder
+ " is invalid. Only those with INTRA_FEED scope are allowed.")
}
// This looks suicidal:
// require(!(sortOrder != null && sortOrder.scope != SortOrder.Scope.INTRA_FEED)) {
// ("The specified sortOrder " + sortOrder
// + " is invalid. Only those with INTRA_FEED scope are allowed.")
// }
field = sortOrder
}

View File

@ -286,10 +286,6 @@ class MainActivity : CastEnabledActivity() {
}
override fun onSlide(view: View, slideOffset: Float) {
val audioPlayer = supportFragmentManager.findFragmentByTag(AudioPlayerFragment.TAG) as? AudioPlayerFragment ?: return
// if (slideOffset == 0.0f) { //STATE_COLLAPSED
// audioPlayer.scrollToPage(AudioPlayerFragment.FIRST_PAGE)
// }
audioPlayer.fadePlayerToToolbar(slideOffset)
}
}

View File

@ -30,6 +30,7 @@ import ac.mdiq.podcini.preferences.UserPreferences.setShowRemainTimeSetting
import ac.mdiq.podcini.preferences.UserPreferences.shouldShowRemainingTime
import ac.mdiq.podcini.ui.appstartintent.MainActivityStarter
import ac.mdiq.podcini.util.event.MessageEvent
import ac.mdiq.podcini.util.event.PlayerErrorEvent
import android.content.DialogInterface
import android.content.Intent
import android.graphics.PixelFormat
@ -489,7 +490,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMediaPlayerError(event: ac.mdiq.podcini.util.event.PlayerErrorEvent) {
fun onMediaPlayerError(event: PlayerErrorEvent) {
MediaPlayerErrorDialog.show(this, event)
}

View File

@ -20,6 +20,7 @@ import ac.mdiq.podcini.util.Converter.getDurationStringLocalized
import ac.mdiq.podcini.util.Converter.getDurationStringLong
import ac.mdiq.podcini.util.IntentUtils.openInBrowser
import ac.mdiq.podcini.storage.model.feed.Chapter
import ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage
import ac.mdiq.podcini.storage.model.playback.Playable
import ac.mdiq.podcini.ui.common.CircularProgressBar
import kotlin.math.max
@ -89,7 +90,7 @@ class ChaptersListAdapter(private val context: Context, private val callback: Ca
Glide.with(context).clear(holder.image)
} else {
if (media != null) Glide.with(context)
.load(ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage.getModelFor(media!!, position))
.load(EmbeddedChapterImage.getModelFor(media!!, position))
.apply(RequestOptions()
.dontAnimate()
.transform(FitCenter(), RoundedCorners((4 * context.resources.displayMetrics.density).toInt())))

View File

@ -15,8 +15,9 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
/**
* Displays a dialog with a text box for filtering episodes and two radio buttons for exclusion/inclusion
*/
abstract class EpisodeFilterDialog(context: Context, filter: FeedFilter) : MaterialAlertDialogBuilder(
context) {
abstract class EpisodeFilterDialog(context: Context, filter: FeedFilter) :
MaterialAlertDialogBuilder(context) {
private val viewBinding = EpisodeFilterDialogBinding.inflate(LayoutInflater.from(context))
private val termList: MutableList<String>

View File

@ -15,7 +15,11 @@ import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.SortDialogBinding
import ac.mdiq.podcini.databinding.SortDialogItemActiveBinding
import ac.mdiq.podcini.databinding.SortDialogItemBinding
import ac.mdiq.podcini.preferences.UserPreferences
import ac.mdiq.podcini.storage.model.feed.SortOrder
import android.graphics.Color
import android.util.Log
import android.view.WindowManager
open class ItemSortDialog : BottomSheetDialogFragment() {
protected var _binding: SortDialogBinding? = null
@ -30,6 +34,11 @@ open class ItemSortDialog : BottomSheetDialogFragment() {
return binding.root
}
override fun onStart() {
super.onStart()
dialog?.window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
}
private fun populateList() {
binding.gridLayout.removeAllViews()
onAddItem(R.string.episode_title, SortOrder.EPISODE_TITLE_A_Z, SortOrder.EPISODE_TITLE_Z_A, true)

View File

@ -36,6 +36,9 @@ import org.greenrobot.eventbus.ThreadMode
@UnstableApi
class ChaptersFragment : AppCompatDialogFragment() {
private var _binding: SimpleListFragmentBinding? = null
private val binding get() = _binding!!
private lateinit var layoutManager: LinearLayoutManager
private lateinit var progressBar: ProgressBar
private lateinit var adapter: ChaptersListAdapter
@ -63,12 +66,12 @@ class ChaptersFragment : AppCompatDialogFragment() {
}
fun onCreateView(inflater: LayoutInflater): View {
val viewBinding = SimpleListFragmentBinding.inflate(inflater)
viewBinding.toolbar.visibility = View.GONE
_binding = SimpleListFragmentBinding.inflate(inflater)
binding.toolbar.visibility = View.GONE
Log.d(TAG, "fragment onCreateView")
val recyclerView = viewBinding.recyclerView
progressBar = viewBinding.progLoading
val recyclerView = binding.recyclerView
progressBar = binding.progLoading
layoutManager = LinearLayoutManager(activity)
recyclerView.layoutManager = layoutManager
recyclerView.addItemDecoration(DividerItemDecoration(recyclerView.context, layoutManager.orientation))
@ -100,11 +103,12 @@ class ChaptersFragment : AppCompatDialogFragment() {
EventBus.getDefault().register(this)
loadMediaInfo(false)
return viewBinding.root
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
controller?.release()
controller = null
EventBus.getDefault().unregister(this)

View File

@ -25,9 +25,7 @@ import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
import ac.mdiq.podcini.util.FeedItemUtil
import ac.mdiq.podcini.util.event.EpisodeDownloadEvent
import ac.mdiq.podcini.util.event.FeedItemEvent
import ac.mdiq.podcini.util.event.SwipeActionsChangedEvent
import ac.mdiq.podcini.util.event.*
import android.os.Bundle
import android.util.Log
import android.view.*
@ -55,12 +53,12 @@ import java.util.*
* Displays all completed downloads and provides a button to delete them.
*/
class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeListener, Toolbar.OnMenuItemClickListener {
private var runningDownloads: Set<String>? = HashSet()
private var items: MutableList<FeedItem> = mutableListOf()
private var _binding: SimpleListFragmentBinding? = null
private val binding get() = _binding!!
private var runningDownloads: Set<String> = HashSet()
private var items: MutableList<FeedItem> = mutableListOf()
private lateinit var infoBar: TextView
private lateinit var adapter: CompletedDownloadsListAdapter
private lateinit var toolbar: MaterialToolbar
@ -105,12 +103,12 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
swipeActions = SwipeActions(this, TAG).attachTo(recyclerView)
swipeActions.setFilter(FeedItemFilter(FeedItemFilter.DOWNLOADED))
refreshSwipeTelltale()
binding.leftActionIcon.setOnClickListener({
binding.leftActionIcon.setOnClickListener {
swipeActions.showDialog()
})
binding.rightActionIcon.setOnClickListener({
}
binding.rightActionIcon.setOnClickListener {
swipeActions.showDialog()
})
}
val animator: RecyclerView.ItemAnimator? = recyclerView.itemAnimator
if (animator is SimpleItemAnimator) {
@ -127,8 +125,8 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
speedDialView.overlayLayout = multiSelectDial.fabSDOverlay
speedDialView.inflate(R.menu.episodes_apply_action_speeddial)
speedDialView.removeActionItemById(R.id.download_batch)
speedDialView.removeActionItemById(R.id.mark_read_batch)
speedDialView.removeActionItemById(R.id.mark_unread_batch)
// speedDialView.removeActionItemById(R.id.mark_read_batch)
// speedDialView.removeActionItemById(R.id.mark_unread_batch)
speedDialView.removeActionItemById(R.id.remove_from_queue_batch)
speedDialView.setOnChangeListener(object : SpeedDialView.OnChangeListener {
override fun onMainActionSelected(): Boolean {
@ -286,17 +284,17 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onPlayerStatusChanged(event: ac.mdiq.podcini.util.event.PlayerStatusEvent?) {
fun onPlayerStatusChanged(event: PlayerStatusEvent?) {
loadItems()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onDownloadLogChanged(event: ac.mdiq.podcini.util.event.DownloadLogEvent?) {
fun onDownloadLogChanged(event: DownloadLogEvent?) {
loadItems()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onUnreadItemsChanged(event: ac.mdiq.podcini.util.event.UnreadItemsUpdateEvent?) {
fun onUnreadItemsChanged(event: UnreadItemsUpdateEvent?) {
loadItems()
}
@ -324,10 +322,10 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
FeedItemFilter(FeedItemFilter.DOWNLOADED), sortOrder)
val mediaUrls: MutableList<String> = ArrayList()
if (runningDownloads == null) {
if (runningDownloads.isEmpty()) {
return@fromCallable downloadedItems
}
for (url in runningDownloads!!) {
for (url in runningDownloads) {
if (FeedItemUtil.indexOfItemWithDownloadUrl(downloadedItems, url) != -1) {
continue // Already in list
}
@ -409,7 +407,11 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
descending: SortOrder,
ascendingIsDefault: Boolean
) {
if (ascending == SortOrder.DATE_OLD_NEW || ascending == SortOrder.DURATION_SHORT_LONG || ascending == SortOrder.EPISODE_TITLE_A_Z || ascending == SortOrder.SIZE_SMALL_LARGE) {
if (ascending == SortOrder.DATE_OLD_NEW ||
ascending == SortOrder.DURATION_SHORT_LONG ||
ascending == SortOrder.EPISODE_TITLE_A_Z ||
ascending == SortOrder.SIZE_SMALL_LARGE ||
ascending == SortOrder.FEED_TITLE_A_Z) {
super.onAddItem(title, ascending, descending, ascendingIsDefault)
}
}
@ -417,7 +419,7 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
override fun onSelectionChanged() {
super.onSelectionChanged()
UserPreferences.downloadsSortedOrder = sortOrder
EventBus.getDefault().post(ac.mdiq.podcini.util.event.DownloadLogEvent.listUpdated())
EventBus.getDefault().post(DownloadLogEvent.listUpdated())
}
}

View File

@ -39,6 +39,9 @@ import java.util.*
* Searches iTunes store for top podcasts and displays results in a list.
*/
class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private var _binding: FragmentItunesSearchBinding? = null
private val binding get() = _binding!!
private lateinit var prefs: SharedPreferences
private lateinit var gridView: GridView
private lateinit var progressBar: ProgressBar
@ -94,15 +97,15 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
// Inflate the layout for this fragment
val viewBinding = FragmentItunesSearchBinding.inflate(inflater)
_binding = FragmentItunesSearchBinding.inflate(inflater)
// val root = inflater.inflate(R.layout.fragment_itunes_search, container, false)
Log.d(TAG, "fragment onCreateView")
gridView = viewBinding.gridView
gridView = binding.gridView
adapter = OnlineFeedsAdapter(requireActivity(), ArrayList())
gridView.setAdapter(adapter)
toolbar = viewBinding.toolbar
toolbar = binding.toolbar
toolbar.setNavigationOnClickListener { parentFragmentManager.popBackStack() }
toolbar.inflateMenu(R.menu.countries_menu)
val discoverHideItem = toolbar.menu.findItem(R.id.discover_hide_item)
@ -121,17 +124,18 @@ class DiscoveryFragment : Fragment(), Toolbar.OnMenuItemClickListener {
startActivity(intent)
}
progressBar = viewBinding.progressBar
txtvError = viewBinding.txtvError
butRetry = viewBinding.butRetry
txtvEmpty = viewBinding.empty
progressBar = binding.progressBar
txtvError = binding.txtvError
butRetry = binding.butRetry
txtvEmpty = binding.empty
loadToplist(countryCode)
return viewBinding.root
return binding.root
}
override fun onDestroy() {
super.onDestroy()
_binding = null
disposable?.dispose()
adapter = null

View File

@ -23,6 +23,7 @@ import ac.mdiq.podcini.util.DateFormatter
import ac.mdiq.podcini.util.PlaybackStatus
import ac.mdiq.podcini.util.event.EpisodeDownloadEvent
import ac.mdiq.podcini.util.event.FeedItemEvent
import ac.mdiq.podcini.util.event.PlayerStatusEvent
import ac.mdiq.podcini.util.event.UnreadItemsUpdateEvent
import android.os.Build
import android.os.Bundle
@ -409,7 +410,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
@UnstableApi @Subscribe(threadMode = ThreadMode.MAIN)
fun onPlayerStatusChanged(event: ac.mdiq.podcini.util.event.PlayerStatusEvent?) {
fun onPlayerStatusChanged(event: PlayerStatusEvent?) {
updateButtons()
}

View File

@ -105,12 +105,12 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
swipeActions = SwipeActions(this, getFragmentTag()).attachTo(recyclerView)
swipeActions.setFilter(getFilter())
refreshSwipeTelltale()
binding.leftActionIcon.setOnClickListener({
binding.leftActionIcon.setOnClickListener {
swipeActions.showDialog()
})
binding.rightActionIcon.setOnClickListener({
}
binding.rightActionIcon.setOnClickListener {
swipeActions.showDialog()
})
}
val animator: RecyclerView.ItemAnimator? = recyclerView.itemAnimator
if (animator is SimpleItemAnimator) {

View File

@ -54,6 +54,9 @@ import kotlin.math.max
* Fragment which is supposed to be displayed outside of the MediaplayerActivity.
*/
class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
private var _binding: ExternalPlayerFragmentBinding? = null
private val binding get() = _binding!!
private lateinit var imgvCover: ImageView
private lateinit var butPlay: PlayButton
@ -79,22 +82,22 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
@UnstableApi
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
val viewBinding = ExternalPlayerFragmentBinding.inflate(inflater)
_binding = ExternalPlayerFragmentBinding.inflate(inflater)
Log.d(TAG, "fragment onCreateView")
episodeTitle = viewBinding.titleView
butPlaybackSpeed = viewBinding.butPlaybackSpeed
txtvPlaybackSpeed = viewBinding.txtvPlaybackSpeed
imgvCover = viewBinding.imgvCover
butPlay = viewBinding.butPlay
butRev = viewBinding.butRev
txtvRev = viewBinding.txtvRev
butFF = viewBinding.butFF
txtvFF = viewBinding.txtvFF
butSkip = viewBinding.butSkip
sbPosition = viewBinding.sbPosition
txtvPosition = viewBinding.txtvPosition
txtvLength = viewBinding.txtvLength
episodeTitle = binding.titleView
butPlaybackSpeed = binding.butPlaybackSpeed
txtvPlaybackSpeed = binding.txtvPlaybackSpeed
imgvCover = binding.imgvCover
butPlay = binding.butPlay
butRev = binding.butRev
txtvRev = binding.txtvRev
butFF = binding.butFF
txtvFF = binding.txtvFF
butSkip = binding.butSkip
sbPosition = binding.sbPosition
txtvPosition = binding.txtvPosition
txtvLength = binding.txtvLength
setupLengthTextView()
setupControlButtons()
@ -103,7 +106,7 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
}
sbPosition.setOnSeekBarChangeListener(this)
viewBinding.externalPlayerFragment.setOnClickListener {
binding.externalPlayerFragment.setOnClickListener {
Log.d(TAG, "externalPlayerFragment was clicked")
val media = controller?.getMedia()
if (media != null) {
@ -118,13 +121,14 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
controller = setupPlaybackController()
controller!!.init()
loadMediaInfo()
// loadMediaInfo()
EventBus.getDefault().register(this)
return viewBinding.root
return binding.root
}
@OptIn(UnstableApi::class) override fun onDestroyView() {
super.onDestroyView()
_binding = null
controller?.release()
controller = null
EventBus.getDefault().unregister(this)
@ -134,11 +138,9 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
butPlay.setOnClickListener {
if (controller == null) {
return@setOnClickListener
}
val media = controller!!.getMedia()
if (controller == null) return@setOnClickListener
val media = controller!!.getMedia()
if (media?.getMediaType() == MediaType.VIDEO && controller!!.status != PlayerStatus.PLAYING) {
controller!!.playPause()
requireContext().startActivity(getPlayerActivityIntent(requireContext(), media))
@ -161,10 +163,10 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
SkipPreferenceDialog.SkipDirection.SKIP_REWIND, txtvRev)
true
}
butPlay.setOnClickListener {
controller?.init()
controller?.playPause()
}
// butPlay.setOnClickListener {
// controller?.init()
// controller?.playPause()
// }
butFF.setOnClickListener {
if (controller != null) {
val curr: Int = controller!!.position

View File

@ -71,10 +71,11 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
private var _binding: FeedItemListFragmentBinding? = null
private val binding get() = _binding!!
private var _speedDialBinding: MultiSelectSpeedDialBinding? = null
private val speedDialBinding get() = _speedDialBinding!!
private lateinit var adapter: FeedItemListAdapter
private lateinit var swipeActions: SwipeActions
private lateinit var speedDialBinding: MultiSelectSpeedDialBinding
private lateinit var nextPageLoader: MoreContentListFooterUtil
private var displayUpArrow = false
@ -96,7 +97,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
Log.d(TAG, "fragment onCreateView")
_binding = FeedItemListFragmentBinding.inflate(inflater)
speedDialBinding = MultiSelectSpeedDialBinding.bind(binding.root)
_speedDialBinding = MultiSelectSpeedDialBinding.bind(binding.root)
binding.toolbar.inflateMenu(R.menu.feedlist)
binding.toolbar.setOnMenuItemClickListener(this)
@ -198,6 +199,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
override fun onDestroyView() {
super.onDestroyView()
_binding = null
_speedDialBinding
EventBus.getDefault().unregister(this)
disposable?.dispose()
adapter.endSelectMode()
@ -209,9 +211,8 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
}
private fun updateToolbar() {
if (feed == null) {
return
}
if (feed == null) return
binding.toolbar.menu.findItem(R.id.visit_website_item).setVisible(feed!!.link != null)
binding.toolbar.menu.findItem(R.id.refresh_complete_item).setVisible(feed!!.isPaged)
if (StringUtils.isBlank(feed!!.link)) {
@ -620,8 +621,11 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
descending: SortOrder,
ascendingIsDefault: Boolean
) {
if (ascending == SortOrder.DATE_OLD_NEW || ascending == SortOrder.DURATION_SHORT_LONG || ascending == SortOrder.EPISODE_TITLE_A_Z || (requireArguments().getBoolean(
ARG_FEED_IS_LOCAL) && ascending == SortOrder.EPISODE_FILENAME_A_Z)) {
if (ascending == SortOrder.DATE_OLD_NEW ||
ascending == SortOrder.DURATION_SHORT_LONG ||
ascending == SortOrder.RANDOM ||
ascending == SortOrder.EPISODE_TITLE_A_Z ||
(requireArguments().getBoolean(ARG_FEED_IS_LOCAL) && ascending == SortOrder.EPISODE_FILENAME_A_Z)) {
super.onAddItem(title, ascending, descending, ascendingIsDefault)
}
}

View File

@ -16,6 +16,8 @@ import ac.mdiq.podcini.ui.dialog.*
import ac.mdiq.podcini.ui.menuhandler.MenuItemUtils
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
import ac.mdiq.podcini.util.event.FeedListUpdateEvent
import ac.mdiq.podcini.util.event.QueueEvent
import ac.mdiq.podcini.util.event.UnreadItemsUpdateEvent
import android.R.attr
import android.app.Activity
import android.content.Context
@ -195,7 +197,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onUnreadItemsChanged(event: ac.mdiq.podcini.util.event.UnreadItemsUpdateEvent?) {
fun onUnreadItemsChanged(event: UnreadItemsUpdateEvent?) {
loadData()
}
@ -206,7 +208,7 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onQueueChanged(event: ac.mdiq.podcini.util.event.QueueEvent) {
fun onQueueChanged(event: QueueEvent) {
Log.d(TAG, "onQueueChanged($event)")
// we are only interested in the number of queue items, not download status or position
if (event.action == ac.mdiq.podcini.util.event.QueueEvent.Action.DELETED_MEDIA || event.action == ac.mdiq.podcini.util.event.QueueEvent.Action.SORTED || event.action == ac.mdiq.podcini.util.event.QueueEvent.Action.MOVED) {

View File

@ -7,6 +7,7 @@ import ac.mdiq.podcini.playback.PlaybackController
import ac.mdiq.podcini.playback.event.PlaybackPositionEvent
import ac.mdiq.podcini.storage.DBReader
import ac.mdiq.podcini.storage.model.feed.Chapter
import ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage
import ac.mdiq.podcini.storage.model.feed.FeedMedia
import ac.mdiq.podcini.storage.model.playback.Playable
import ac.mdiq.podcini.ui.activity.MainActivity
@ -279,7 +280,7 @@ class PlayerDetailsFragment : Fragment() {
cover.into(binding.imgvCover)
} else {
Glide.with(this)
.load(ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage.getModelFor(media!!, displayedChapterIndex))
.load(EmbeddedChapterImage.getModelFor(media!!, displayedChapterIndex))
.apply(options)
.thumbnail(cover)
.error(cover)

View File

@ -1,6 +1,34 @@
package ac.mdiq.podcini.ui.fragment
import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.CheckboxDoNotShowAgainBinding
import ac.mdiq.podcini.databinding.MultiSelectSpeedDialBinding
import ac.mdiq.podcini.databinding.QueueFragmentBinding
import ac.mdiq.podcini.feed.util.PlaybackSpeedUtils
import ac.mdiq.podcini.net.download.FeedUpdateManager
import ac.mdiq.podcini.playback.event.PlaybackPositionEvent
import ac.mdiq.podcini.preferences.UserPreferences
import ac.mdiq.podcini.storage.DBReader
import ac.mdiq.podcini.storage.DBWriter
import ac.mdiq.podcini.storage.model.feed.FeedItem
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
import ac.mdiq.podcini.storage.model.feed.SortOrder
import ac.mdiq.podcini.ui.activity.MainActivity
import ac.mdiq.podcini.ui.adapter.QueueRecyclerAdapter
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
import ac.mdiq.podcini.ui.dialog.ItemSortDialog
import ac.mdiq.podcini.ui.fragment.actions.EpisodeMultiSelectActionHandler
import ac.mdiq.podcini.ui.fragment.swipeactions.SwipeActions
import ac.mdiq.podcini.ui.menuhandler.FeedItemMenuHandler
import ac.mdiq.podcini.ui.menuhandler.MenuItemUtils
import ac.mdiq.podcini.ui.view.EmptyViewHandler
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
import ac.mdiq.podcini.util.Converter
import ac.mdiq.podcini.util.FeedItemUtil
import ac.mdiq.podcini.util.event.*
import android.content.Context
import android.content.DialogInterface
import android.content.SharedPreferences
@ -22,35 +50,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.leinardi.android.speeddial.SpeedDialActionItem
import com.leinardi.android.speeddial.SpeedDialView
import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.CheckboxDoNotShowAgainBinding
import ac.mdiq.podcini.databinding.MultiSelectSpeedDialBinding
import ac.mdiq.podcini.databinding.QueueFragmentBinding
import ac.mdiq.podcini.ui.adapter.QueueRecyclerAdapter
import ac.mdiq.podcini.ui.adapter.SelectableAdapter
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
import ac.mdiq.podcini.feed.util.PlaybackSpeedUtils
import ac.mdiq.podcini.ui.menuhandler.MenuItemUtils
import ac.mdiq.podcini.storage.DBReader
import ac.mdiq.podcini.storage.DBWriter
import ac.mdiq.podcini.util.FeedItemUtil
import ac.mdiq.podcini.net.download.FeedUpdateManager
import ac.mdiq.podcini.ui.dialog.ItemSortDialog
import ac.mdiq.podcini.util.event.*
import ac.mdiq.podcini.playback.event.PlaybackPositionEvent
import ac.mdiq.podcini.ui.fragment.actions.EpisodeMultiSelectActionHandler
import ac.mdiq.podcini.ui.fragment.swipeactions.SwipeActions
import ac.mdiq.podcini.ui.menuhandler.FeedItemMenuHandler
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.preferences.UserPreferences
import ac.mdiq.podcini.ui.dialog.SwipeActionsDialog
import ac.mdiq.podcini.ui.view.EmptyViewHandler
import ac.mdiq.podcini.ui.view.EpisodeItemListRecyclerView
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
import ac.mdiq.podcini.ui.view.viewholder.EpisodeItemViewHolder
import ac.mdiq.podcini.util.Converter
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
@ -126,12 +125,12 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
swipeActions.setFilter(FeedItemFilter(FeedItemFilter.QUEUED))
swipeActions.attachTo(recyclerView)
refreshSwipeTelltale()
binding.leftActionIcon.setOnClickListener({
binding.leftActionIcon.setOnClickListener {
swipeActions.showDialog()
})
binding.rightActionIcon.setOnClickListener({
}
binding.rightActionIcon.setOnClickListener {
swipeActions.showDialog()
})
}
recyclerAdapter = object : QueueRecyclerAdapter(activity as MainActivity, swipeActions) {
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {

View File

@ -33,6 +33,9 @@ import org.greenrobot.eventbus.ThreadMode
import java.util.*
class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
private var _binding: QuickFeedDiscoveryBinding? = null
private val binding get() = _binding!!
private var disposable: Disposable? = null
private lateinit var adapter: FeedDiscoverAdapter
@ -44,18 +47,17 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
@OptIn(UnstableApi::class) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
val viewBinding = QuickFeedDiscoveryBinding.inflate(inflater)
// val root: View = inflater.inflate(R.layout.quick_feed_discovery, container, false)
_binding = QuickFeedDiscoveryBinding.inflate(inflater)
Log.d(TAG, "fragment onCreateView")
val discoverMore = viewBinding.discoverMore
val discoverMore = binding.discoverMore
discoverMore.setOnClickListener { (activity as MainActivity).loadChildFragment(DiscoveryFragment()) }
discoverGridLayout = viewBinding.discoverGrid
errorView = viewBinding.discoverError
errorTextView = viewBinding.discoverErrorTxtV
errorRetry = viewBinding.discoverErrorRetryBtn
poweredByTextView = viewBinding.discoverPoweredByItunes
discoverGridLayout = binding.discoverGrid
errorView = binding.discoverError
errorTextView = binding.discoverErrorTxtV
errorRetry = binding.discoverErrorRetryBtn
poweredByTextView = binding.discoverPoweredByItunes
adapter = FeedDiscoverAdapter(activity as MainActivity)
discoverGridLayout.setAdapter(adapter)
@ -80,11 +82,12 @@ class QuickFeedDiscoveryFragment : Fragment(), AdapterView.OnItemClickListener {
loadToplist()
EventBus.getDefault().register(this)
return viewBinding.root
return binding.root
}
override fun onDestroy() {
super.onDestroy()
_binding = null
EventBus.getDefault().unregister(this)
disposable?.dispose()
}

View File

@ -56,6 +56,9 @@ import org.greenrobot.eventbus.ThreadMode
* Performs a search operation on all feeds or one specific feed and displays the search result.
*/
class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
private var _binding: SearchFragmentBinding? = null
private val binding get() = _binding!!
private lateinit var adapter: EpisodeItemListAdapter
private lateinit var adapterFeeds: HorizontalFeedListAdapter
private lateinit var progressBar: ProgressBar
@ -87,14 +90,13 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val viewBinding = SearchFragmentBinding.inflate(inflater)
// val layout: View = inflater.inflate(R.layout.search_fragment, container, false)
_binding = SearchFragmentBinding.inflate(inflater)
Log.d(TAG, "fragment onCreateView")
setupToolbar(viewBinding.toolbar)
speedDialBinding = MultiSelectSpeedDialBinding.bind(viewBinding.root)
progressBar = viewBinding.progressBar
recyclerView = viewBinding.recyclerView
setupToolbar(binding.toolbar)
speedDialBinding = MultiSelectSpeedDialBinding.bind(binding.root)
progressBar = binding.progressBar
recyclerView = binding.recyclerView
recyclerView.setRecycledViewPool((activity as MainActivity).recycledViewPool)
registerForContextMenu(recyclerView)
adapter = object : EpisodeItemListAdapter(activity as MainActivity) {
@ -109,9 +111,9 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
}
adapter.setOnSelectModeListener(this)
recyclerView.adapter = adapter
recyclerView.addOnScrollListener(LiftOnScrollListener(viewBinding.appbar))
recyclerView.addOnScrollListener(LiftOnScrollListener(binding.appbar))
val recyclerViewFeeds = viewBinding.recyclerViewFeeds
val recyclerViewFeeds = binding.recyclerViewFeeds
val layoutManagerFeeds = LinearLayoutManager(activity)
layoutManagerFeeds.orientation = RecyclerView.HORIZONTAL
recyclerViewFeeds.layoutManager = layoutManagerFeeds
@ -133,7 +135,7 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
emptyViewHandler.setMessage(R.string.type_to_search)
EventBus.getDefault().register(this)
chip = viewBinding.feedTitleChip
chip = binding.feedTitleChip
chip.setOnCloseIconClickListener {
requireArguments().putLong(ARG_FEED, 0)
searchWithProgressBar()
@ -178,11 +180,12 @@ class SearchFragment : Fragment(), SelectableAdapter.OnSelectModeListener {
true
}
return viewBinding.root
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
EventBus.getDefault().unregister(this)
}

View File

@ -38,7 +38,7 @@ class ApGlideModule : AppGlideModule() {
registry.append(String::class.java, InputStream::class.java, ApOkHttpUrlLoader.Factory())
registry.append(String::class.java, InputStream::class.java, NoHttpStringLoader.StreamFactory())
registry.append(ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage::class.java, ByteBuffer::class.java, ChapterImageModelLoader.Factory())
registry.append(EmbeddedChapterImage::class.java, ByteBuffer::class.java, ChapterImageModelLoader.Factory())
}
companion object {

View File

@ -1,6 +1,7 @@
package ac.mdiq.podcini.ui.glide
import ac.mdiq.podcini.service.download.PodciniHttpClient.getHttpClient
import ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage
import com.bumptech.glide.Priority
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.Options
@ -17,9 +18,9 @@ import java.io.FileInputStream
import java.io.IOException
import java.nio.ByteBuffer
class ChapterImageModelLoader : ModelLoader<ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage?, ByteBuffer?> {
class Factory : ModelLoaderFactory<ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage?, ByteBuffer?> {
override fun build(unused: MultiModelLoaderFactory): ModelLoader<ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage?, ByteBuffer?> {
class ChapterImageModelLoader : ModelLoader<EmbeddedChapterImage?, ByteBuffer?> {
class Factory : ModelLoaderFactory<EmbeddedChapterImage?, ByteBuffer?> {
override fun build(unused: MultiModelLoaderFactory): ModelLoader<EmbeddedChapterImage?, ByteBuffer?> {
return ChapterImageModelLoader()
}
@ -28,7 +29,7 @@ class ChapterImageModelLoader : ModelLoader<ac.mdiq.podcini.storage.model.feed.E
}
}
override fun buildLoadData(model: ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage,
override fun buildLoadData(model: EmbeddedChapterImage,
width: Int,
height: Int,
options: Options
@ -36,11 +37,11 @@ class ChapterImageModelLoader : ModelLoader<ac.mdiq.podcini.storage.model.feed.E
return ModelLoader.LoadData(ObjectKey(model), EmbeddedImageFetcher(model))
}
override fun handles(model: ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage): Boolean {
override fun handles(model: EmbeddedChapterImage): Boolean {
return true
}
internal class EmbeddedImageFetcher(private val image: ac.mdiq.podcini.storage.model.feed.EmbeddedChapterImage) : DataFetcher<ByteBuffer?> {
internal class EmbeddedImageFetcher(private val image: EmbeddedChapterImage) : DataFetcher<ByteBuffer?> {
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in ByteBuffer?>) {
var stream: BufferedInputStream? = null
try {

View File

@ -10,6 +10,7 @@ import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
import ac.mdiq.podcini.ui.statistics.downloads.DownloadStatisticsFragment
import ac.mdiq.podcini.ui.statistics.subscriptions.SubscriptionStatisticsFragment
import ac.mdiq.podcini.ui.statistics.years.YearsStatisticsFragment
import ac.mdiq.podcini.util.event.StatisticsEvent
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
@ -106,7 +107,7 @@ class StatisticsFragment : PagedToolbarFragment() {
val disposable = Completable.fromFuture(DBWriter.resetStatistics())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ EventBus.getDefault().post(ac.mdiq.podcini.util.event.StatisticsEvent()) },
.subscribe({ EventBus.getDefault().post(StatisticsEvent()) },
{ error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
}

View File

@ -17,19 +17,21 @@ import io.reactivex.schedulers.Schedulers
import java.util.*
class FeedStatisticsFragment : Fragment() {
private var _binding: FeedStatisticsBinding? = null
private val binding get() = _binding!!
private var feedId: Long = 0
private var disposable: Disposable? = null
private var viewBinding: FeedStatisticsBinding? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
feedId = requireArguments().getLong(EXTRA_FEED_ID)
viewBinding = FeedStatisticsBinding.inflate(inflater)
_binding = FeedStatisticsBinding.inflate(inflater)
if (!requireArguments().getBoolean(EXTRA_DETAILED)) {
for (i in 0 until viewBinding!!.root.childCount) {
val child = viewBinding!!.root.getChildAt(i)
for (i in 0 until binding.root.childCount) {
val child = binding.root.getChildAt(i)
if ("detailed" == child.tag) {
child.visibility = View.GONE
}
@ -37,7 +39,7 @@ class FeedStatisticsFragment : Fragment() {
}
loadStatistics()
return viewBinding!!.root
return binding.root
}
private fun loadStatistics() {
@ -62,21 +64,20 @@ class FeedStatisticsFragment : Fragment() {
}
private fun showStats(s: StatisticsItem?) {
viewBinding!!.startedTotalLabel.text = String.format(Locale.getDefault(), "%d / %d",
binding.startedTotalLabel.text = String.format(Locale.getDefault(), "%d / %d",
s!!.episodesStarted, s.episodes)
viewBinding!!.timePlayedLabel.text =
binding.timePlayedLabel.text =
shortLocalizedDuration(requireContext(), s.timePlayed)
viewBinding!!.totalDurationLabel.text =
binding.totalDurationLabel.text =
shortLocalizedDuration(requireContext(), s.time)
viewBinding!!.onDeviceLabel.text = String.format(Locale.getDefault(), "%d", s.episodesDownloadCount)
viewBinding!!.spaceUsedLabel.text = Formatter.formatShortFileSize(context, s.totalDownloadSize)
binding.onDeviceLabel.text = String.format(Locale.getDefault(), "%d", s.episodesDownloadCount)
binding.spaceUsedLabel.text = Formatter.formatShortFileSize(context, s.totalDownloadSize)
}
override fun onDestroy() {
super.onDestroy()
if (disposable != null) {
disposable!!.dispose()
}
_binding = null
disposable?.dispose()
}
companion object {

View File

@ -7,6 +7,7 @@ import ac.mdiq.podcini.storage.DBReader
import ac.mdiq.podcini.storage.DBReader.StatisticsResult
import ac.mdiq.podcini.storage.StatisticsItem
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
import ac.mdiq.podcini.util.event.StatisticsEvent
import android.content.Context
import android.os.Bundle
import android.util.Log
@ -67,7 +68,7 @@ class SubscriptionStatisticsFragment : Fragment() {
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun statisticsEvent(event: ac.mdiq.podcini.util.event.StatisticsEvent?) {
fun statisticsEvent(event: StatisticsEvent?) {
refreshStatistics()
}

View File

@ -5,6 +5,7 @@ import ac.mdiq.podcini.R
import ac.mdiq.podcini.databinding.StatisticsFragmentBinding
import ac.mdiq.podcini.storage.DBReader
import ac.mdiq.podcini.storage.DBReader.MonthlyStatisticsItem
import ac.mdiq.podcini.util.event.StatisticsEvent
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
@ -59,7 +60,7 @@ class YearsStatisticsFragment : Fragment() {
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun statisticsEvent(event: ac.mdiq.podcini.util.event.StatisticsEvent?) {
fun statisticsEvent(event: StatisticsEvent?) {
refreshStatistics()
}

View File

@ -2,6 +2,7 @@ package ac.mdiq.podcini.util
import ac.mdiq.podcini.storage.model.feed.FeedItem
import ac.mdiq.podcini.storage.model.feed.SortOrder
import android.util.Log
import java.util.*
/**
@ -96,7 +97,8 @@ object FeedItemPermutors {
}
private fun feedTitle(item: FeedItem?): String {
return if (item?.feed != null && item.feed!!.title != null) item.feed!!.title!!.lowercase(Locale.getDefault()) else ""
Log.d("permutors", "feedTitle ${item?.feed?.title}")
return if (item?.feed?.title != null) item.feed!!.title!!.lowercase(Locale.getDefault()) else ""
}
/**

View File

@ -17,6 +17,7 @@ import ac.mdiq.podcini.net.ssl.SslProviderInstaller
import ac.mdiq.podcini.storage.database.PodDBAdapter
import ac.mdiq.podcini.preferences.UserPreferences
import ac.mdiq.podcini.preferences.UserPreferences.proxyConfig
import ac.mdiq.podcini.service.download.DownloadServiceInterfaceImpl
import java.io.File
@UnstableApi
@ -35,7 +36,7 @@ object ClientConfigurator {
SslProviderInstaller.install(context)
NetworkUtils.init(context)
NetworkConnectionChangeHandler.init(context)
DownloadServiceInterface.setImpl(ac.mdiq.podcini.service.download.DownloadServiceInterfaceImpl())
DownloadServiceInterface.setImpl(DownloadServiceInterfaceImpl())
SynchronizationQueueSink.setServiceStarterImpl { SyncService.sync(context) }
setCacheDirectory(File(context.cacheDir, "okhttp"))
setProxyConfig(proxyConfig)

View File

@ -4,9 +4,10 @@
<item
android:id="@+id/sort_items"
android:icon="@drawable/arrows_sort"
android:menuCategory="container"
android:title="@string/sort"
custom:showAsAction="never">
custom:showAsAction="always">
</item>
<item
android:id="@+id/refresh_item"
@ -31,7 +32,7 @@
android:id="@+id/visit_website_item"
android:icon="@drawable/ic_web"
android:menuCategory="container"
custom:showAsAction="collapseActionView"
custom:showAsAction="ifRoom|collapseActionView"
android:title="@string/visit_website_label"
android:visible="true">
</item>

View File

@ -58,8 +58,8 @@ class LocalFeedUpdaterTest {
init(context)
val app = context as Application?
ac.mdiq.podcini.util.config.ClientConfig.applicationCallbacks = Mockito.mock(ac.mdiq.podcini.util.config.ApplicationCallbacks::class.java)
Mockito.`when`(ac.mdiq.podcini.util.config.ClientConfig.applicationCallbacks?.getApplicationInstance()).thenReturn(app)
ClientConfig.applicationCallbacks = Mockito.mock(ApplicationCallbacks::class.java)
Mockito.`when`(ClientConfig.applicationCallbacks?.getApplicationInstance()).thenReturn(app)
DownloadServiceInterface.setImpl(DownloadServiceInterfaceStub())
// Initialize database

View File

@ -72,8 +72,8 @@ open class DbCleanupTests {
init(context)
val app = context as Application?
ac.mdiq.podcini.util.config.ClientConfig.applicationCallbacks = Mockito.mock(ac.mdiq.podcini.util.config.ApplicationCallbacks::class.java)
Mockito.`when`(ac.mdiq.podcini.util.config.ClientConfig.applicationCallbacks?.getApplicationInstance()).thenReturn(app)
ClientConfig.applicationCallbacks = Mockito.mock(ApplicationCallbacks::class.java)
Mockito.`when`(ClientConfig.applicationCallbacks?.getApplicationInstance()).thenReturn(app)
}
@After

View File

@ -40,8 +40,8 @@ class DbTasksTest {
init(context)
val app = context as Application?
ac.mdiq.podcini.util.config.ClientConfig.applicationCallbacks = Mockito.mock(ac.mdiq.podcini.util.config.ApplicationCallbacks::class.java)
Mockito.`when`(ac.mdiq.podcini.util.config.ClientConfig.applicationCallbacks?.getApplicationInstance()).thenReturn(app)
ClientConfig.applicationCallbacks = Mockito.mock(ApplicationCallbacks::class.java)
Mockito.`when`(ClientConfig.applicationCallbacks?.getApplicationInstance()).thenReturn(app)
// create new database
PodDBAdapter.init(context!!)

View File

@ -64,8 +64,8 @@ class DbWriterTest {
init(context)
val app = context as Application?
ac.mdiq.podcini.util.config.ClientConfig.applicationCallbacks = Mockito.mock(ac.mdiq.podcini.util.config.ApplicationCallbacks::class.java)
Mockito.`when`(ac.mdiq.podcini.util.config.ClientConfig.applicationCallbacks?.getApplicationInstance()).thenReturn(app)
ClientConfig.applicationCallbacks = Mockito.mock(ApplicationCallbacks::class.java)
Mockito.`when`(ClientConfig.applicationCallbacks?.getApplicationInstance()).thenReturn(app)
DownloadServiceInterface.setImpl(DownloadServiceInterfaceStub())
// create new database

View File

@ -136,4 +136,14 @@
* vertical swipe no longer collapses the expanded view
* only the down arrow on top left page collapses the expanded view
* share notes directly from expanded view of the player
* in episode info, changed rendering of description, removed nested scroll
* in episode info, changed rendering of description, removed nested scroll
# 4.3.4
* fixed bug player disappear on first play
* more viewbinding GC enhancements
* added sort by feed title in downloads view
* more items on action bar in feed item list view
* some cleaning of redundant qualifiers
* sort dialog no longer dims the main view
* added random sort to feed items view

View File

@ -0,0 +1,10 @@
Version 4.3.4 brings several changes:
* fixed bug player disappear on first play
* more viewbinding GC enhancements
* added sort by feed title in downloads view
* more items on action bar in feed item list view
* some cleaning of redundant qualifiers
* sort dialog no longer dims the main view
* added random sort to feed items view