6.3.5 commit

This commit is contained in:
Xilin Jia 2024-08-07 11:51:54 +01:00
parent 87af1a18f6
commit 50e9dffdb8
25 changed files with 757 additions and 652 deletions

View File

@ -31,8 +31,8 @@ android {
testApplicationId "ac.mdiq.podcini.tests"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionCode 3020228
versionName "6.3.4"
versionCode 3020229
versionName "6.3.5"
applicationId "ac.mdiq.podcini.R"
def commit = ""

View File

@ -85,7 +85,6 @@ class DownloadServiceInterfaceImpl : DownloadServiceInterface() {
try {
val workInfoList = future.get() // Wait for the completion of the future operation and retrieve the result
workInfoList.forEach { workInfo ->
// TODO: why cancel so many times??
if (workInfo.tags.contains(WORK_DATA_WAS_QUEUED)) {
val item_ = media.episodeOrFetch()
if (item_ != null) Queues.removeFromQueue(item_)

View File

@ -271,7 +271,7 @@ class PlaybackService : MediaSessionService() {
}
override fun onMediaChanged(reloadUI: Boolean) {
Logd(TAG, "reloadUI callback reached")
Logd(TAG, "onMediaChanged reloadUI callback reached")
if (reloadUI) sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0)
}

View File

@ -138,7 +138,7 @@ class MainPreferencesFragment : PreferenceFragmentCompat() {
.addBreadcrumb(getTitleOfPage(R.xml.preferences_autodownload))
config.index(R.xml.preferences_synchronization).addBreadcrumb(getTitleOfPage(R.xml.preferences_synchronization))
config.index(R.xml.preferences_notifications).addBreadcrumb(getTitleOfPage(R.xml.preferences_notifications))
config.index(R.xml.feed_settings).addBreadcrumb(getTitleOfPage(R.xml.feed_settings))
// config.index(R.xml.feed_settings).addBreadcrumb(getTitleOfPage(R.xml.feed_settings))
config.index(R.xml.preferences_swipe)
.addBreadcrumb(getTitleOfPage(R.xml.preferences_user_interface))
.addBreadcrumb(getTitleOfPage(R.xml.preferences_swipe))

View File

@ -1,8 +1,6 @@
package ac.mdiq.podcini.receiver
import ac.mdiq.podcini.playback.service.PlaybackService
import ac.mdiq.podcini.playback.service.PlaybackService.Companion
import ac.mdiq.podcini.util.Logd
import ac.mdiq.podcini.util.config.ClientConfigurator
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
@ -12,7 +10,6 @@ import android.util.Log
import android.view.KeyEvent
import androidx.core.content.ContextCompat
import androidx.media3.common.util.UnstableApi
import ac.mdiq.podcini.util.config.ClientConfigurator
/**
* Receives media button events.
@ -20,17 +17,29 @@ import ac.mdiq.podcini.util.config.ClientConfigurator
class MediaButtonReceiver : BroadcastReceiver() {
@UnstableApi
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "onReceive called with action: ${intent.action}")
if (intent.extras == null) return
Log.d(TAG, "onReceive Received intent: $intent")
Log.d(TAG, "onReceive Action: ${intent.action}")
val extras = intent.extras
Log.d(TAG, "onReceive Extras: $extras")
if (extras == null) return
val event = intent.extras!![Intent.EXTRA_KEY_EVENT] as? KeyEvent
if (event != null && event.action == KeyEvent.ACTION_DOWN && event.repeatCount == 0) {
Log.d(TAG, "onReceive Extras: ${extras.keySet()}")
for (key in extras.keySet()) {
Log.d(TAG, "onReceive Extra[$key] = ${extras[key]}")
}
// val event = extras.getParcelable(Intent.EXTRA_KEY_EVENT, KeyEvent::class.java)
val keyEvent: KeyEvent? = if (Build.VERSION.SDK_INT >= 33) extras.getParcelable(Intent.EXTRA_KEY_EVENT, KeyEvent::class.java)
else extras.getParcelable(Intent.EXTRA_KEY_EVENT) as KeyEvent?
Log.d(TAG, "onReceive keyEvent = $keyEvent" )
if (keyEvent != null && keyEvent.action == KeyEvent.ACTION_DOWN && keyEvent.repeatCount == 0) {
ClientConfigurator.initialize(context)
val serviceIntent = Intent(PLAYBACK_SERVICE_INTENT)
serviceIntent.setPackage(context.packageName)
serviceIntent.putExtra(EXTRA_KEYCODE, event.keyCode)
serviceIntent.putExtra(EXTRA_SOURCE, event.source)
serviceIntent.putExtra(EXTRA_HARDWAREBUTTON, event.eventTime > 0 || event.downTime > 0)
serviceIntent.putExtra(EXTRA_KEYCODE, keyEvent.keyCode)
serviceIntent.putExtra(EXTRA_SOURCE, keyEvent.source)
serviceIntent.putExtra(EXTRA_HARDWAREBUTTON, keyEvent.eventTime > 0 || keyEvent.downTime > 0)
try {
ContextCompat.startForegroundService(context, serviceIntent)
} catch (e: Exception) {

View File

@ -71,48 +71,7 @@ object AutoDownloads {
// likely not needed
@UnstableApi
open fun autoDownloadEpisodeMedia(context: Context, feeds: List<Feed>? = null): Runnable? {
return Runnable {
// // true if we should auto download based on network status
//// val networkShouldAutoDl = (isAutoDownloadAllowed)
// val networkShouldAutoDl = (isAutoDownloadAllowed && isEnableAutodownload)
// // true if we should auto download based on power status
// val powerShouldAutoDl = (deviceCharging(context) || isEnableAutodownloadOnBattery)
// Logd(TAG, "prepare autoDownloadUndownloadedItems $networkShouldAutoDl $powerShouldAutoDl")
// // we should only auto download if both network AND power are happy
// if (networkShouldAutoDl && powerShouldAutoDl) {
// Logd(TAG, "Performing auto-dl of undownloaded episodes")
// val queueItems = curQueue.episodes
// val newItems = getEpisodes(0, Int.MAX_VALUE, EpisodeFilter(EpisodeFilter.States.new.name), EpisodeSortOrder.DATE_NEW_OLD)
// Logd(TAG, "newItems: ${newItems.size}")
// val candidates: MutableList<Episode> = ArrayList(queueItems.size + newItems.size)
// candidates.addAll(queueItems)
// for (newItem in newItems) {
// val feedPrefs = newItem.feed!!.preferences
// if (feedPrefs!!.autoDownload && !candidates.contains(newItem) && feedPrefs.autoDownloadFilter!!.shouldAutoDownload(newItem)) candidates.add(newItem)
// }
// // filter items that are not auto downloadable
// val it = candidates.iterator()
// while (it.hasNext()) {
// val item = it.next()
// if (!item.isAutoDownloadEnabled || item.isDownloaded || item.media == null || isCurMedia(item.media) || item.feed?.isLocalFeed == true)
// it.remove()
// }
// val autoDownloadableEpisodes = candidates.size
// val downloadedEpisodes = getEpisodesCount(EpisodeFilter(EpisodeFilter.States.downloaded.name))
// val deletedEpisodes = AutoCleanups.build().makeRoomForEpisodes(context, autoDownloadableEpisodes)
// val cacheIsUnlimited = episodeCacheSize == UserPreferences.EPISODE_CACHE_SIZE_UNLIMITED
// val episodeCacheSize = episodeCacheSize
// val episodeSpaceLeft =
// if (cacheIsUnlimited || episodeCacheSize >= downloadedEpisodes + autoDownloadableEpisodes) autoDownloadableEpisodes
// else episodeCacheSize - (downloadedEpisodes - deletedEpisodes)
// val itemsToDownload: List<Episode> = candidates.subList(0, episodeSpaceLeft)
// if (itemsToDownload.isNotEmpty()) {
// Logd(TAG, "Enqueueing " + itemsToDownload.size + " items for download")
// for (episode in itemsToDownload) DownloadServiceInterface.get()?.download(context, episode)
// }
// }
// else Logd(TAG, "not auto downloaded networkShouldAutoDl: $networkShouldAutoDl powerShouldAutoDl $powerShouldAutoDl")
}
return Runnable {}
}
/**

View File

@ -9,6 +9,7 @@ import ac.mdiq.podcini.preferences.UserPreferences.isAutoDelete
import ac.mdiq.podcini.preferences.UserPreferences.isAutoDeleteLocal
import ac.mdiq.podcini.storage.database.Episodes.deleteEpisodes
import ac.mdiq.podcini.storage.database.LogsAndStats.addDownloadStatus
import ac.mdiq.podcini.storage.database.Queues.addToQueueSync
import ac.mdiq.podcini.storage.database.Queues.removeFromAllQueuesQuiet
import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope
@ -284,6 +285,10 @@ object Feeds {
if (pubDate == null || priorMostRecentDate == null || priorMostRecentDate.before(pubDate) || priorMostRecentDate == pubDate) {
Logd(TAG, "Marking episode published on $pubDate new, prior most recent date = $priorMostRecentDate")
episode.setNew()
if (savedFeed.preferences?.autoAddNewToQueue == true) {
val q = savedFeed.preferences?.queue
if (q != null) runOnIOScope { addToQueueSync(false, episode, q) }
}
}
}
}

View File

@ -133,18 +133,13 @@ object Queues {
if (queueModified) {
// TODO: handle sorting
applySortOrder(qItems, events)
// curQueue.episodes.clear()
// curQueue.episodes.addAll(qItems)
curQueue = upsert(curQueue) {
it.episodeIds.clear()
it.episodeIds.addAll(qItemIds)
it.update()
}
// curQueue.episodes.addAll(qItems)
for (event in events) EventFlow.postEvent(event)
// EventFlow.postEvent(FlowEvent.EpisodeEvent.updated(updatedItems))
if (markAsUnplayed && markAsUnplayeds.size > 0) setPlayState(Episode.PlayState.UNPLAYED.code, false, *markAsUnplayeds.toTypedArray())
// if (performAutoDownload) autodownloadEpisodeMedia(context)
}

View File

@ -18,7 +18,7 @@ import kotlin.coroutines.ContinuationInterceptor
object RealmDB {
private val TAG: String = RealmDB::class.simpleName ?: "Anonymous"
private const val SCHEMA_VERSION_NUMBER = 18L
private const val SCHEMA_VERSION_NUMBER = 19L
private val ioScope = CoroutineScope(Dispatchers.IO)

View File

@ -1,5 +1,6 @@
package ac.mdiq.podcini.storage.model
import ac.mdiq.podcini.R
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
import ac.mdiq.podcini.storage.database.RealmDB.realm
import ac.mdiq.podcini.storage.model.VolumeAdaptionSetting.Companion.fromInteger
@ -93,6 +94,8 @@ class FeedPreferences : EmbeddedRealmObject {
}
var queueId: Long = 0L
var autoAddNewToQueue: Boolean = false
@Ignore
var autoDownloadFilter: FeedAutoDownloadFilter? = null
get() = field ?: FeedAutoDownloadFilter(autoDLInclude, autoDLExclude, autoDLMinDuration, markExcludedPlayed)
@ -121,10 +124,10 @@ class FeedPreferences : EmbeddedRealmObject {
}
var autoDLPolicyCode: Int = 0
enum class AutoDLPolicy(val code: Int) {
ONLY_NEW(0),
NEWER(1),
OLDER(2);
enum class AutoDLPolicy(val code: Int, val resId: Int) {
ONLY_NEW(0, R.string.feed_auto_download_new),
NEWER(1, R.string.feed_auto_download_newer),
OLDER(2, R.string.feed_auto_download_older);
companion object {
fun fromCode(code: Int): AutoDLPolicy {
@ -190,5 +193,6 @@ class FeedPreferences : EmbeddedRealmObject {
const val TAG_SEPARATOR: String = "\u001e"
val FeedAutoDeleteOptions = AutoDeleteAction.values().map { it.tag }
}
}

View File

@ -1,12 +1,14 @@
package ac.mdiq.podcini.storage.model
enum class VolumeAdaptionSetting(private val value: Int, @JvmField val adaptionFactor: Float) {
OFF(0, 1.0f),
LIGHT_REDUCTION(1, 0.5f),
HEAVY_REDUCTION(2, 0.2f),
LIGHT_BOOST(3, 1.5f),
MEDIUM_BOOST(4, 2f),
HEAVY_BOOST(5, 2.5f);
import ac.mdiq.podcini.R
enum class VolumeAdaptionSetting(private val value: Int, @JvmField val adaptionFactor: Float, val resId: Int) {
OFF(0, 1.0f, R.string.feed_volume_reduction_off),
LIGHT_REDUCTION(1, 0.5f, R.string.feed_volume_reduction_light),
HEAVY_REDUCTION(2, 0.2f, R.string.feed_volume_reduction_heavy),
LIGHT_BOOST(3, 1.5f, R.string.feed_volume_boost_light),
MEDIUM_BOOST(4, 2f, R.string.feed_volume_boost_medium),
HEAVY_BOOST(5, 2.5f, R.string.feed_volume_boost_heavy);
fun toInteger(): Int {
return value

View File

@ -215,11 +215,11 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v
}
@JvmField
val swipeActions: List<SwipeAction> = Collections.unmodifiableList(
listOf(NoActionSwipeAction(), AddToQueueSwipeAction(), StartDownloadSwipeAction(), MarkFavoriteSwipeAction(),
TogglePlaybackStateSwipeAction(), RemoveFromQueueSwipeAction(),
DeleteSwipeAction(), RemoveFromHistorySwipeAction())
)
val swipeActions: List<SwipeAction> = listOf(
NoActionSwipeAction(), AddToQueueSwipeAction(),
StartDownloadSwipeAction(), MarkFavoriteSwipeAction(),
TogglePlaybackStateSwipeAction(), RemoveFromQueueSwipeAction(),
DeleteSwipeAction(), RemoveFromHistorySwipeAction())
private fun getPrefs(tag: String, defaultActions: String): Actions {
val prefsString = prefs!!.getString(KEY_PREFIX_SWIPEACTIONS + tag, defaultActions)

View File

@ -113,13 +113,13 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
override fun onSearchResultClicked(result: SearchPreferenceResult) {
when (val screen = result.resourceFile) {
R.xml.feed_settings -> {
val builder = MaterialAlertDialogBuilder(this)
builder.setTitle(R.string.feed_settings_label)
builder.setMessage(R.string.pref_feed_settings_dialog_msg)
builder.setPositiveButton(android.R.string.ok, null)
builder.show()
}
// R.xml.feed_settings -> {
// val builder = MaterialAlertDialogBuilder(this)
// builder.setTitle(R.string.feed_settings_label)
// builder.setMessage(R.string.pref_feed_settings_dialog_msg)
// builder.setPositiveButton(android.R.string.ok, null)
// builder.show()
// }
R.xml.preferences_notifications -> openScreen(screen)
else -> {
val fragment = openScreen(result.resourceFile)
@ -197,9 +197,9 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
R.xml.preferences_notifications -> {
return R.string.notification_pref_fragment
}
R.xml.feed_settings -> {
return R.string.feed_settings_label
}
// R.xml.feed_settings -> {
// return R.string.feed_settings_label
// }
R.xml.preferences_swipe -> {
return R.string.swipeactions_label
}

View File

@ -23,7 +23,6 @@ object SkipPreferenceDialog {
val choices = arrayOfNulls<String>(values.size)
for (i in values.indices) {
if (skipSecs == values[i]) checked = i
choices[i] = String.format(Locale.getDefault(), "%d %s", values[i], context.getString(R.string.time_seconds))
}
@ -31,15 +30,12 @@ object SkipPreferenceDialog {
builder.setTitle(if (direction == SkipDirection.SKIP_FORWARD) R.string.pref_fast_forward else R.string.pref_rewind)
builder.setSingleChoiceItems(choices, checked) { dialog: DialogInterface, _: Int ->
val choice = (dialog as AlertDialog).listView.checkedItemPosition
if (choice < 0 || choice >= values.size) {
System.err.printf("Choice in showSkipPreference is out of bounds %d", choice)
} else {
if (choice < 0 || choice >= values.size) System.err.printf("Choice in showSkipPreference is out of bounds %d", choice)
else {
val seconds = values[choice]
if (direction == SkipDirection.SKIP_FORWARD) fastForwardSecs = seconds
else rewindSecs = seconds
if (textView != null) textView.text = NumberFormat.getInstance().format(seconds.toLong())
dialog.dismiss()
}
}

View File

@ -26,7 +26,7 @@ class TagSettingsDialog : DialogFragment() {
private var _binding: EditTagsDialogBinding? = null
private val binding get() = _binding!!
private var feedList: List<Feed> = mutableListOf()
private var feedList: MutableList<Feed> = mutableListOf()
private lateinit var displayedTags: MutableList<String>
private lateinit var adapter: SimpleChipAdapter
@ -100,9 +100,10 @@ class TagSettingsDialog : DialogFragment() {
}
@OptIn(UnstableApi::class) private fun updatePreferencesTags(commonTags: Set<String>) {
for (f in feedList) {
for (i in 0..feedList.size-1) {
val f = feedList[i]
Logd(TAG, "${f.title} $displayedTags")
upsertBlk(f) {
feedList[i] = upsertBlk(f) {
if (it.preferences != null) {
it.preferences!!.tags.removeAll(commonTags)
it.preferences!!.tags.addAll(displayedTags)
@ -112,7 +113,7 @@ class TagSettingsDialog : DialogFragment() {
}
private fun setFeedList(feedLst_: List<Feed>) {
feedList = feedLst_
feedList = feedLst_.toMutableList()
}
companion object {

View File

@ -70,8 +70,6 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
private var datasetStats: DatasetStats? = null
private lateinit var navAdapter: NavListAdapter
// private var openFolders: MutableSet<String> = HashSet()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
_binding = NavListBinding.inflate(inflater)
@ -92,11 +90,6 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
insets
}
// val preferences: SharedPreferences = requireContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
// TODO: what is this?
// openFolders = HashSet(prefs!!.getStringSet(PREF_OPEN_FOLDERS, HashSet())!!) // Must not modify
// loadData()
val navList = binding.navRecycler
navAdapter = NavListAdapter()
navAdapter.setHasStableIds(true)
@ -197,7 +190,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
}
}
fun getFragmentTags(): List<String?> {
return Collections.unmodifiableList(fragmentTags)
return fragmentTags
}
override fun getItemCount(): Int {
return subscriptionOffset
@ -359,7 +352,6 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
@VisibleForTesting
const val PREF_LAST_FRAGMENT_TAG: String = "prefLastFragmentTag"
// private const val PREF_OPEN_FOLDERS = "prefOpenFolders"
const val VIEW_TYPE_NAV: Int = 0
const val VIEW_TYPE_SECTION_DIVIDER: Int = 1

View File

@ -294,7 +294,7 @@ import java.util.*
val pos = queueItems.size
queueItems.addAll(event.episodes)
adapter?.notifyItemRangeInserted(pos, queueItems.size)
adapter?.notifyItemRangeChanged(pos, event.episodes.size);
adapter?.notifyItemRangeChanged(pos, event.episodes.size)
}
}
FlowEvent.QueueEvent.Action.SET_QUEUE, FlowEvent.QueueEvent.Action.SORTED -> {
@ -312,7 +312,7 @@ import java.util.*
holder?.unbind()
queueItems.removeAt(pos)
adapter?.notifyItemRemoved(pos)
adapter?.notifyItemRangeChanged(pos, adapter!!.getItemCount()-pos);
adapter?.notifyItemRangeChanged(pos, adapter!!.itemCount -pos)
} else {
Log.e(TAG, "Trying to remove item non-existent from queue ${e.id} ${e.title}")
continue
@ -494,19 +494,16 @@ import java.util.*
fun RenameQueueDialog(showDialog: Boolean, onDismiss: () -> Unit) {
if (showDialog) {
Dialog(onDismissRequest = onDismiss) {
Card(
modifier = Modifier
.wrapContentSize(align = Alignment.Center)
.padding(16.dp),
Card(modifier = Modifier
.wrapContentSize(align = Alignment.Center)
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column(
modifier = Modifier.padding(16.dp),
Column(modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
var newName by remember { mutableStateOf(curQueue.name) }
TextField(
value = newName,
TextField(value = newName,
onValueChange = { newName = it },
label = { Text("Rename (Unique name only)") }
)
@ -537,19 +534,16 @@ import java.util.*
fun AddQueueDialog(showDialog: Boolean, onDismiss: () -> Unit) {
if (showDialog) {
Dialog(onDismissRequest = onDismiss) {
Card(
modifier = Modifier
.wrapContentSize(align = Alignment.Center)
.padding(16.dp),
Card(modifier = Modifier
.wrapContentSize(align = Alignment.Center)
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
Column(
modifier = Modifier.padding(16.dp),
Column(modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
var newName by remember { mutableStateOf("") }
TextField(
value = newName,
TextField(value = newName,
onValueChange = { newName = it },
label = { Text("Add queue (Unique name only)") }
)

View File

@ -27,6 +27,7 @@ import ac.mdiq.podcini.ui.fragment.FeedSettingsFragment.Companion.queueSettingOp
import ac.mdiq.podcini.ui.utils.CoverLoader
import ac.mdiq.podcini.ui.utils.EmptyViewHandler
import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
import ac.mdiq.podcini.util.DateFormatter.formatAbbrev
import ac.mdiq.podcini.util.Logd
import ac.mdiq.podcini.util.event.EventFlow
import ac.mdiq.podcini.util.event.FlowEvent
@ -425,8 +426,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
for (f in feedList_) {
val d = realm.query(Episode::class).query(queryString, f.id).first().find()?.pubDate ?: 0L
counterMap[f.id] = d
val dateFormat = SimpleDateFormat("yy-MM-dd HH:mm", Locale.getDefault())
f.sortInfo = "Updated: " + dateFormat.format(Date(d))
// val dateFormat = SimpleDateFormat("yy-MM-dd HH:mm", Locale.getDefault())
f.sortInfo = "Updated: " + formatAbbrev(requireContext(), Date(d))
}
comparator(counterMap, dir)
}
@ -434,11 +435,10 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
val queryString = "feedId == $0 SORT(media.downloadTime DESC)"
val counterMap: MutableMap<Long, Long> = mutableMapOf()
for (f in feedList_) {
val d =
realm.query(Episode::class).query(queryString, f.id).first().find()?.media?.downloadTime ?: 0L
val d = realm.query(Episode::class).query(queryString, f.id).first().find()?.media?.downloadTime ?: 0L
counterMap[f.id] = d
val dateFormat = SimpleDateFormat("yy-MM-dd HH:mm", Locale.getDefault())
f.sortInfo = "Downloaded: " + dateFormat.format(Date(d))
// val dateFormat = SimpleDateFormat("yy-MM-dd HH:mm", Locale.getDefault())
f.sortInfo = "Downloaded: " + formatAbbrev(requireContext(), Date(d))
}
comparator(counterMap, dir)
}
@ -449,8 +449,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
for (f in feedList_) {
val d = realm.query(Episode::class).query(queryString, f.id).first().find()?.pubDate ?: 0L
counterMap[f.id] = d
val dateFormat = SimpleDateFormat("yy-MM-dd HH:mm", Locale.getDefault())
f.sortInfo = "Unplayed: " + dateFormat.format(Date(d))
// val dateFormat = SimpleDateFormat("yy-MM-dd HH:mm", Locale.getDefault())
f.sortInfo = "Unplayed: " + formatAbbrev(requireContext(), Date(d))
}
comparator(counterMap, dir)
}

View File

@ -13,7 +13,7 @@ object DateFormatter {
@JvmStatic
fun formatRfc822Date(date: Date?): String {
val format = SimpleDateFormat("dd MMM yy HH:mm:ss Z", Locale.US)
return format.format(date)
return format.format(date?: Date(0))
}
@JvmStatic
@ -25,16 +25,40 @@ object DateFormatter {
cal.time = date
val withinLastYear = now[Calendar.YEAR] == cal[Calendar.YEAR]
var format = DateUtils.FORMAT_ABBREV_ALL
if (withinLastYear) {
format = format or DateUtils.FORMAT_NO_YEAR
}
if (withinLastYear) format = format or DateUtils.FORMAT_NO_YEAR
return DateUtils.formatDateTime(context, date.time, format)
}
@JvmStatic
fun formatForAccessibility(date: Date?): String {
if (date == null) return ""
return DateFormat.getDateInstance(DateFormat.LONG).format(date)
}
fun formatDateTimeFlex(date: Date): String {
val now = Date()
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault())
return when {
isSameDay(date, now) -> SimpleDateFormat("HH:mm", Locale.getDefault()).format(date)
isSameYear(date, now) -> SimpleDateFormat("MM-dd HH:mm", Locale.getDefault()).format(date)
else -> formatter.format(date)
}
}
fun isSameDay(date1: Date, date2: Date): Boolean {
val cal1 = Calendar.getInstance()
cal1.time = date1
val cal2 = Calendar.getInstance()
cal2.time = date2
return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)
}
fun isSameYear(date1: Date, date2: Date): Boolean {
val cal1 = Calendar.getInstance()
cal1.time = date1
val cal2 = Calendar.getInstance()
cal2.time = date2
return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
}
}

View File

@ -23,22 +23,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/settings_fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
android:layout_height="wrap_content" />
</ScrollView>
</LinearLayout>

View File

@ -1,47 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="spnAutoDLPolicyItems">
<item>@string/feed_auto_download_new</item>
<item>@string/feed_auto_download_newer</item>
<item>@string/feed_auto_download_older</item>
</string-array>
<string-array name="spnAutoDLPolicyValues">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
<string-array name="spnAutoDeleteItems">
<item>@string/global_default</item>
<item>@string/feed_auto_download_always</item>
<item>@string/feed_auto_download_never</item>
</string-array>
<string-array name="spnAutoDeleteValues">
<item>global</item>
<item>always</item>
<item>never</item>
</string-array>
<string-array name="spnVolumeAdaptationItems">
<item>@string/feed_volume_reduction_heavy</item>
<item>@string/feed_volume_reduction_light</item>
<item>@string/feed_volume_reduction_off</item>
<item>@string/feed_volume_boost_light</item>
<item>@string/feed_volume_boost_medium</item>
<item>@string/feed_volume_boost_heavy</item>
</string-array>
<string-array name="spnVolumeAdaptationValues">
<item>heavy</item>
<item>light</item>
<item>off</item>
<item>light_boost</item>
<item>medium_boost</item>
<item>heavy_boost</item>
</string-array>
<string-array name="feed_refresh_interval_entries">
<item>@string/feed_refresh_never</item>
<item>@string/feed_every_hour</item>
@ -92,7 +50,6 @@
<item>500</item>
<item>@string/pref_episode_cache_unlimited</item>
</string-array>
<string-array name="episode_cache_size_values">
<item>5</item>
<item>10</item>
@ -103,27 +60,6 @@
<item>-1</item>
</string-array>
<string-array name="feed_episode_cache_size_entries">
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>10</item>
<item>15</item>
<item>20</item>
<item>@string/pref_episode_cache_unlimited</item>
</string-array>
<string-array name="feed_episode_cache_size_values">
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>10</item>
<item>15</item>
<item>20</item>
<item>-1</item>
</string-array>
<string-array name="mobile_update_entries">
<item>@string/pref_mobileUpdate_refresh</item>
<item>@string/pref_mobileUpdate_episode_download</item>

View File

@ -810,6 +810,8 @@
<string name="mark_excluded_episodes_played">Mark excluded episodes played</string>
<string name="keep_updated">Keep updated</string>
<string name="keep_updated_summary">Include this podcast when (auto-)refreshing all podcasts</string>
<string name="audo_add_new_queue">Auto add new to queue</string>
<string name="audo_add_new_queue_summary">Automatically add new episodes in this podcast to the designated queue when (auto-)refreshing</string>
<string name="auto_download_disabled_globally">Auto download is disabled in the main Podcini settings</string>
<string name="statistics_time_played">Time played:</string>
<string name="statistics_total_duration">Total duration (estimate):</string>

View File

@ -1,89 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="feedSettingsScreen">
<!-- <SwitchPreferenceCompat-->
<!-- android:icon="@drawable/ic_refresh"-->
<!-- android:key="keepUpdated"-->
<!-- android:summary="@string/keep_updated_summary"-->
<!-- android:title="@string/keep_updated" />-->
<Preference
android:icon="@drawable/ic_key"
android:key="authentication"
android:summary="@string/authentication_descr"
android:title="@string/authentication_label" />
<Preference
android:icon="@drawable/ic_tag"
android:key="tags"
android:summary="@string/feed_tags_summary"
android:title="@string/feed_tags_label" />
<!-- <Preference-->
<!-- android:icon="@drawable/ic_playlist_play"-->
<!-- android:key="associatedQueue"-->
<!-- android:summary="@string/pref_feed_associated_queue_sum"-->
<!-- android:title="@string/pref_feed_associated_queue" />-->
<Preference
android:icon="@drawable/ic_playback_speed"
android:key="feedPlaybackSpeed"
android:summary="@string/pref_feed_playback_speed_sum"
android:title="@string/playback_speed" />
<Preference
android:icon="@drawable/ic_skip_24dp"
android:key="feedAutoSkip"
android:summary="@string/pref_feed_skip_sum"
android:title="@string/pref_feed_skip" />
<!-- <ac.mdiq.podcini.preferences.MaterialListPreference-->
<!-- android:entries="@array/spnAutoDeleteItems"-->
<!-- android:entryValues="@array/spnAutoDeleteValues"-->
<!-- android:icon="@drawable/ic_delete"-->
<!-- android:key="autoDelete"-->
<!-- android:summary="@string/global_default"-->
<!-- android:title="@string/auto_delete_label" />-->
<ac.mdiq.podcini.preferences.MaterialListPreference
android:defaultValue="off"
android:entries="@array/spnVolumeAdaptationItems"
android:entryValues="@array/spnVolumeAdaptationValues"
android:icon="@drawable/ic_volume_adaption"
android:key="volumeReduction"
android:summary="@string/feed_volume_adaptation_summary"
android:title="@string/feed_volume_adapdation" />
<PreferenceCategory
android:key="autoDownloadCategory"
android:title="@string/auto_download_settings_label">
<SwitchPreferenceCompat
android:key="autoDownload"
android:title="@string/auto_download_label" />
<ac.mdiq.podcini.preferences.MaterialListPreference
android:entries="@array/spnAutoDLPolicyItems"
android:entryValues="@array/spnAutoDLPolicyValues"
android:key="feedAutoDownloadPolicy"
android:title="@string/feed_auto_download_policy" />
<ac.mdiq.podcini.preferences.MaterialListPreference
android:defaultValue="3"
android:entries="@array/feed_episode_cache_size_entries"
android:key="feedEpisodeCacheSize"
android:title="@string/pref_episode_cache_title"
android:summary="@string/pref_episode_cache_summary"
android:entryValues="@array/feed_episode_cache_size_values"/>
<SwitchPreferenceCompat
android:key="countingPlayed"
android:summary="@string/pref_auto_download_counting_played_summary"
android:title="@string/pref_auto_download_counting_played_title" />
<Preference
android:key="episodeInclusiveFilter"
android:summary="@string/episode_filters_description"
android:title="@string/episode_inclusive_filters_label" />
<Preference
android:key="episodeExclusiveFilter"
android:summary="@string/episode_filters_description"
android:title="@string/episode_exclusive_filters_label" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -1,3 +1,11 @@
# 6.3.5
* added more Log.d statements in hope for tracking down the mysterious random playing
* FeedSettings view is all in Jetpack Compose, FeedSettingsPreferenceFragment removed
* in FeedSettings, added "Audo add new to queue" (accissible when associated queue not set to "None")
* when set, new episodes during refresh will be added to the associated queue, regardless of being downloaded
* use adaptive date formats (stripped time) in Subscriptions view
# 6.3.4
* fixed mis-behavior of setting associated queue to Active in FeedSettings