4.7.1 commit

This commit is contained in:
Xilin Jia 2024-04-12 10:38:27 +00:00
parent 26b506ed62
commit f7193b9af5
23 changed files with 170 additions and 363 deletions

View File

@ -149,8 +149,8 @@ android {
// Version code schema (not used):
// "1.2.3-beta4" -> 1020304
// "1.2.3" -> 1020395
versionCode 3020128
versionName "4.7.0"
versionCode 3020129
versionName "4.7.1"
def commit = ""
try {

View File

@ -32,12 +32,12 @@ import android.os.Build
import java.util.*
class FeedUpdateWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
private val newEpisodesNotification = NewEpisodesNotification()
// private val newEpisodesNotification = NewEpisodesNotification()
private val notificationManager = NotificationManagerCompat.from(context)
@UnstableApi override fun doWork(): Result {
ClientConfigurator.initialize(applicationContext)
newEpisodesNotification.loadCountersBeforeRefresh()
// newEpisodesNotification.loadCountersBeforeRefresh()
val toUpdate: MutableList<Feed>
val feedId = inputData.getLong(FeedUpdateManager.EXTRA_FEED_ID, -1)
@ -110,6 +110,7 @@ class FeedUpdateWorker(context: Context, params: WorkerParameters) : Worker(cont
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
@ -186,7 +187,7 @@ class FeedUpdateWorker(context: Context, params: WorkerParameters) : Worker(cont
if (log.isNotEmpty() && !log[0].isSuccessful) {
DBWriter.addDownloadStatus(feedSyncTask.downloadStatus)
}
newEpisodesNotification.showIfNeeded(applicationContext, feedSyncTask.savedFeed!!)
// newEpisodesNotification.showIfNeeded(applicationContext, feedSyncTask.savedFeed!!)
if (!request.source.isNullOrEmpty()) {
when {
!downloader.permanentRedirectUrl.isNullOrEmpty() -> {

View File

@ -1,161 +0,0 @@
package ac.mdiq.podcini.net.download.service
import ac.mdiq.podcini.R
import ac.mdiq.podcini.storage.database.PodDBAdapter
import ac.mdiq.podcini.storage.model.feed.Feed
import ac.mdiq.podcini.storage.model.feed.FeedCounter
import ac.mdiq.podcini.ui.utils.NotificationUtils
import android.Manifest
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
class NewEpisodesNotification {
private var countersBefore: Map<Long, Int>? = null
fun loadCountersBeforeRefresh() {
val adapter = PodDBAdapter.getInstance()
adapter.open()
countersBefore = adapter.getFeedCounters(FeedCounter.SHOW_NEW)
adapter.close()
}
fun showIfNeeded(context: Context, feed: Feed) {
val prefs = feed.preferences
if (prefs == null || !prefs.keepUpdated || !prefs.showEpisodeNotification) {
return
}
val newEpisodesBefore = if (countersBefore!!.containsKey(feed.id)) countersBefore!![feed.id]!! else 0
val newEpisodesAfter = getNewEpisodeCount(feed.id)
Log.d(TAG, "New episodes before: $newEpisodesBefore, after: $newEpisodesAfter")
if (newEpisodesAfter > newEpisodesBefore) {
val notificationManager = NotificationManagerCompat.from(context)
showNotification(newEpisodesAfter, feed, context, notificationManager)
}
}
companion object {
private const val TAG = "NewEpisodesNotification"
private const val GROUP_KEY = "ac.mdiq.podcini.EPISODES"
private fun showNotification(newEpisodes: Int, feed: Feed, context: Context,
notificationManager: NotificationManagerCompat
) {
val res = context.resources
val text = res.getQuantityString(
R.plurals.new_episode_notification_message, newEpisodes, newEpisodes, feed.title
)
val title = res.getQuantityString(R.plurals.new_episode_notification_title, newEpisodes)
val intent = Intent()
intent.setAction("NewEpisodes" + feed.id)
intent.setComponent(ComponentName(context, "ac.mdiq.podcini.activity.MainActivity"))
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
intent.putExtra("fragment_feed_id", feed.id)
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
val notification = NotificationCompat.Builder(
context, NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS)
.setSmallIcon(R.drawable.ic_notification_new)
.setContentTitle(title)
.setLargeIcon(loadIcon(context, feed))
.setContentText(text)
.setContentIntent(pendingIntent)
.setGroup(GROUP_KEY)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
.setOnlyAlertOnce(true)
.setAutoCancel(true)
.build()
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(context,
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
Log.e(TAG, "showNotification: require POST_NOTIFICATIONS permission")
Toast.makeText(context, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
return
}
notificationManager.notify(NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS,
feed.hashCode(),
notification)
showGroupSummaryNotification(context, notificationManager)
}
private fun showGroupSummaryNotification(context: Context, notificationManager: NotificationManagerCompat) {
val intent = Intent()
intent.setAction("NewEpisodes")
intent.setComponent(ComponentName(context, "ac.mdiq.podcini.activity.MainActivity"))
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
intent.putExtra("fragment_tag", "NewEpisodesFragment")
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
val notificationGroupSummary = NotificationCompat.Builder(
context, NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS)
.setSmallIcon(R.drawable.ic_notification_new)
.setContentTitle(context.getString(R.string.new_episode_notification_group_text))
.setContentIntent(pendingIntent)
.setGroup(GROUP_KEY)
.setGroupSummary(true)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
.setOnlyAlertOnce(true)
.setAutoCancel(true)
.build()
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(context,
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
Log.e(TAG, "showGroupSummaryNotification: require POST_NOTIFICATIONS permission")
Toast.makeText(context, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
return
}
notificationManager.notify(NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS, 0, notificationGroupSummary)
}
private fun loadIcon(context: Context, feed: Feed): Bitmap? {
val iconSize = (128 * context.resources.displayMetrics.density).toInt()
return try {
if (!feed.imageUrl.isNullOrBlank()) Glide.with(context)
.asBitmap()
.load(feed.imageUrl)
.apply(RequestOptions().centerCrop())
.submit(iconSize, iconSize)
.get()
else null
} catch (tr: Throwable) {
null
}
}
private fun getNewEpisodeCount(feedId: Long): Int {
val adapter = PodDBAdapter.getInstance()
adapter.open()
val counters = adapter.getFeedCounters(FeedCounter.SHOW_NEW, feedId)
val episodeCount = if (counters.containsKey(feedId)) counters[feedId]!! else 0
adapter.close()
return episodeCount
}
}
}

View File

@ -253,6 +253,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
@ -590,6 +591,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
@ -1334,6 +1336,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)

View File

@ -251,7 +251,7 @@ object UserPreferences {
@JvmStatic
val feedCounterSetting: FeedCounter
get() {
val value = prefs.getString(PREF_DRAWER_FEED_COUNTER, "" + FeedCounter.SHOW_NEW.id)
val value = prefs.getString(PREF_DRAWER_FEED_COUNTER, "" + FeedCounter.SHOW_UNPLAYED.id)
return FeedCounter.fromOrdinal(value!!.toInt())
}

View File

@ -19,8 +19,7 @@ class NotificationPreferencesFragment : PreferenceFragmentCompat() {
}
private fun setUpScreen() {
findPreference<Preference>(ac.mdiq.podcini.preferences.fragments.NotificationPreferencesFragment.Companion.PREF_GPODNET_NOTIFICATIONS)!!.isEnabled =
SynchronizationSettings.isProviderConnected
findPreference<Preference>(PREF_GPODNET_NOTIFICATIONS)!!.isEnabled = SynchronizationSettings.isProviderConnected
}
companion object {

View File

@ -141,6 +141,7 @@ object DBReader {
* @param items The FeedItems whose Feed-objects should be loaded.
*/
private fun loadFeedDataOfFeedItemList(items: List<FeedItem>) {
Log.d(TAG, "loadFeedDataOfFeedItemList called")
val feedIndex: MutableMap<Long, Feed> = ArrayMap(feeds.size)
val feedsCopy = ArrayList(feeds)
for (feed in feedsCopy) {
@ -235,7 +236,7 @@ object DBReader {
@JvmStatic
fun getQueue(adapter: PodDBAdapter?): List<FeedItem> {
// Log.d(TAG, "getQueue()")
Log.d(TAG, "getQueue(adapter)")
adapter?.queueCursor.use { cursor ->
val items = extractItemlistFromCursor(adapter, cursor)
loadAdditionalFeedItemListData(items)
@ -323,6 +324,7 @@ object DBReader {
@JvmStatic
fun getTotalEpisodeCount(filter: FeedItemFilter?): Int {
Log.d(TAG, "getTotalEpisodeCount called")
val adapter = getInstance()
adapter.open()
try {
@ -506,7 +508,7 @@ object DBReader {
}
return item
}
return item
return null
}
/**
@ -582,19 +584,16 @@ object DBReader {
* Does NOT load additional attributes like feed or queue state.
*/
private fun getFeedItemByGuidOrEpisodeUrl(guid: String?, episodeUrl: String,
adapter: PodDBAdapter?
): FeedItem? {
if (adapter != null) {
adapter.getFeedItemCursor(guid, episodeUrl).use { cursor ->
if (!cursor.moveToNext()) {
return null
}
val list = extractItemlistFromCursor(adapter, cursor)
if (list.isNotEmpty()) {
return list[0]
}
adapter: PodDBAdapter?): FeedItem? {
adapter?.getFeedItemCursor(guid, episodeUrl)?.use { cursor ->
if (!cursor.moveToNext()) {
return null
}
val list = extractItemlistFromCursor(adapter, cursor)
if (list.isNotEmpty()) {
return list[0]
}
return null
}
return null
}
@ -618,20 +617,18 @@ object DBReader {
}
private fun getImageAuthentication(imageUrl: String, adapter: PodDBAdapter?): String {
var credentials: String = ""
if (adapter != null) {
adapter.getImageAuthenticationCursor(imageUrl).use { cursor ->
if (cursor.moveToFirst()) {
val username = cursor.getString(0)
val password = cursor.getString(1)
credentials = if (!username.isNullOrEmpty() && password != null) {
"$username:$password"
} else {
""
}
var credentials = ""
adapter?.getImageAuthenticationCursor(imageUrl)?.use { cursor ->
if (cursor.moveToFirst()) {
val username = cursor.getString(0)
val password = cursor.getString(1)
credentials = if (!username.isNullOrEmpty() && password != null) {
"$username:$password"
} else {
credentials = ""
""
}
} else {
credentials = ""
}
}
return credentials
@ -647,6 +644,7 @@ object DBReader {
*/
@JvmStatic
fun getFeedItemByGuidOrEpisodeUrl(guid: String?, episodeUrl: String): FeedItem? {
Log.d(TAG, "getFeedItemByGuidOrEpisodeUrl called")
val adapter = getInstance()
adapter.open()
try {
@ -705,19 +703,17 @@ object DBReader {
}
private fun loadChaptersOfFeedItem(adapter: PodDBAdapter?, item: FeedItem): List<Chapter>? {
if (adapter != null) {
adapter.getSimpleChaptersOfFeedItemCursor(item).use { cursor ->
val chaptersCount = cursor.count
if (chaptersCount == 0) {
item.chapters = null
return null
}
val chapters = ArrayList<Chapter>()
while (cursor.moveToNext()) {
chapters.add(ChapterCursorMapper.convert(cursor))
}
return chapters
adapter?.getSimpleChaptersOfFeedItemCursor(item)?.use { cursor ->
val chaptersCount = cursor.count
if (chaptersCount == 0) {
item.chapters = null
return null
}
val chapters = ArrayList<Chapter>()
while (cursor.moveToNext()) {
chapters.add(ChapterCursorMapper.convert(cursor))
}
return chapters
}
return null
}
@ -730,9 +726,9 @@ object DBReader {
*/
@JvmStatic
fun getFeedMedia(mediaId: Long): FeedMedia? {
Log.d(TAG, "getFeedMedia called")
val adapter = getInstance()
adapter.open()
try {
adapter.getSingleFeedMediaCursor(mediaId).use { mediaCursor ->
if (!mediaCursor.moveToFirst()) {
@ -798,8 +794,7 @@ object DBReader {
* @return The list of statistics objects
*/
fun getStatistics(includeMarkedAsPlayed: Boolean,
timeFilterFrom: Long, timeFilterTo: Long
): StatisticsResult {
timeFilterFrom: Long, timeFilterTo: Long): StatisticsResult {
val adapter = getInstance()
adapter.open()

View File

@ -196,6 +196,7 @@ import java.util.concurrent.*
@JvmStatic
@Synchronized
fun updateFeed(context: Context, newFeed: Feed, removeUnlistedItems: Boolean): Feed? {
Log.d(TAG, "updateFeed called")
var resultFeed: Feed?
val unlistedItems: MutableList<FeedItem> = ArrayList()
@ -227,14 +228,6 @@ import java.util.concurrent.*
savedFeed.preferences!!.updateFromOther(newFeed.preferences)
}
// get the most recent date now, before we start changing the list
// only used to add to IN_Box???
// val priorMostRecent = savedFeed.mostRecentItem
// var priorMostRecentDate: Date? = Date()
// if (priorMostRecent != null) {
// priorMostRecentDate = priorMostRecent.getPubDate()
// }
// Look for new or updated Items
for (idx in newFeed.items.indices) {
val item = newFeed.items[idx]
@ -298,18 +291,6 @@ import java.util.concurrent.*
} else {
savedFeed.items.add(idx, item)
}
// var action = savedFeed.preferences!!.newEpisodesAction
// if (action == NewEpisodesAction.GLOBAL) {
// action = newEpisodesAction
// }
// if (action == NewEpisodesAction.ADD_TO_INBOX
// && (item.getPubDate() == null || priorMostRecentDate == null || priorMostRecentDate.before(
// item.getPubDate()) || priorMostRecentDate == item.getPubDate())) {
// Log.d(TAG, "Marking item published on " + item.getPubDate()
// + " new, prior most recent date = " + priorMostRecentDate)
// item.setNew()
// }
}
}
@ -383,6 +364,7 @@ import java.util.concurrent.*
*/
@JvmStatic
fun searchFeedItems(feedID: Long, query: String): FutureTask<List<FeedItem>> {
Log.d(TAG, "searchFeedItems called")
return FutureTask(object : QueryTask<List<FeedItem>>() {
override fun execute(adapter: PodDBAdapter?) {
val searchResult = adapter?.searchItems(feedID, query)

View File

@ -88,6 +88,7 @@ import java.util.concurrent.TimeUnit
*/
@JvmStatic
fun deleteFeedMediaOfItem(context: Context, mediaId: Long): Future<*> {
Log.d(TAG, "deleteFeedMediaOfItem called")
return runOnDbThread {
val media = getFeedMedia(mediaId)
if (media != null) {
@ -194,6 +195,7 @@ import java.util.concurrent.TimeUnit
* Deleting media also removes the download log entries.
*/
fun deleteFeedItems(context: Context, items: List<FeedItem>): Future<*> {
Log.d(TAG, "deleteFeedItems called")
return runOnDbThread { deleteFeedItemsSynchronous(context, items) }
}
@ -282,13 +284,6 @@ import java.util.concurrent.TimeUnit
* @param media FeedMedia that should be added to the playback history.
* @param date PlaybackCompletionDate for `media`
*/
/**
* Adds a FeedMedia object to the playback history. A FeedMedia object is in the playback history if
* its playback completion date is set to a non-null value. This method will set the playback completion date to the
* current date regardless of the current value.
*
* @param media FeedMedia that should be added to the playback history.
*/
@JvmOverloads
fun addItemToPlaybackHistory(media: FeedMedia?, date: Date? = Date()): Future<*> {
return runOnDbThread {
@ -311,6 +306,7 @@ import java.util.concurrent.TimeUnit
* @param status The DownloadStatus object.
*/
fun addDownloadStatus(status: DownloadResult?): Future<*> {
Log.d(TAG, "addDownloadStatus called")
return runOnDbThread {
if (status != null) {
val adapter = getInstance()
@ -335,6 +331,7 @@ import java.util.concurrent.TimeUnit
@UnstableApi fun addQueueItemAt(context: Context, itemId: Long,
index: Int, performAutoDownload: Boolean
): Future<*> {
Log.d(TAG, "addQueueItemAt called")
return runOnDbThread {
val adapter = getInstance()
adapter.open()
@ -368,6 +365,7 @@ import java.util.concurrent.TimeUnit
}
fun addQueueItem(context: Context, markAsUnplayed: Boolean, vararg items: FeedItem): Future<*> {
Log.d(TAG, "addQueueItem called")
val itemIds = LongList(items.size)
for (item in items) {
if (!item.hasMedia()) {
@ -403,8 +401,8 @@ import java.util.concurrent.TimeUnit
* @param itemIds IDs of the FeedItem objects that should be added to the queue.
*/
@UnstableApi fun addQueueItem(context: Context, performAutoDownload: Boolean,
markAsUnplayed: Boolean, vararg itemIds: Long
): Future<*> {
markAsUnplayed: Boolean, vararg itemIds: Long): Future<*> {
Log.d(TAG, "addQueueItem(context ...) called")
return runOnDbThread {
if (itemIds.isEmpty()) {
return@runOnDbThread
@ -506,26 +504,20 @@ import java.util.concurrent.TimeUnit
* @param item FeedItem that should be removed.
*/
@JvmStatic
fun removeQueueItem(context: Context,
performAutoDownload: Boolean, item: FeedItem
): Future<*> {
fun removeQueueItem(context: Context, performAutoDownload: Boolean, item: FeedItem): Future<*> {
return runOnDbThread { removeQueueItemSynchronous(context, performAutoDownload, item.id) }
}
@JvmStatic
fun removeQueueItem(context: Context, performAutoDownload: Boolean,
vararg itemIds: Long
): Future<*> {
fun removeQueueItem(context: Context, performAutoDownload: Boolean, vararg itemIds: Long): Future<*> {
return runOnDbThread { removeQueueItemSynchronous(context, performAutoDownload, *itemIds) }
}
@UnstableApi private fun removeQueueItemSynchronous(context: Context,
performAutoDownload: Boolean,
vararg itemIds: Long
) {
if (itemIds.isEmpty()) {
return
}
performAutoDownload: Boolean, vararg itemIds: Long) {
Log.d(TAG, "removeQueueItemSynchronous called")
if (itemIds.isEmpty()) return
val adapter = getInstance()
adapter.open()
val queue = getQueue(adapter).toMutableList()
@ -643,9 +635,7 @@ import java.util.concurrent.TimeUnit
* @throws IndexOutOfBoundsException if (to < 0 || to >= queue.size()) || (from < 0 || from >= queue.size())
*/
@JvmStatic
fun moveQueueItem(from: Int,
to: Int, broadcastUpdate: Boolean
): Future<*> {
fun moveQueueItem(from: Int, to: Int, broadcastUpdate: Boolean): Future<*> {
return runOnDbThread { moveQueueItemHelper(from, to, broadcastUpdate) }
}
@ -661,9 +651,7 @@ import java.util.concurrent.TimeUnit
* false if the caller wants to avoid unexpected updates of the GUI.
* @throws IndexOutOfBoundsException if (to < 0 || to >= queue.size()) || (from < 0 || from >= queue.size())
*/
private fun moveQueueItemHelper(from: Int,
to: Int, broadcastUpdate: Boolean
) {
private fun moveQueueItemHelper(from: Int, to: Int, broadcastUpdate: Boolean) {
val adapter = getInstance()
adapter.open()
val queue = getQueue(adapter).toMutableList()
@ -743,13 +731,11 @@ import java.util.concurrent.TimeUnit
private fun markItemPlayed(itemId: Long,
played: Int,
mediaId: Long,
resetMediaPosition: Boolean
): Future<*> {
resetMediaPosition: Boolean): Future<*> {
return runOnDbThread {
val adapter = getInstance()
adapter.open()
adapter.setFeedItemRead(played, itemId, mediaId,
resetMediaPosition)
adapter.setFeedItemRead(played, itemId, mediaId, resetMediaPosition)
adapter.close()
EventBus.getDefault().post(UnreadItemsUpdateEvent())
}
@ -828,6 +814,7 @@ import java.util.concurrent.TimeUnit
* @param media The FeedMedia object.
*/
fun setFeedMedia(media: FeedMedia?): Future<*> {
Log.d(TAG, "setFeedMedia called")
return runOnDbThread {
val adapter = getInstance()
adapter.open()
@ -843,6 +830,7 @@ import java.util.concurrent.TimeUnit
*/
@JvmStatic
fun setFeedMediaPlaybackInformation(media: FeedMedia?): Future<*> {
Log.d(TAG, "setFeedMediaPlaybackInformation called")
return runOnDbThread {
if (media != null) {
val adapter = getInstance()
@ -861,6 +849,7 @@ import java.util.concurrent.TimeUnit
*/
@JvmStatic
fun setFeedItem(item: FeedItem?): Future<*> {
Log.d(TAG, "setFeedItem called")
return runOnDbThread {
if (item != null) {
val adapter = getInstance()
@ -905,6 +894,7 @@ import java.util.concurrent.TimeUnit
}
private fun indexInItemList(items: List<FeedItem?>, itemId: Long): Int {
Log.d(TAG, "indexInItemList called")
for (i in items.indices) {
val item = items[i]
if (item?.id == itemId) {
@ -919,9 +909,7 @@ import java.util.concurrent.TimeUnit
*
* @param lastUpdateFailed true if last update failed
*/
fun setFeedLastUpdateFailed(feedId: Long,
lastUpdateFailed: Boolean
): Future<*> {
fun setFeedLastUpdateFailed(feedId: Long, lastUpdateFailed: Boolean): Future<*> {
return runOnDbThread {
val adapter = getInstance()
adapter.open()
@ -974,9 +962,7 @@ import java.util.concurrent.TimeUnit
* @param feedId The feed's ID
* @param filterValues Values that represent properties to filter by
*/
fun setFeedItemsFilter(feedId: Long,
filterValues: Set<String>
): Future<*> {
fun setFeedItemsFilter(feedId: Long, filterValues: Set<String>): Future<*> {
Log.d(TAG, "setFeedItemsFilter() called with: feedId = [$feedId], filterValues = [$filterValues]")
return runOnDbThread {
val adapter = getInstance()

View File

@ -917,7 +917,7 @@ class PodDBAdapter private constructor() {
fun getFeedCounters(setting: FeedCounter?, vararg feedIds: Long): Map<Long, Int> {
val whereRead = when (setting) {
FeedCounter.SHOW_NEW -> KEY_READ + "=" + FeedItem.NEW
// FeedCounter.SHOW_NEW -> KEY_READ + "=" + FeedItem.NEW
FeedCounter.SHOW_UNPLAYED -> ("(" + KEY_READ + "=" + FeedItem.NEW
+ " OR " + KEY_READ + "=" + FeedItem.UNPLAYED + ")")
FeedCounter.SHOW_DOWNLOADED -> "$KEY_DOWNLOADED=1"

View File

@ -1,7 +1,7 @@
package ac.mdiq.podcini.storage.model.feed
enum class FeedCounter(val id: Int) {
SHOW_NEW(1),
// SHOW_NEW(1),
SHOW_UNPLAYED(2),
SHOW_NONE(3),
SHOW_DOWNLOADED(4),

View File

@ -28,9 +28,9 @@ class FeedMultiSelectActionHandler(private val activity: MainActivity, private v
R.id.remove_feed -> {
RemoveFeedDialog.show(activity, selectedItems)
}
R.id.notify_new_episodes -> {
notifyNewEpisodesPrefHandler()
}
// R.id.notify_new_episodes -> {
// notifyNewEpisodesPrefHandler()
// }
R.id.keep_updated -> {
keepUpdatedPrefHandler()
}
@ -52,20 +52,20 @@ class FeedMultiSelectActionHandler(private val activity: MainActivity, private v
}
}
private fun notifyNewEpisodesPrefHandler() {
val preferenceSwitchDialog = PreferenceSwitchDialog(activity,
activity.getString(R.string.episode_notification),
activity.getString(R.string.episode_notification_summary))
preferenceSwitchDialog.setOnPreferenceChangedListener(object: PreferenceSwitchDialog.OnPreferenceChangedListener {
@UnstableApi override fun preferenceChanged(enabled: Boolean) {
saveFeedPreferences { feedPreferences: FeedPreferences ->
feedPreferences.showEpisodeNotification = enabled
}
}
})
preferenceSwitchDialog.openDialog()
}
// private fun notifyNewEpisodesPrefHandler() {
// val preferenceSwitchDialog = PreferenceSwitchDialog(activity,
// activity.getString(R.string.episode_notification),
// activity.getString(R.string.episode_notification_summary))
//
// preferenceSwitchDialog.setOnPreferenceChangedListener(object: PreferenceSwitchDialog.OnPreferenceChangedListener {
// @UnstableApi override fun preferenceChanged(enabled: Boolean) {
// saveFeedPreferences { feedPreferences: FeedPreferences ->
// feedPreferences.showEpisodeNotification = enabled
// }
// }
// })
// preferenceSwitchDialog.openDialog()
// }
private fun autoDownloadPrefHandler() {
val preferenceSwitchDialog = PreferenceSwitchDialog(activity,

View File

@ -78,7 +78,7 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
prefFragment = PlaybackPreferencesFragment()
}
R.xml.preferences_notifications -> {
prefFragment = ac.mdiq.podcini.preferences.fragments.NotificationPreferencesFragment()
prefFragment = NotificationPreferencesFragment()
}
R.xml.preferences_swipe -> {
prefFragment = SwipePreferencesFragment()

View File

@ -288,8 +288,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
binding.videoView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
setupVideoControlsToggler()
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
binding.videoPlayerContainer.setOnTouchListener(onVideoviewTouched)
binding.videoPlayerContainer.viewTreeObserver.addOnGlobalLayoutListener {
@ -308,12 +307,10 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
}
private val onVideoviewTouched = OnTouchListener { v: View, event: MotionEvent ->
if (event.action != MotionEvent.ACTION_DOWN) {
return@OnTouchListener false
}
if (PictureInPictureUtil.isInPictureInPictureMode(this)) {
return@OnTouchListener true
}
if (event.action != MotionEvent.ACTION_DOWN) return@OnTouchListener false
if (PictureInPictureUtil.isInPictureInPictureMode(this)) return@OnTouchListener true
videoControlsHider.removeCallbacks(hideVideoControls)
if (System.currentTimeMillis() - lastScreenTap < 300) {
@ -333,9 +330,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
}
toggleVideoControlsVisibility()
if (videoControlsShowing) {
setupVideoControlsToggler()
}
if (videoControlsShowing) setupVideoControlsToggler()
lastScreenTap = System.currentTimeMillis()
true
@ -609,7 +604,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
shareDialog.show(supportFragmentManager, "ShareEpisodeDialog")
}
item.itemId == R.id.playback_speed -> {
VariableSpeedDialog.newInstance(booleanArrayOf(true, false, true))?.show(supportFragmentManager, null)
VariableSpeedDialog.newInstance(booleanArrayOf(true, true, true))?.show(supportFragmentManager, null)
}
else -> {
return false
@ -714,9 +709,7 @@ class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener {
//Hardware keyboard support
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
val currentFocus = currentFocus
if (currentFocus is EditText) {
return super.onKeyUp(keyCode, event)
}
if (currentFocus is EditText) return super.onKeyUp(keyCode, event)
val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager

View File

@ -137,7 +137,6 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
playerView2.setBackgroundColor(
SurfaceColors.getColorForElevation(requireContext(), 8 * resources.displayMetrics.density))
// itemDesrView = binding.itemDescription
cardViewSeek = binding.cardViewSeek
txtvSeek = binding.txtvSeek
@ -146,11 +145,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
itemDescFrag = PlayerDetailsFragment()
transaction.replace(R.id.itemDescription, itemDescFrag).commit()
// controller = externalPlayerFragment1.controller
// loadMediaInfo(false)
EventBus.getDefault().register(this)
// updateUi(controller?.getMedia())
return binding.root
}

View File

@ -11,7 +11,6 @@ import ac.mdiq.podcini.storage.model.feed.Feed
import ac.mdiq.podcini.storage.model.feed.FeedFilter
import ac.mdiq.podcini.storage.model.feed.FeedPreferences
import ac.mdiq.podcini.storage.model.feed.FeedPreferences.AutoDeleteAction
import ac.mdiq.podcini.storage.model.feed.FeedPreferences.NewEpisodesAction
import ac.mdiq.podcini.storage.model.feed.VolumeAdaptionSetting
import ac.mdiq.podcini.ui.dialog.AuthenticationDialog
import ac.mdiq.podcini.ui.dialog.EpisodeFilterDialog
@ -20,12 +19,9 @@ import ac.mdiq.podcini.ui.dialog.TagSettingsDialog
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
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.util.Log
@ -36,7 +32,6 @@ import android.widget.CompoundButton
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.OptIn
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.media3.common.util.UnstableApi
import androidx.preference.ListPreference
@ -159,7 +154,7 @@ class FeedSettingsFragment : Fragment() {
setupEpisodeFilterPreference()
setupPlaybackSpeedPreference()
setupFeedAutoSkipPreference()
setupEpisodeNotificationPreference()
// setupEpisodeNotificationPreference()
setupTags()
updateAutoDeleteSummary()
@ -440,25 +435,25 @@ class FeedSettingsFragment : Fragment() {
}
}
@OptIn(UnstableApi::class) private fun setupEpisodeNotificationPreference() {
val pref = findPreference<SwitchPreferenceCompat>("episodeNotification")
pref!!.isChecked = feedPreferences!!.showEpisodeNotification
pref.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
if (Build.VERSION.SDK_INT >= 33 && ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
return@OnPreferenceChangeListener false
}
val checked = newValue == true
feedPreferences!!.showEpisodeNotification = checked
if (feedPreferences != null) DBWriter.setFeedPreferences(feedPreferences!!)
pref.isChecked = checked
false
}
}
// @OptIn(UnstableApi::class) private fun setupEpisodeNotificationPreference() {
// val pref = findPreference<SwitchPreferenceCompat>("episodeNotification")
//
// pref!!.isChecked = feedPreferences!!.showEpisodeNotification
// pref.onPreferenceChangeListener =
// Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
// if (Build.VERSION.SDK_INT >= 33 && ContextCompat.checkSelfPermission(
// requireContext(),
// Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
// requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
// return@OnPreferenceChangeListener false
// }
// val checked = newValue == true
// feedPreferences!!.showEpisodeNotification = checked
// if (feedPreferences != null) DBWriter.setFeedPreferences(feedPreferences!!)
// pref.isChecked = checked
// false
// }
// }
companion object {
private val PREF_EPISODE_FILTER: CharSequence = "episodeFilter"

View File

@ -135,7 +135,7 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
catSpinner.setAdapter(catAdapter)
catSpinner.setSelection(catAdapter.getPosition("All"))
catSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
tagFilterIndex = position
filterOnTag()
}

View File

@ -23,8 +23,9 @@ object NotificationUtils {
val mNotificationManager = NotificationManagerCompat.from(context)
val channelGroups = listOf(
createGroupErrors(context),
createGroupNews(context))
createGroupErrors(context)
// createGroupNews(context)
)
mNotificationManager.createNotificationChannelGroupsCompat(channelGroups)
val channels = listOf(
@ -32,10 +33,13 @@ object NotificationUtils {
createChannelDownloading(context),
createChannelPlaying(context),
createChannelError(context),
createChannelSyncError(context),
createChannelEpisodeNotification(context))
createChannelSyncError(context)
// createChannelEpisodeNotification(context)
)
mNotificationManager.createNotificationChannelsCompat(channels)
mNotificationManager.deleteNotificationChannelGroup(GROUP_ID_NEWS)
mNotificationManager.deleteNotificationChannel(CHANNEL_ID_EPISODE_NOTIFICATIONS)
}
private fun createChannelUserAction(c: Context): NotificationChannelCompat {
@ -93,14 +97,14 @@ object NotificationUtils {
return notificationChannel.build()
}
private fun createChannelEpisodeNotification(c: Context): NotificationChannelCompat {
return NotificationChannelCompat.Builder(
CHANNEL_ID_EPISODE_NOTIFICATIONS, NotificationManagerCompat.IMPORTANCE_DEFAULT)
.setName(c.getString(R.string.notification_channel_new_episode))
.setDescription(c.getString(R.string.notification_channel_new_episode_description))
.setGroup(GROUP_ID_NEWS)
.build()
}
// private fun createChannelEpisodeNotification(c: Context): NotificationChannelCompat {
// return NotificationChannelCompat.Builder(
// CHANNEL_ID_EPISODE_NOTIFICATIONS, NotificationManagerCompat.IMPORTANCE_DEFAULT)
// .setName(c.getString(R.string.notification_channel_new_episode))
// .setDescription(c.getString(R.string.notification_channel_new_episode_description))
// .setGroup(GROUP_ID_NEWS)
// .build()
// }
private fun createGroupErrors(c: Context): NotificationChannelGroupCompat {
return NotificationChannelGroupCompat.Builder(GROUP_ID_ERRORS)
@ -108,9 +112,9 @@ object NotificationUtils {
.build()
}
private fun createGroupNews(c: Context): NotificationChannelGroupCompat {
return NotificationChannelGroupCompat.Builder(GROUP_ID_NEWS)
.setName(c.getString(R.string.notification_group_news))
.build()
}
// private fun createGroupNews(c: Context): NotificationChannelGroupCompat {
// return NotificationChannelGroupCompat.Builder(GROUP_ID_NEWS)
// .setName(c.getString(R.string.notification_group_news))
// .build()
// }
}

View File

@ -20,6 +20,7 @@
<com.google.android.material.chip.Chip
android:id="@+id/add_current_speed_chip"
android:gravity="center_vertical|start"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

View File

@ -10,11 +10,11 @@
android:menuCategory="container"
android:title="@string/keep_updated"
android:icon="@drawable/ic_refresh"/>
<item
android:id="@+id/notify_new_episodes"
android:menuCategory="container"
android:title="@string/episode_notification"
android:icon="@drawable/ic_notifications"/>
<!-- <item-->
<!-- android:id="@+id/notify_new_episodes"-->
<!-- android:menuCategory="container"-->
<!-- android:title="@string/episode_notification"-->
<!-- android:icon="@drawable/ic_notifications"/>-->
<item
android:id="@+id/autodownload"
android:menuCategory="container"

View File

@ -8,13 +8,13 @@
android:summary="@string/keep_updated_summary"
android:title="@string/keep_updated" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:dependency="keepUpdated"
android:icon="@drawable/ic_notifications"
android:key="episodeNotification"
android:summary="@string/episode_notification_summary"
android:title="@string/episode_notification" />
<!-- <SwitchPreferenceCompat-->
<!-- android:defaultValue="false"-->
<!-- android:dependency="keepUpdated"-->
<!-- android:icon="@drawable/ic_notifications"-->
<!-- android:key="episodeNotification"-->
<!-- android:summary="@string/episode_notification_summary"-->
<!-- android:title="@string/episode_notification" />-->
<Preference
android:icon="@drawable/ic_key"

View File

@ -254,3 +254,10 @@
* streamed media is added to queue and is resumed after restart
* changes in text of share feed link
* disabled some undesired deep links
## 4.7.1
* enabled speed setting for podcast in video player
* fixed bug of receiving null view in function requiring non-null in subscriptions page
* set Counter default to number of SHOW_UNPLAYED
* removed FeedCounter.SHOW_NEW, NewEpisodesNotification, and associated notifications settings

View File

@ -0,0 +1,7 @@
Version 4.7.1 brings several changes:
* enabled speed setting for podcast in video player
* fixed bug of receiving null view in function requiring non-null in subscriptions page
* set Counter default to number of SHOW_UNPLAYED
* removed FeedCounter.SHOW_NEW, NewEpisodesNotification, and associated notifications settings