changes in click handling and refresh bug fix
|
@ -22,8 +22,8 @@ android {
|
|||
// Version code schema:
|
||||
// "1.2.3-beta4" -> 1020304
|
||||
// "1.2.3" -> 1020395
|
||||
versionCode 3020103
|
||||
versionName "4.2.0"
|
||||
versionCode 3020104
|
||||
versionName "4.2.1"
|
||||
|
||||
def commit = ""
|
||||
try {
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
|
|
|
@ -30,6 +30,8 @@ import ac.mdiq.podcini.ui.gui.NotificationUtils
|
|||
import ac.mdiq.podcini.storage.model.download.DownloadError
|
||||
import ac.mdiq.podcini.storage.model.download.DownloadResult
|
||||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import java.util.*
|
||||
|
||||
class FeedUpdateWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
|
@ -107,11 +109,7 @@ class FeedUpdateWorker(context: Context, params: WorkerParameters) : Worker(cont
|
|||
}
|
||||
|
||||
@UnstableApi private fun refreshFeeds(toUpdate: MutableList<Feed>, force: Boolean) {
|
||||
while (toUpdate.isNotEmpty()) {
|
||||
if (isStopped) {
|
||||
return
|
||||
}
|
||||
if (ActivityCompat.checkSelfPermission(this.applicationContext,
|
||||
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(this.applicationContext,
|
||||
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
// TODO: Consider calling
|
||||
// ActivityCompat#requestPermissions
|
||||
|
@ -120,6 +118,13 @@ class FeedUpdateWorker(context: Context, params: WorkerParameters) : Worker(cont
|
|||
// int[] grantResults)
|
||||
// to handle the case where the user grants the permission. See the documentation
|
||||
// for ActivityCompat#requestPermissions for more details.
|
||||
Log.e(TAG, "refreshFeeds: require POST_NOTIFICATIONS permission")
|
||||
// Toast.makeText(applicationContext, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
|
||||
return
|
||||
}
|
||||
|
||||
while (toUpdate.isNotEmpty()) {
|
||||
if (isStopped) {
|
||||
return
|
||||
}
|
||||
notificationManager.notify(R.id.notification_updating_feeds, createNotification(toUpdate))
|
||||
|
@ -187,9 +192,9 @@ class FeedUpdateWorker(context: Context, params: WorkerParameters) : Worker(cont
|
|||
newEpisodesNotification.showIfNeeded(applicationContext, feedSyncTask.savedFeed!!)
|
||||
if (!request.source.isNullOrEmpty()) {
|
||||
if (!downloader.permanentRedirectUrl.isNullOrEmpty()) {
|
||||
DBWriter.updateFeedDownloadURL(request.source!!, downloader.permanentRedirectUrl!!)
|
||||
DBWriter.updateFeedDownloadURL(request.source, downloader.permanentRedirectUrl!!)
|
||||
} else if (feedSyncTask.redirectUrl.isNotEmpty() && feedSyncTask.redirectUrl != request.source) {
|
||||
DBWriter.updateFeedDownloadURL(request.source!!, feedSyncTask.redirectUrl)
|
||||
DBWriter.updateFeedDownloadURL(request.source, feedSyncTask.redirectUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import android.app.PendingIntent
|
|||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.work.Data
|
||||
|
@ -79,8 +80,8 @@ class EpisodeDownloadWorker(context: Context, params: WorkerParameters) : Worker
|
|||
e.printStackTrace()
|
||||
result = Result.failure()
|
||||
}
|
||||
if (result == Result.failure() && downloader != null) {
|
||||
FileUtils.deleteQuietly(File(downloader!!.downloadRequest.destination))
|
||||
if (result == Result.failure() && downloader?.downloadRequest?.destination != null) {
|
||||
FileUtils.deleteQuietly(File(downloader!!.downloadRequest.destination!!))
|
||||
}
|
||||
progressUpdaterThread.interrupt()
|
||||
try {
|
||||
|
@ -102,9 +103,7 @@ class EpisodeDownloadWorker(context: Context, params: WorkerParameters) : Worker
|
|||
|
||||
override fun onStopped() {
|
||||
super.onStopped()
|
||||
if (downloader != null) {
|
||||
downloader!!.cancel()
|
||||
}
|
||||
downloader?.cancel()
|
||||
}
|
||||
|
||||
override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
|
||||
|
@ -112,7 +111,7 @@ class EpisodeDownloadWorker(context: Context, params: WorkerParameters) : Worker
|
|||
ForegroundInfo(R.id.notification_downloading, generateProgressNotification()))
|
||||
}
|
||||
|
||||
private fun performDownload(media: FeedMedia, request: DownloadRequest): Result {
|
||||
@OptIn(UnstableApi::class) private fun performDownload(media: FeedMedia, request: DownloadRequest): Result {
|
||||
val dest = File(request.destination)
|
||||
if (!dest.exists()) {
|
||||
try {
|
||||
|
@ -162,7 +161,7 @@ class EpisodeDownloadWorker(context: Context, params: WorkerParameters) : Worker
|
|||
if (status.reason == DownloadError.ERROR_HTTP_DATA_ERROR
|
||||
&& status.reasonDetailed.toInt() == 416) {
|
||||
Log.d(TAG, "Requested invalid range, restarting download from the beginning")
|
||||
FileUtils.deleteQuietly(File(downloader!!.downloadRequest.destination))
|
||||
if (downloader?.downloadRequest?.destination != null) FileUtils.deleteQuietly(File(downloader!!.downloadRequest.destination!!))
|
||||
sendMessage(request.title?:"", false)
|
||||
return retry3times()
|
||||
}
|
||||
|
@ -199,7 +198,7 @@ class EpisodeDownloadWorker(context: Context, params: WorkerParameters) : Worker
|
|||
EventBus.getDefault().post(MessageEvent(
|
||||
applicationContext.getString(
|
||||
if (retrying) R.string.download_error_retrying else R.string.download_error_not_retrying,
|
||||
episodeTitle), { ctx: Context -> MainActivityStarter(ctx!!).withDownloadLogsOpen().start() },
|
||||
episodeTitle), { ctx: Context -> MainActivityStarter(ctx).withDownloadLogsOpen().start() },
|
||||
applicationContext.getString(R.string.download_error_details)))
|
||||
}
|
||||
|
||||
|
@ -251,8 +250,7 @@ class EpisodeDownloadWorker(context: Context, params: WorkerParameters) : Worker
|
|||
applicationContext.resources.getQuantityString(R.plurals.downloads_left,
|
||||
progressCopy.size, progressCopy.size)
|
||||
}
|
||||
val builder = NotificationCompat.Builder(applicationContext,
|
||||
NotificationUtils.CHANNEL_ID_DOWNLOADING)
|
||||
val builder = NotificationCompat.Builder(applicationContext, NotificationUtils.CHANNEL_ID_DOWNLOADING)
|
||||
builder.setTicker(applicationContext.getString(R.string.download_notification_title_episodes))
|
||||
.setContentTitle(applicationContext.getString(R.string.download_notification_title_episodes))
|
||||
.setContentText(contentText)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ac.mdiq.podcini.service.download
|
||||
|
||||
import ac.mdiq.podcini.R
|
||||
import ac.mdiq.podcini.service.FeedUpdateWorker
|
||||
import android.Manifest
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
|
@ -19,6 +20,7 @@ import ac.mdiq.podcini.ui.gui.NotificationUtils
|
|||
import ac.mdiq.podcini.storage.model.feed.Feed
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedCounter
|
||||
import ac.mdiq.podcini.storage.database.PodDBAdapter
|
||||
import android.widget.Toast
|
||||
|
||||
class NewEpisodesNotification {
|
||||
private var countersBefore: Map<Long, Int>? = null
|
||||
|
@ -80,7 +82,7 @@ class NewEpisodesNotification {
|
|||
.setAutoCancel(true)
|
||||
.build()
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(context,
|
||||
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(context,
|
||||
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
// TODO: Consider calling
|
||||
// ActivityCompat#requestPermissions
|
||||
|
@ -89,6 +91,8 @@ class NewEpisodesNotification {
|
|||
// 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,
|
||||
|
@ -117,7 +121,7 @@ class NewEpisodesNotification {
|
|||
.setOnlyAlertOnce(true)
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
if (ActivityCompat.checkSelfPermission(context,
|
||||
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(context,
|
||||
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
// TODO: Consider calling
|
||||
// ActivityCompat#requestPermissions
|
||||
|
@ -126,6 +130,8 @@ class NewEpisodesNotification {
|
|||
// 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)
|
||||
|
|
|
@ -103,6 +103,7 @@ import ac.mdiq.podcini.preferences.UserPreferences.showNextChapterOnFullNotifica
|
|||
import ac.mdiq.podcini.preferences.UserPreferences.showPlaybackSpeedOnFullNotification
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.showSkipOnFullNotification
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.videoPlaybackSpeed
|
||||
import ac.mdiq.podcini.service.download.NewEpisodesNotification
|
||||
import ac.mdiq.podcini.ui.appstartintent.MainActivityStarter
|
||||
import ac.mdiq.podcini.ui.appstartintent.VideoPlayerActivityStarter
|
||||
import android.os.Build.VERSION_CODES
|
||||
|
@ -246,7 +247,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
|||
if (notificationBuilder.playerStatus == PlayerStatus.PLAYING) {
|
||||
notificationBuilder.playerStatus = PlayerStatus.STOPPED
|
||||
val notificationManager = NotificationManagerCompat.from(this)
|
||||
if (ActivityCompat.checkSelfPermission(this,
|
||||
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(this,
|
||||
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
// TODO: Consider calling
|
||||
// ActivityCompat#requestPermissions
|
||||
|
@ -255,6 +256,8 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
|||
// int[] grantResults)
|
||||
// to handle the case where the user grants the permission. See the documentation
|
||||
// for ActivityCompat#requestPermissions for more details.
|
||||
Log.e(TAG, "onDestroy: require POST_NOTIFICATIONS permission")
|
||||
Toast.makeText(applicationContext, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
|
||||
return
|
||||
}
|
||||
notificationManager.notify(R.id.notification_playing, notificationBuilder.build())
|
||||
|
@ -576,7 +579,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
|||
pendingIntentAlwaysAllow)
|
||||
.setAutoCancel(true)
|
||||
val notificationManager = NotificationManagerCompat.from(this)
|
||||
if (ActivityCompat.checkSelfPermission(this,
|
||||
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(this,
|
||||
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
// TODO: Consider calling
|
||||
// ActivityCompat#requestPermissions
|
||||
|
@ -585,6 +588,8 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
|||
// int[] grantResults)
|
||||
// to handle the case where the user grants the permission. See the documentation
|
||||
// for ActivityCompat#requestPermissions for more details.
|
||||
Log.e(TAG, "displayStreamingNotAllowedNotification: require POST_NOTIFICATIONS permission")
|
||||
Toast.makeText(applicationContext, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
|
||||
return
|
||||
}
|
||||
notificationManager.notify(R.id.notification_streaming_confirmation, builder.build())
|
||||
|
@ -1303,7 +1308,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
|||
notificationBuilder.updatePosition(currentPosition, currentPlaybackSpeed)
|
||||
|
||||
val notificationManager = NotificationManagerCompat.from(this)
|
||||
if (ActivityCompat.checkSelfPermission(this,
|
||||
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(this,
|
||||
Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
// TODO: Consider calling
|
||||
// ActivityCompat#requestPermissions
|
||||
|
@ -1312,6 +1317,8 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
|||
// int[] grantResults)
|
||||
// to handle the case where the user grants the permission. See the documentation
|
||||
// for ActivityCompat#requestPermissions for more details.
|
||||
Log.e(TAG, "setupNotification: require POST_NOTIFICATIONS permission")
|
||||
Toast.makeText(applicationContext, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
|
||||
return
|
||||
}
|
||||
notificationManager.notify(R.id.notification_playing, notificationBuilder.build())
|
||||
|
|
|
@ -37,10 +37,11 @@ open class AutomaticDownloadAlgorithm {
|
|||
@UnstableApi open fun autoDownloadUndownloadedItems(context: Context): Runnable? {
|
||||
return Runnable {
|
||||
// true if we should auto download based on network status
|
||||
val networkShouldAutoDl = (isAutoDownloadAllowed && isEnableAutodownload)
|
||||
val networkShouldAutoDl = (isAutoDownloadAllowed)
|
||||
// val networkShouldAutoDl = (isAutoDownloadAllowed && isEnableAutodownload)
|
||||
|
||||
// true if we should auto download based on power status
|
||||
val powerShouldAutoDl = (deviceCharging(context!!) || isEnableAutodownloadOnBattery)
|
||||
val powerShouldAutoDl = (deviceCharging(context) || isEnableAutodownloadOnBattery)
|
||||
|
||||
// we should only auto download if both network AND power are happy
|
||||
if (networkShouldAutoDl && powerShouldAutoDl) {
|
||||
|
@ -48,8 +49,7 @@ open class AutomaticDownloadAlgorithm {
|
|||
|
||||
val candidates: MutableList<FeedItem>
|
||||
val queue = getQueue()
|
||||
val newItems = getEpisodes(0, Int.MAX_VALUE,
|
||||
FeedItemFilter(FeedItemFilter.NEW), SortOrder.DATE_NEW_OLD)
|
||||
val newItems = getEpisodes(0, Int.MAX_VALUE, FeedItemFilter(FeedItemFilter.NEW), SortOrder.DATE_NEW_OLD)
|
||||
candidates = ArrayList(queue.size + newItems.size)
|
||||
candidates.addAll(queue)
|
||||
for (newItem in newItems) {
|
||||
|
|
|
@ -75,10 +75,12 @@ object DBReader {
|
|||
fun buildTags() {
|
||||
val tagsSet = mutableSetOf<String>()
|
||||
for (feed in feeds) {
|
||||
if (feed.preferences != null) {
|
||||
for (tag in feed.preferences!!.getTags()) {
|
||||
if (tag != TAG_ROOT) tagsSet.add(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
tags.clear()
|
||||
tags.addAll(tagsSet)
|
||||
tags.sort()
|
||||
|
@ -177,18 +179,23 @@ object DBReader {
|
|||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
try {
|
||||
adapter.getItemsOfFeedCursor(feed!!, filter).use { cursor ->
|
||||
if (feed != null) {
|
||||
adapter.getItemsOfFeedCursor(feed, filter).use { cursor ->
|
||||
val items = extractItemlistFromCursor(adapter, cursor).toMutableList()
|
||||
getPermutor(sortOrder!!).reorder(items)
|
||||
if (sortOrder != null) getPermutor(sortOrder).reorder(items)
|
||||
feed.items = items
|
||||
for (item in items) {
|
||||
item.feed = feed
|
||||
}
|
||||
return items
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "getFeedItemList feed is null")
|
||||
}
|
||||
} finally {
|
||||
adapter.close()
|
||||
}
|
||||
return listOf()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
@ -204,7 +211,8 @@ object DBReader {
|
|||
}
|
||||
|
||||
private fun extractItemlistFromCursor(adapter: PodDBAdapter?, cursor: Cursor?): List<FeedItem> {
|
||||
val result: MutableList<FeedItem> = ArrayList(cursor!!.count)
|
||||
if (cursor == null) return listOf()
|
||||
val result: MutableList<FeedItem> = ArrayList(cursor.count)
|
||||
if (cursor.moveToFirst()) {
|
||||
val indexMediaId = cursor.getColumnIndexOrThrow(PodDBAdapter.SELECT_KEY_MEDIA_ID)
|
||||
do {
|
||||
|
@ -228,7 +236,7 @@ object DBReader {
|
|||
@JvmStatic
|
||||
fun getQueue(adapter: PodDBAdapter?): List<FeedItem> {
|
||||
// Log.d(TAG, "getQueue()")
|
||||
adapter!!.queueCursor.use { cursor ->
|
||||
adapter?.queueCursor.use { cursor ->
|
||||
val items = extractItemlistFromCursor(adapter, cursor)
|
||||
loadAdditionalFeedItemListData(items)
|
||||
return items
|
||||
|
@ -253,13 +261,14 @@ object DBReader {
|
|||
}
|
||||
|
||||
private fun getQueueIDList(adapter: PodDBAdapter?): LongList {
|
||||
adapter!!.queueIDCursor.use { cursor ->
|
||||
adapter?.queueIDCursor?.use { cursor ->
|
||||
val queueIds = LongList(cursor.count)
|
||||
while (cursor.moveToNext()) {
|
||||
queueIds.add(cursor.getLong(0))
|
||||
}
|
||||
return queueIds
|
||||
}
|
||||
return LongList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
@ -490,7 +499,7 @@ object DBReader {
|
|||
Log.d(TAG, "Loading feeditem with id $itemId")
|
||||
|
||||
var item: FeedItem? = null
|
||||
adapter!!.getFeedItemCursor(itemId.toString()).use { cursor ->
|
||||
adapter?.getFeedItemCursor(itemId.toString())?.use { cursor ->
|
||||
if (cursor.moveToNext()) {
|
||||
val list = extractItemlistFromCursor(adapter, cursor)
|
||||
if (list.isNotEmpty()) {
|
||||
|
@ -500,6 +509,7 @@ object DBReader {
|
|||
}
|
||||
return item
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -577,7 +587,8 @@ object DBReader {
|
|||
private fun getFeedItemByGuidOrEpisodeUrl(guid: String?, episodeUrl: String,
|
||||
adapter: PodDBAdapter?
|
||||
): FeedItem? {
|
||||
adapter!!.getFeedItemCursor(guid, episodeUrl).use { cursor ->
|
||||
if (adapter != null) {
|
||||
adapter.getFeedItemCursor(guid, episodeUrl).use { cursor ->
|
||||
if (!cursor.moveToNext()) {
|
||||
return null
|
||||
}
|
||||
|
@ -588,6 +599,8 @@ object DBReader {
|
|||
return null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns credentials based on image URL
|
||||
|
@ -608,8 +621,9 @@ object DBReader {
|
|||
}
|
||||
|
||||
private fun getImageAuthentication(imageUrl: String, adapter: PodDBAdapter?): String {
|
||||
var credentials: String
|
||||
adapter!!.getImageAuthenticationCursor(imageUrl).use { cursor ->
|
||||
var credentials: String = ""
|
||||
if (adapter != null) {
|
||||
adapter.getImageAuthenticationCursor(imageUrl).use { cursor ->
|
||||
if (cursor.moveToFirst()) {
|
||||
val username = cursor.getString(0)
|
||||
val password = cursor.getString(1)
|
||||
|
@ -622,6 +636,7 @@ object DBReader {
|
|||
credentials = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
return credentials
|
||||
}
|
||||
|
||||
|
@ -696,7 +711,8 @@ object DBReader {
|
|||
}
|
||||
|
||||
private fun loadChaptersOfFeedItem(adapter: PodDBAdapter?, item: FeedItem): List<Chapter>? {
|
||||
adapter!!.getSimpleChaptersOfFeedItemCursor(item).use { cursor ->
|
||||
if (adapter != null) {
|
||||
adapter.getSimpleChaptersOfFeedItemCursor(item).use { cursor ->
|
||||
val chaptersCount = cursor.count
|
||||
if (chaptersCount == 0) {
|
||||
item.chapters = null
|
||||
|
@ -709,6 +725,8 @@ object DBReader {
|
|||
return chapters
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the DB for a FeedMedia of the given id.
|
||||
|
@ -746,15 +764,18 @@ object DBReader {
|
|||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
try {
|
||||
adapter.getFeedItemCursorByUrl(urls!!).use { itemCursor ->
|
||||
if (urls != null) {
|
||||
adapter.getFeedItemCursorByUrl(urls).use { itemCursor ->
|
||||
val items = extractItemlistFromCursor(adapter, itemCursor).toMutableList()
|
||||
loadAdditionalFeedItemListData(items)
|
||||
items.sortWith(PlaybackCompletionDateComparator())
|
||||
return items
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
adapter.close()
|
||||
}
|
||||
return listOf()
|
||||
}
|
||||
|
||||
fun getMonthlyTimeStatistics(): List<MonthlyStatisticsItem> {
|
||||
|
@ -861,7 +882,7 @@ object DBReader {
|
|||
// reverse natural order: podcast with most unplayed episodes first
|
||||
return@Comparator -1
|
||||
} else if (counterLhs == counterRhs) {
|
||||
return@Comparator lhs.title!!.compareTo(rhs.title!!, ignoreCase = true)
|
||||
return@Comparator lhs.title?.compareTo(rhs.title!!, ignoreCase = true) ?: -1
|
||||
} else {
|
||||
return@Comparator 1
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ import java.util.concurrent.*
|
|||
media.setDownloaded(false)
|
||||
media.setFile_url(null)
|
||||
DBWriter.setFeedMedia(media)
|
||||
EventBus.getDefault().post(updated(media.getItem()!!))
|
||||
if (media.getItem() != null) EventBus.getDefault().post(updated(media.getItem()!!))
|
||||
EventBus.getDefault().post(ac.mdiq.podcini.util.event.MessageEvent(context.getString(R.string.error_file_not_found)))
|
||||
}
|
||||
|
||||
|
@ -153,7 +153,8 @@ import java.util.concurrent.*
|
|||
* Get a FeedItem by its identifying value.
|
||||
*/
|
||||
private fun searchFeedItemByIdentifyingValue(items: List<FeedItem>?, searchItem: FeedItem): FeedItem? {
|
||||
for (item in items!!) {
|
||||
if (items == null) return null
|
||||
for (item in items) {
|
||||
if (TextUtils.equals(item.identifyingValue, searchItem.identifyingValue)) {
|
||||
return item
|
||||
}
|
||||
|
@ -166,7 +167,8 @@ import java.util.concurrent.*
|
|||
* This is to work around podcasters breaking their GUIDs.
|
||||
*/
|
||||
private fun searchFeedItemGuessDuplicate(items: List<FeedItem>?, searchItem: FeedItem): FeedItem? {
|
||||
for (item in items!!) {
|
||||
if (items == null) return null
|
||||
for (item in items) {
|
||||
if (FeedItemDuplicateGuesser.seemDuplicates(item, searchItem)) {
|
||||
return item
|
||||
}
|
||||
|
@ -220,7 +222,7 @@ import java.util.concurrent.*
|
|||
Log.d(TAG, "New feed has a higher page number.")
|
||||
savedFeed.nextPageLink = newFeed.nextPageLink
|
||||
}
|
||||
if (savedFeed.preferences!!.compareWithOther(newFeed.preferences)) {
|
||||
if (savedFeed.preferences != null && savedFeed.preferences!!.compareWithOther(newFeed.preferences)) {
|
||||
Log.d(TAG, "Feed has updated preferences. Updating old feed's preferences")
|
||||
savedFeed.preferences!!.updateFromOther(newFeed.preferences)
|
||||
}
|
||||
|
@ -240,7 +242,7 @@ import java.util.concurrent.*
|
|||
if (!newFeed.isLocalFeed && possibleDuplicate != null && item !== possibleDuplicate) {
|
||||
// Canonical episode is the first one returned (usually oldest)
|
||||
DBWriter.addDownloadStatus(DownloadResult(savedFeed,
|
||||
item.title!!, DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
|
||||
item.title?:"", DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
|
||||
"""
|
||||
The podcast host appears to have added the same episode twice. Podcini still refreshed the feed and attempted to repair it.
|
||||
|
||||
|
@ -259,7 +261,7 @@ import java.util.concurrent.*
|
|||
if (oldItem != null) {
|
||||
Log.d(TAG, "Repaired duplicate: $oldItem, $item")
|
||||
DBWriter.addDownloadStatus(DownloadResult(savedFeed,
|
||||
item.title!!, DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
|
||||
item.title?:"", DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false,
|
||||
"""
|
||||
The podcast host changed the ID of an existing episode instead of just updating the episode itself. Podcini still refreshed the feed and attempted to repair it.
|
||||
|
||||
|
@ -381,12 +383,14 @@ import java.util.concurrent.*
|
|||
fun searchFeedItems(feedID: Long, query: String): FutureTask<List<FeedItem>> {
|
||||
return FutureTask(object : QueryTask<List<FeedItem>>() {
|
||||
override fun execute(adapter: PodDBAdapter?) {
|
||||
val searchResult = adapter!!.searchItems(feedID, query)
|
||||
val searchResult = adapter?.searchItems(feedID, query)
|
||||
if (searchResult != null) {
|
||||
val items = extractItemlistFromCursor(searchResult)
|
||||
loadAdditionalFeedItemListData(items)
|
||||
setResult(items)
|
||||
searchResult.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -394,7 +398,8 @@ import java.util.concurrent.*
|
|||
fun searchFeeds(query: String): FutureTask<List<Feed>> {
|
||||
return FutureTask(object : QueryTask<List<Feed>>() {
|
||||
override fun execute(adapter: PodDBAdapter?) {
|
||||
val cursor = adapter!!.searchFeeds(query)
|
||||
if (adapter != null) {
|
||||
val cursor = adapter.searchFeeds(query)
|
||||
val items: MutableList<Feed> = ArrayList()
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
|
@ -404,6 +409,7 @@ import java.util.concurrent.*
|
|||
setResult(items)
|
||||
cursor.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -93,8 +93,9 @@ import java.util.concurrent.TimeUnit
|
|||
if (media != null) {
|
||||
val result = deleteFeedMediaSynchronous(context, media)
|
||||
|
||||
if (result && shouldDeleteRemoveFromQueue()) {
|
||||
removeQueueItemSynchronous(context, false, media.getItem()!!.id)
|
||||
val item = media.getItem()
|
||||
if (result && item != null && shouldDeleteRemoveFromQueue()) {
|
||||
removeQueueItemSynchronous(context, false, item.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +105,8 @@ import java.util.concurrent.TimeUnit
|
|||
Log.i(TAG, String.format(Locale.US, "Requested to delete FeedMedia [id=%d, title=%s, downloaded=%s",
|
||||
media.id, media.getEpisodeTitle(), media.isDownloaded()))
|
||||
var localDelete = false
|
||||
if (media.getFile_url() != null && media.getFile_url()!!.startsWith("content://")) {
|
||||
val url = media.getFile_url()
|
||||
if (url != null && url.startsWith("content://")) {
|
||||
// Local feed
|
||||
val documentFile = DocumentFile.fromSingleUri(context, Uri.parse(media.getFile_url()))
|
||||
if (documentFile == null || !documentFile.exists() || !documentFile.delete()) {
|
||||
|
@ -113,9 +115,9 @@ import java.util.concurrent.TimeUnit
|
|||
}
|
||||
media.setFile_url(null)
|
||||
localDelete = true
|
||||
} else if (media.getFile_url() != null) {
|
||||
} else if (url != null) {
|
||||
// delete downloaded media file
|
||||
val mediaFile = File(media.getFile_url()!!)
|
||||
val mediaFile = File(url)
|
||||
if (mediaFile.exists() && !mediaFile.delete()) {
|
||||
val evt = MessageEvent(context.getString(R.string.delete_failed))
|
||||
EventBus.getDefault().post(evt)
|
||||
|
@ -138,13 +140,13 @@ import java.util.concurrent.TimeUnit
|
|||
nm.cancel(R.id.notification_playing)
|
||||
}
|
||||
|
||||
if (localDelete) {
|
||||
// Do full update of this feed to get rid of the item
|
||||
updateFeed(media.getItem()!!.feed!!, context.applicationContext, null)
|
||||
} else {
|
||||
// Gpodder: queue delete action for synchronization
|
||||
val item = media.getItem()
|
||||
if (item != null) {
|
||||
if (localDelete) {
|
||||
// Do full update of this feed to get rid of the item
|
||||
if (item.feed != null) updateFeed(item.feed!!, context.applicationContext, null)
|
||||
} else {
|
||||
// Gpodder: queue delete action for synchronization
|
||||
val action = EpisodeAction.Builder(item, EpisodeAction.DELETE)
|
||||
.currentTimestamp()
|
||||
.build()
|
||||
|
@ -287,8 +289,9 @@ import java.util.concurrent.TimeUnit
|
|||
@JvmOverloads
|
||||
fun addItemToPlaybackHistory(media: FeedMedia?, date: Date? = Date()): Future<*> {
|
||||
return runOnDbThread {
|
||||
if (media != null) {
|
||||
Log.d(TAG, "Adding item to playback history")
|
||||
media!!.setPlaybackCompletionDate(date)
|
||||
media.setPlaybackCompletionDate(date)
|
||||
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
|
@ -297,6 +300,7 @@ import java.util.concurrent.TimeUnit
|
|||
EventBus.getDefault().post(PlaybackHistoryEvent.listUpdated())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Download status object to the download log.
|
||||
|
@ -305,13 +309,15 @@ import java.util.concurrent.TimeUnit
|
|||
*/
|
||||
fun addDownloadStatus(status: DownloadResult?): Future<*> {
|
||||
return runOnDbThread {
|
||||
if (status != null) {
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
adapter.setDownloadStatus(status!!)
|
||||
adapter.setDownloadStatus(status)
|
||||
adapter.close()
|
||||
EventBus.getDefault().post(DownloadLogEvent.listUpdated())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a FeedItem in the queue at the specified index. The 'read'-attribute of the FeedItem will be set to
|
||||
|
@ -466,9 +472,10 @@ import java.util.concurrent.TimeUnit
|
|||
// do not shuffle the list on every change
|
||||
return
|
||||
}
|
||||
val permutor = getPermutor(sortOrder!!)
|
||||
if (sortOrder != null) {
|
||||
val permutor = getPermutor(sortOrder)
|
||||
permutor.reorder(queue)
|
||||
|
||||
}
|
||||
// Replace ADDED events by a single SORTED event
|
||||
events.clear()
|
||||
events.add(QueueEvent.sorted(queue))
|
||||
|
@ -610,9 +617,7 @@ import java.util.concurrent.TimeUnit
|
|||
* @param itemId The item to move to the bottom of the queue
|
||||
* @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to
|
||||
*/
|
||||
fun moveQueueItemToBottom(itemId: Long,
|
||||
broadcastUpdate: Boolean
|
||||
): Future<*> {
|
||||
fun moveQueueItemToBottom(itemId: Long, broadcastUpdate: Boolean): Future<*> {
|
||||
return runOnDbThread {
|
||||
val queueIdList = getQueueIDList()
|
||||
val index = queueIdList.indexOf(itemId)
|
||||
|
@ -678,12 +683,14 @@ import java.util.concurrent.TimeUnit
|
|||
|
||||
fun resetPagedFeedPage(feed: Feed?): Future<*> {
|
||||
return runOnDbThread {
|
||||
if (feed != null) {
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
adapter.resetPagedFeedPage(feed!!)
|
||||
adapter.resetPagedFeedPage(feed)
|
||||
adapter.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the 'read'-attribute of all specified FeedItems
|
||||
|
@ -726,7 +733,7 @@ import java.util.concurrent.TimeUnit
|
|||
* @param resetMediaPosition true if this method should also reset the position of the FeedItem's FeedMedia object.
|
||||
*/
|
||||
fun markItemPlayed(item: FeedItem, played: Int, resetMediaPosition: Boolean): Future<*> {
|
||||
val mediaId = if ((item.hasMedia())) item.media!!.id else 0
|
||||
val mediaId = if (item.media != null) item.media!!.id else 0
|
||||
return markItemPlayed(item.id, played, mediaId, resetMediaPosition)
|
||||
}
|
||||
|
||||
|
@ -834,12 +841,14 @@ import java.util.concurrent.TimeUnit
|
|||
@JvmStatic
|
||||
fun setFeedMediaPlaybackInformation(media: FeedMedia?): Future<*> {
|
||||
return runOnDbThread {
|
||||
if (media != null) {
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
adapter.setFeedMediaPlaybackInformation(media!!)
|
||||
adapter.setFeedMediaPlaybackInformation(media)
|
||||
adapter.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a FeedItem object in the database. This method will save all attributes of the FeedItem object including
|
||||
|
@ -850,13 +859,15 @@ import java.util.concurrent.TimeUnit
|
|||
@JvmStatic
|
||||
fun setFeedItem(item: FeedItem?): Future<*> {
|
||||
return runOnDbThread {
|
||||
if (item != null) {
|
||||
val adapter = getInstance()
|
||||
adapter.open()
|
||||
adapter.setSingleFeedItem(item!!)
|
||||
adapter.setSingleFeedItem(item)
|
||||
adapter.close()
|
||||
EventBus.getDefault().post(updated(item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates download URL of a feed
|
||||
|
@ -893,7 +904,7 @@ import java.util.concurrent.TimeUnit
|
|||
private fun indexInItemList(items: List<FeedItem?>, itemId: Long): Int {
|
||||
for (i in items.indices) {
|
||||
val item = items[i]
|
||||
if (item!!.id == itemId) {
|
||||
if (item?.id == itemId) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,11 +21,15 @@ import ac.mdiq.podcini.ui.appstartintent.MainActivityStarter
|
|||
import ac.mdiq.podcini.ui.common.ThemeUtils.getDrawableFromAttr
|
||||
import ac.mdiq.podcini.ui.dialog.RatingDialog
|
||||
import ac.mdiq.podcini.ui.fragment.*
|
||||
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
||||
import ac.mdiq.podcini.ui.view.LockableBottomSheetBehavior
|
||||
import ac.mdiq.podcini.util.event.EpisodeDownloadEvent
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.media.AudioManager
|
||||
import android.net.Uri
|
||||
|
@ -38,6 +42,8 @@ import android.view.MenuItem
|
|||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.ViewCompat
|
||||
|
@ -54,6 +60,7 @@ import com.bumptech.glide.Glide
|
|||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.apache.commons.lang3.ArrayUtils
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
@ -107,6 +114,11 @@ class MainActivity : CastEnabledActivity() {
|
|||
|
||||
mainView = findViewById(R.id.main_view)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 33 && checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
// Toast.makeText(this, R.string.notification_permission_text, Toast.LENGTH_LONG).show()
|
||||
requestPostNotificationPermission()
|
||||
}
|
||||
|
||||
// Consume navigation bar insets - we apply them in setPlayerVisible()
|
||||
ViewCompat.setOnApplyWindowInsetsListener(mainView) { _: View?, insets: WindowInsetsCompat ->
|
||||
navigationBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
|
@ -219,6 +231,22 @@ class MainActivity : CastEnabledActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
fun requestPostNotificationPermission() {
|
||||
if (Build.VERSION.SDK_INT >= 33) requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
|
||||
private val requestPermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setMessage(R.string.notification_permission_text)
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> {} }
|
||||
.setNegativeButton(R.string.cancel_label) { _: DialogInterface?, _: Int -> finish() }
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
updateInsets()
|
||||
|
@ -331,12 +359,12 @@ class MainActivity : CastEnabledActivity() {
|
|||
val fragment: Fragment
|
||||
when (tag) {
|
||||
QueueFragment.TAG -> fragment = QueueFragment()
|
||||
// InboxFragment.TAG -> fragment = InboxFragment()
|
||||
AllEpisodesFragment.TAG -> fragment = AllEpisodesFragment()
|
||||
CompletedDownloadsFragment.TAG -> fragment = CompletedDownloadsFragment()
|
||||
PlaybackHistoryFragment.TAG -> fragment = PlaybackHistoryFragment()
|
||||
AddFeedFragment.TAG -> fragment = AddFeedFragment()
|
||||
SubscriptionFragment.TAG -> fragment = SubscriptionFragment()
|
||||
StatisticsFragment.TAG -> fragment = StatisticsFragment()
|
||||
else -> {
|
||||
// default to subscriptions screen
|
||||
fragment = SubscriptionFragment()
|
||||
|
@ -451,6 +479,7 @@ class MainActivity : CastEnabledActivity() {
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
handleNavIntent()
|
||||
RatingDialog.check()
|
||||
|
||||
|
@ -633,6 +662,7 @@ class MainActivity : CastEnabledActivity() {
|
|||
"EPISODES" -> loadFragment(AllEpisodesFragment.TAG, null)
|
||||
"QUEUE" -> loadFragment(QueueFragment.TAG, null)
|
||||
"SUBSCRIPTIONS" -> loadFragment(SubscriptionFragment.TAG, null)
|
||||
"STATISTCS" -> loadFragment(StatisticsFragment.TAG, null)
|
||||
else -> {
|
||||
showSnackbarAbovePlayer(getString(R.string.app_action_not_found)+feature, Snackbar.LENGTH_LONG)
|
||||
return
|
||||
|
|
|
@ -71,7 +71,30 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) :
|
|||
val item: FeedItem = episodes[pos]
|
||||
holder.bind(item)
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
holder.coverHolder.setOnCreateContextMenuListener(this)
|
||||
holder.coverHolder.setOnLongClickListener {
|
||||
longPressedItem = item
|
||||
longPressedPosition = holder.bindingAdapterPosition
|
||||
startSelectMode(longPressedPosition)
|
||||
false
|
||||
}
|
||||
holder.coverHolder.setOnClickListener {
|
||||
if (inActionMode()) {
|
||||
toggleSelection(holder.bindingAdapterPosition)
|
||||
} else {
|
||||
longPressedItem = item
|
||||
longPressedPosition = holder.bindingAdapterPosition
|
||||
it.showContextMenu()
|
||||
}
|
||||
}
|
||||
|
||||
// holder.infoCard.setOnCreateContextMenuListener(this)
|
||||
// holder.infoCard.setOnLongClickListener {
|
||||
// longPressedItem = item
|
||||
// longPressedPosition = holder.bindingAdapterPosition
|
||||
// false
|
||||
// }
|
||||
holder.infoCard.setOnClickListener {
|
||||
val activity: MainActivity? = mainActivityRef.get()
|
||||
if (!inActionMode()) {
|
||||
val ids: LongArray = FeedItemUtil.getIds(episodes)
|
||||
|
@ -81,12 +104,6 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) :
|
|||
toggleSelection(holder.bindingAdapterPosition)
|
||||
}
|
||||
}
|
||||
holder.itemView.setOnCreateContextMenuListener(this)
|
||||
holder.itemView.setOnLongClickListener {
|
||||
longPressedItem = item
|
||||
longPressedPosition = holder.bindingAdapterPosition
|
||||
false
|
||||
}
|
||||
holder.itemView.setOnTouchListener(View.OnTouchListener { _: View?, e: MotionEvent ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (e.isFromSource(InputDevice.SOURCE_MOUSE) && e.buttonState == MotionEvent.BUTTON_SECONDARY) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import ac.mdiq.podcini.preferences.UserPreferences.subscriptionsFilter
|
|||
import ac.mdiq.podcini.storage.NavDrawerData.FeedDrawerItem
|
||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
||||
import ac.mdiq.podcini.ui.fragment.*
|
||||
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
||||
import android.app.Activity
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
|
@ -96,12 +97,11 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
private fun getDrawable(tag: String?): Int {
|
||||
return when (tag) {
|
||||
QueueFragment.TAG -> R.drawable.ic_playlist_play
|
||||
// InboxFragment.TAG -> R.drawable.ic_inbox
|
||||
AllEpisodesFragment.TAG -> R.drawable.ic_feed
|
||||
CompletedDownloadsFragment.TAG -> R.drawable.ic_download
|
||||
PlaybackHistoryFragment.TAG -> R.drawable.ic_history
|
||||
SubscriptionFragment.TAG -> R.drawable.ic_subscriptions
|
||||
// StatisticsFragment.TAG -> R.drawable.ic_sta
|
||||
StatisticsFragment.TAG -> R.drawable.ic_chart_box
|
||||
AddFeedFragment.TAG -> R.drawable.ic_add
|
||||
else -> 0
|
||||
}
|
||||
|
@ -260,7 +260,7 @@ class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) :
|
|||
}
|
||||
|
||||
private fun bindSectionDivider(holder: DividerHolder) {
|
||||
val context = activity.get() ?: return
|
||||
// val context = activity.get() ?: return
|
||||
|
||||
if (subscriptionsFilter.isEnabled && showSubscriptionList) {
|
||||
holder.itemView.isEnabled = true
|
||||
|
|
|
@ -33,7 +33,7 @@ open class QueueRecyclerAdapter(mainActivity: MainActivity, private val swipeAct
|
|||
if (!dragDropEnabled) {
|
||||
holder.dragHandle.setVisibility(View.GONE)
|
||||
holder.dragHandle.setOnTouchListener(null)
|
||||
holder.coverHolder.setOnTouchListener(null)
|
||||
// holder.coverHolder.setOnTouchListener(null)
|
||||
} else {
|
||||
holder.dragHandle.setVisibility(View.VISIBLE)
|
||||
holder.dragHandle.setOnTouchListener { _: View?, event: MotionEvent ->
|
||||
|
@ -59,7 +59,7 @@ open class QueueRecyclerAdapter(mainActivity: MainActivity, private val swipeAct
|
|||
}
|
||||
if (inActionMode()) {
|
||||
holder.dragHandle.setOnTouchListener(null)
|
||||
holder.coverHolder.setOnTouchListener(null)
|
||||
// holder.coverHolder.setOnTouchListener(null)
|
||||
}
|
||||
|
||||
holder.isInQueue.setVisibility(View.GONE)
|
||||
|
@ -71,7 +71,7 @@ open class QueueRecyclerAdapter(mainActivity: MainActivity, private val swipeAct
|
|||
super.onCreateContextMenu(menu, v, menuInfo)
|
||||
|
||||
if (!inActionMode()) {
|
||||
menu.findItem(R.id.multi_select).setVisible(true)
|
||||
// menu.findItem(R.id.multi_select).setVisible(true)
|
||||
val keepSorted: Boolean = UserPreferences.isQueueKeepSorted
|
||||
if (getItem(0)?.id === longPressedItem?.id || keepSorted) {
|
||||
menu.findItem(R.id.move_to_top_item).setVisible(false)
|
||||
|
|
|
@ -54,15 +54,13 @@ open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) :
|
|||
@UnstableApi override fun onBindViewHolder(holder: SubscriptionViewHolder, position: Int) {
|
||||
val drawerItem: NavDrawerData.FeedDrawerItem = listItems[position]
|
||||
holder.bind(drawerItem)
|
||||
holder.itemView.setOnCreateContextMenuListener(this)
|
||||
if (inActionMode()) {
|
||||
holder.selectCheckbox.visibility = View.VISIBLE
|
||||
holder.selectView.visibility = View.VISIBLE
|
||||
|
||||
holder.selectCheckbox.setChecked((isSelected(position)))
|
||||
holder.selectCheckbox.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
setSelected(holder.bindingAdapterPosition,
|
||||
isChecked)
|
||||
setSelected(holder.bindingAdapterPosition, isChecked)
|
||||
}
|
||||
holder.coverImage.alpha = 0.6f
|
||||
holder.count.visibility = View.GONE
|
||||
|
@ -71,13 +69,30 @@ open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) :
|
|||
holder.coverImage.alpha = 1.0f
|
||||
}
|
||||
|
||||
holder.itemView.setOnLongClickListener {
|
||||
if (!inActionMode()) {
|
||||
holder.coverImage.setOnCreateContextMenuListener(this)
|
||||
holder.coverImage.setOnLongClickListener {
|
||||
longPressedPosition = holder.bindingAdapterPosition
|
||||
selectedItem = drawerItem
|
||||
}
|
||||
startSelectMode(longPressedPosition)
|
||||
false
|
||||
}
|
||||
holder.coverImage.setOnClickListener {
|
||||
if (inActionMode()) {
|
||||
holder.selectCheckbox.setChecked(!isSelected(holder.bindingAdapterPosition))
|
||||
} else {
|
||||
longPressedPosition = holder.bindingAdapterPosition
|
||||
selectedItem = drawerItem
|
||||
it.showContextMenu()
|
||||
}
|
||||
}
|
||||
// holder.infoCard.setOnCreateContextMenuListener(this)
|
||||
// holder.infoCard.setOnLongClickListener {
|
||||
// if (!inActionMode()) {
|
||||
// longPressedPosition = holder.bindingAdapterPosition
|
||||
// selectedItem = drawerItem
|
||||
// }
|
||||
// false
|
||||
// }
|
||||
|
||||
holder.itemView.setOnTouchListener { _: View?, e: MotionEvent ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
|
@ -95,8 +110,7 @@ open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) :
|
|||
if (inActionMode()) {
|
||||
holder.selectCheckbox.setChecked(!isSelected(holder.bindingAdapterPosition))
|
||||
} else {
|
||||
val fragment: Fragment = FeedItemlistFragment
|
||||
.newInstance(drawerItem.feed.id)
|
||||
val fragment: Fragment = FeedItemlistFragment.newInstance(drawerItem.feed.id)
|
||||
mainActivityRef.get()?.loadChildFragment(fragment)
|
||||
}
|
||||
}
|
||||
|
@ -114,14 +128,19 @@ open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) :
|
|||
}
|
||||
|
||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
if (inActionMode() || selectedItem == null) {
|
||||
if (selectedItem == null) {
|
||||
return
|
||||
}
|
||||
val inflater: MenuInflater = mainActivityRef.get()!!.menuInflater
|
||||
if (inActionMode()) {
|
||||
inflater.inflate(R.menu.multi_select_context_popup, menu)
|
||||
// menu.findItem(R.id.multi_select).setVisible(true)
|
||||
} else {
|
||||
inflater.inflate(R.menu.nav_feed_context, menu)
|
||||
menu.findItem(R.id.multi_select).setVisible(true)
|
||||
// menu.findItem(R.id.multi_select).setVisible(true)
|
||||
menu.setHeaderTitle(selectedItem?.title)
|
||||
}
|
||||
}
|
||||
|
||||
fun onContextItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.multi_select) {
|
||||
|
@ -156,6 +175,7 @@ open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) :
|
|||
val count: TextView = binding.countLabel
|
||||
|
||||
val coverImage: ImageView = binding.coverImage
|
||||
val infoCard: LinearLayout = binding.infoCard
|
||||
val selectView: FrameLayout = binding.selectContainer
|
||||
val selectCheckbox: CheckBox = binding.selectCheckBox
|
||||
private val card: CardView = binding.outerContainer
|
||||
|
|
|
@ -37,14 +37,6 @@ class SwipeActionsDialog(private val context: Context, private val tag: String)
|
|||
|
||||
var forFragment = ""
|
||||
when (tag) {
|
||||
// InboxFragment.TAG -> {
|
||||
// forFragment = context.getString(R.string.inbox_label)
|
||||
// keys = Stream.of(keys).filter { a: SwipeAction ->
|
||||
// (!a.getId().equals(SwipeAction.TOGGLE_PLAYED)
|
||||
// && !a.getId().equals(SwipeAction.DELETE)
|
||||
// && !a.getId().equals(SwipeAction.REMOVE_FROM_HISTORY))
|
||||
// }.toList()
|
||||
// }
|
||||
AllEpisodesFragment.TAG -> {
|
||||
forFragment = context.getString(R.string.episodes_label)
|
||||
keys = Stream.of(keys).filter { a: SwipeAction -> !a.getId().equals(SwipeAction.REMOVE_FROM_HISTORY) }.toList()
|
||||
|
@ -52,8 +44,7 @@ class SwipeActionsDialog(private val context: Context, private val tag: String)
|
|||
CompletedDownloadsFragment.TAG -> {
|
||||
forFragment = context.getString(R.string.downloads_label)
|
||||
keys = Stream.of(keys).filter { a: SwipeAction ->
|
||||
(!a.getId().equals(SwipeAction.REMOVE_FROM_INBOX)
|
||||
&& !a.getId().equals(SwipeAction.REMOVE_FROM_HISTORY)
|
||||
(!a.getId().equals(SwipeAction.REMOVE_FROM_HISTORY)
|
||||
&& !a.getId().equals(SwipeAction.START_DOWNLOAD)) }.toList()
|
||||
}
|
||||
FeedItemlistFragment.TAG -> {
|
||||
|
@ -64,12 +55,11 @@ class SwipeActionsDialog(private val context: Context, private val tag: String)
|
|||
forFragment = context.getString(R.string.queue_label)
|
||||
keys = Stream.of(keys).filter { a: SwipeAction ->
|
||||
(!a.getId().equals(SwipeAction.ADD_TO_QUEUE)
|
||||
&& !a.getId().equals(SwipeAction.REMOVE_FROM_INBOX)
|
||||
&& !a.getId().equals(SwipeAction.REMOVE_FROM_HISTORY)) }.toList()
|
||||
}
|
||||
PlaybackHistoryFragment.TAG -> {
|
||||
forFragment = context.getString(R.string.playback_history_label)
|
||||
keys = Stream.of(keys).filter { a: SwipeAction -> !a.getId().equals(SwipeAction.REMOVE_FROM_INBOX) }.toList()
|
||||
keys = Stream.of(keys).toList()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
@ -169,7 +159,6 @@ class SwipeActionsDialog(private val context: Context, private val tag: String)
|
|||
view.container.alpha = 0.3f
|
||||
view.secondaryActionButton.secondaryAction.visibility = View.GONE
|
||||
view.dragHandle.visibility = View.GONE
|
||||
view.statusInbox.visibility = View.GONE
|
||||
view.txtvTitle.text = "███████"
|
||||
view.txtvPosition.text = "█████"
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
|
|||
speedDialView.removeActionItemById(R.id.mark_read_batch)
|
||||
speedDialView.removeActionItemById(R.id.mark_unread_batch)
|
||||
speedDialView.removeActionItemById(R.id.remove_from_queue_batch)
|
||||
speedDialView.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
// speedDialView.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
speedDialView.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
override fun onMainActionSelected(): Boolean {
|
||||
return false
|
||||
|
@ -343,9 +343,9 @@ class CompletedDownloadsFragment : Fragment(), SelectableAdapter.OnSelectModeLis
|
|||
|
||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo)
|
||||
if (!inActionMode()) {
|
||||
menu.findItem(R.id.multi_select).setVisible(true)
|
||||
}
|
||||
// if (!inActionMode()) {
|
||||
// menu.findItem(R.id.multi_select).setVisible(true)
|
||||
// }
|
||||
MenuItemUtils.setOnClickListeners(menu) { item: MenuItem ->
|
||||
this@CompletedDownloadsFragment.onContextItemSelected(item)
|
||||
}
|
||||
|
|
|
@ -124,9 +124,8 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
val viewBinding = EpisodesListFragmentBinding.inflate(inflater)
|
||||
// val root: View = inflater.inflate(R.layout.episodes_list_fragment, container, false)
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
|
||||
txtvInformation = viewBinding.txtvInformation
|
||||
toolbar = viewBinding.toolbar
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
|
@ -163,9 +162,9 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
listAdapter = object : EpisodeItemListAdapter(activity as MainActivity) {
|
||||
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo)
|
||||
if (!inActionMode()) {
|
||||
menu.findItem(R.id.multi_select).setVisible(true)
|
||||
}
|
||||
// if (!inActionMode()) {
|
||||
// menu.findItem(R.id.multi_select).setVisible(true)
|
||||
// }
|
||||
MenuItemUtils.setOnClickListeners(menu
|
||||
) { item: MenuItem ->
|
||||
this@EpisodesListFragment.onContextItemSelected(item)
|
||||
|
@ -231,8 +230,7 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode
|
|||
}
|
||||
|
||||
@UnstableApi private fun performMultiSelectAction(actionItemId: Int) {
|
||||
val handler =
|
||||
EpisodeMultiSelectActionHandler((activity as MainActivity), actionItemId)
|
||||
val handler = EpisodeMultiSelectActionHandler((activity as MainActivity), actionItemId)
|
||||
Completable.fromAction {
|
||||
handler.handleAction(listAdapter.selectedItems.filterIsInstance<FeedItem>())
|
||||
if (listAdapter.shouldSelectLazyLoadedItems()) {
|
||||
|
|
|
@ -362,7 +362,7 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
if (feed != null && feed!!.isLocalFeed) {
|
||||
speedDialBinding.fabSD.removeActionItemById(R.id.download_batch)
|
||||
}
|
||||
speedDialBinding.fabSD.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
// speedDialBinding.fabSD.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
speedDialBinding.fabSD.visibility = View.VISIBLE
|
||||
updateToolbar()
|
||||
}
|
||||
|
@ -431,12 +431,10 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
if (feed != null && feed!!.itemFilter != null) {
|
||||
val filter: FeedItemFilter? = feed!!.itemFilter
|
||||
if (filter != null && filter.values.isNotEmpty()) {
|
||||
viewBinding.header.txtvInformation.text = ("{md-info-outline} "
|
||||
+ this.getString(R.string.filtered_label))
|
||||
viewBinding.header.txtvInformation.text = ("{md-info-outline} " + this.getString(R.string.filtered_label))
|
||||
Iconify.addIcons(viewBinding.header.txtvInformation)
|
||||
viewBinding.header.txtvInformation.setOnClickListener {
|
||||
FeedItemFilterDialog.newInstance(feed!!).show(
|
||||
childFragmentManager, null)
|
||||
FeedItemFilterDialog.newInstance(feed!!).show(childFragmentManager, null)
|
||||
}
|
||||
viewBinding.header.txtvInformation.visibility = View.VISIBLE
|
||||
} else {
|
||||
|
@ -571,14 +569,14 @@ class FeedItemlistFragment : Fragment(), AdapterView.OnItemClickListener, Toolba
|
|||
|
||||
private inner class FeedItemListAdapter(mainActivity: MainActivity) : EpisodeItemListAdapter(mainActivity) {
|
||||
@UnstableApi override fun beforeBindViewHolder(holder: EpisodeItemViewHolder, pos: Int) {
|
||||
holder.coverHolder.visibility = View.GONE
|
||||
// holder.coverHolder.visibility = View.GONE
|
||||
}
|
||||
|
||||
@UnstableApi override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo)
|
||||
if (!inActionMode()) {
|
||||
menu.findItem(R.id.multi_select).setVisible(true)
|
||||
}
|
||||
// if (!inActionMode()) {
|
||||
// menu.findItem(R.id.multi_select).setVisible(true)
|
||||
// }
|
||||
MenuItemUtils.setOnClickListeners(menu) { item: MenuItem ->
|
||||
this@FeedItemlistFragment.onContextItemSelected(item)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import ac.mdiq.podcini.ui.appstartintent.MainActivityStarter
|
|||
import ac.mdiq.podcini.ui.common.ThemeUtils
|
||||
import ac.mdiq.podcini.ui.dialog.*
|
||||
import ac.mdiq.podcini.ui.menuhandler.MenuItemUtils
|
||||
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
||||
import android.R.attr
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
|
@ -159,18 +160,18 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
@OptIn(UnstableApi::class) private fun onFeedContextMenuClicked(feed: Feed, item: MenuItem): Boolean {
|
||||
val itemId = item.itemId
|
||||
when (itemId) {
|
||||
R.id.remove_all_inbox_item -> {
|
||||
val removeAllNewFlagsConfirmationDialog: ConfirmationDialog = object : ConfirmationDialog(requireContext(),
|
||||
R.string.remove_all_inbox_label,
|
||||
R.string.remove_all_inbox_confirmation_msg) {
|
||||
@OptIn(UnstableApi::class) override fun onConfirmButtonPressed(dialog: DialogInterface) {
|
||||
dialog.dismiss()
|
||||
DBWriter.removeFeedNewFlag(feed.id)
|
||||
}
|
||||
}
|
||||
removeAllNewFlagsConfirmationDialog.createNewDialog().show()
|
||||
return true
|
||||
}
|
||||
// R.id.remove_all_inbox_item -> {
|
||||
// val removeAllNewFlagsConfirmationDialog: ConfirmationDialog = object : ConfirmationDialog(requireContext(),
|
||||
// R.string.remove_all_inbox_label,
|
||||
// R.string.remove_all_inbox_confirmation_msg) {
|
||||
// @OptIn(UnstableApi::class) override fun onConfirmButtonPressed(dialog: DialogInterface) {
|
||||
// dialog.dismiss()
|
||||
// DBWriter.removeFeedNewFlag(feed.id)
|
||||
// }
|
||||
// }
|
||||
// removeAllNewFlagsConfirmationDialog.createNewDialog().show()
|
||||
// return true
|
||||
// }
|
||||
R.id.edit_tags -> {
|
||||
if (feed.preferences != null)
|
||||
TagSettingsDialog.newInstance(listOf(feed.preferences!!)).show(childFragmentManager, TagSettingsDialog.TAG)
|
||||
|
@ -378,9 +379,8 @@ class NavDrawerFragment : Fragment(), SharedPreferences.OnSharedPreferenceChange
|
|||
QueueFragment.TAG,
|
||||
AllEpisodesFragment.TAG,
|
||||
CompletedDownloadsFragment.TAG,
|
||||
// InboxFragment.TAG,
|
||||
PlaybackHistoryFragment.TAG,
|
||||
// StatisticsFragment.TAG,
|
||||
StatisticsFragment.TAG,
|
||||
AddFeedFragment.TAG,
|
||||
)
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ import java.util.*
|
|||
* Shows all items in the queue.
|
||||
*/
|
||||
class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAdapter.OnSelectModeListener {
|
||||
|
||||
private lateinit var infoBar: TextView
|
||||
private lateinit var recyclerView: EpisodeItemListRecyclerView
|
||||
private lateinit var emptyView: EmptyViewHandler
|
||||
|
@ -87,7 +88,6 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
@UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
val binding = QueueFragmentBinding.inflate(inflater)
|
||||
// val root: View = inflater.inflate(R.layout.queue_fragment, container, false)
|
||||
|
||||
Log.d(TAG, "fragment onCreateView")
|
||||
toolbar = binding.toolbar
|
||||
|
@ -151,7 +151,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
|||
// speedDialView.removeActionItemById(R.id.mark_read_batch)
|
||||
// speedDialView.removeActionItemById(R.id.mark_unread_batch)
|
||||
speedDialView.removeActionItemById(R.id.add_to_queue_batch)
|
||||
speedDialView.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
// speedDialView.removeActionItemById(R.id.remove_all_inbox_item)
|
||||
speedDialView.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
override fun onMainActionSelected(): Boolean {
|
||||
return false
|
||||
|
|
|
@ -16,7 +16,6 @@ import ac.mdiq.podcini.ui.dialog.SubscriptionsFilterDialog
|
|||
import ac.mdiq.podcini.ui.fragment.actions.FeedMultiSelectActionHandler
|
||||
import ac.mdiq.podcini.ui.menuhandler.FeedMenuHandler
|
||||
import ac.mdiq.podcini.ui.menuhandler.MenuItemUtils
|
||||
import ac.mdiq.podcini.ui.statistics.StatisticsFragment
|
||||
import ac.mdiq.podcini.ui.view.EmptyViewHandler
|
||||
import ac.mdiq.podcini.ui.view.LiftOnScrollListener
|
||||
import ac.mdiq.podcini.util.event.FeedListUpdateEvent
|
||||
|
@ -245,24 +244,20 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
@UnstableApi override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
val itemId = item.itemId
|
||||
when (itemId) {
|
||||
R.id.refresh_item -> {
|
||||
FeedUpdateManager.runOnceOrAsk(requireContext())
|
||||
return true
|
||||
}
|
||||
R.id.subscriptions_filter -> {
|
||||
SubscriptionsFilterDialog().show(childFragmentManager, "filter")
|
||||
R.id.action_search -> {
|
||||
(activity as MainActivity).loadChildFragment(SearchFragment.newInstance())
|
||||
return true
|
||||
}
|
||||
R.id.subscriptions_sort -> {
|
||||
FeedSortDialog.showDialog(requireContext())
|
||||
return true
|
||||
}
|
||||
R.id.action_search -> {
|
||||
(activity as MainActivity).loadChildFragment(SearchFragment.newInstance())
|
||||
R.id.subscriptions_filter -> {
|
||||
SubscriptionsFilterDialog().show(childFragmentManager, "filter")
|
||||
return true
|
||||
}
|
||||
R.id.action_statistics -> {
|
||||
(activity as MainActivity).loadChildFragment(StatisticsFragment())
|
||||
R.id.refresh_item -> {
|
||||
FeedUpdateManager.runOnceOrAsk(requireContext())
|
||||
return true
|
||||
}
|
||||
else -> return false
|
||||
|
@ -344,6 +339,7 @@ class SubscriptionFragment : Fragment(), Toolbar.OnMenuItemClickListener, Select
|
|||
}
|
||||
|
||||
override fun onStartSelectMode() {
|
||||
speedDialView.visibility = View.VISIBLE
|
||||
val feedsOnly: MutableList<NavDrawerData.FeedDrawerItem> = ArrayList<NavDrawerData.FeedDrawerItem>()
|
||||
for (item in feedListFiltered) {
|
||||
feedsOnly.add(item)
|
||||
|
|
|
@ -63,7 +63,8 @@ class SwipePreferencesFragment : PreferenceFragmentCompat() {
|
|||
|
||||
companion object {
|
||||
private const val PREF_SWIPE_QUEUE = "prefSwipeQueue"
|
||||
private const val PREF_SWIPE_INBOX = "prefSwipeInbox"
|
||||
// private const val PREF_SWIPE_INBOX = "prefSwipeInbox"
|
||||
// private const val PREF_SWIPE_STATISTICS = "prefSwipeStatistics"
|
||||
private const val PREF_SWIPE_EPISODES = "prefSwipeEpisodes"
|
||||
private const val PREF_SWIPE_DOWNLOADS = "prefSwipeDownloads"
|
||||
private const val PREF_SWIPE_FEED = "prefSwipeFeed"
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
package ac.mdiq.podcini.ui.fragment.swipeactions
|
||||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import ac.mdiq.podcini.R
|
||||
import ac.mdiq.podcini.ui.menuhandler.FeedItemMenuHandler.markReadWithUndo
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||
import ac.mdiq.podcini.storage.model.feed.FeedItemFilter
|
||||
|
||||
class RemoveFromInboxSwipeAction : SwipeAction {
|
||||
override fun getId(): String {
|
||||
return SwipeAction.REMOVE_FROM_INBOX
|
||||
}
|
||||
|
||||
override fun getActionIcon(): Int {
|
||||
return R.drawable.ic_check
|
||||
}
|
||||
|
||||
override fun getActionColor(): Int {
|
||||
return R.attr.icon_purple
|
||||
}
|
||||
|
||||
override fun getTitle(context: Context): String {
|
||||
return context.getString(R.string.remove_inbox_label)
|
||||
}
|
||||
|
||||
override fun performAction(item: FeedItem, fragment: Fragment, filter: FeedItemFilter) {
|
||||
if (item.isNew) {
|
||||
markReadWithUndo(fragment, item, FeedItem.UNPLAYED, willRemove(filter, item))
|
||||
}
|
||||
}
|
||||
|
||||
override fun willRemove(filter: FeedItemFilter, item: FeedItem): Boolean {
|
||||
return filter.showNew
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ interface SwipeAction {
|
|||
|
||||
companion object {
|
||||
const val ADD_TO_QUEUE: String = "ADD_TO_QUEUE"
|
||||
const val REMOVE_FROM_INBOX: String = "REMOVE_FROM_INBOX"
|
||||
// const val REMOVE_FROM_INBOX: String = "REMOVE_FROM_INBOX"
|
||||
const val START_DOWNLOAD: String = "START_DOWNLOAD"
|
||||
const val MARK_FAV: String = "MARK_FAV"
|
||||
const val TOGGLE_PLAYED: String = "MARK_PLAYED"
|
||||
|
|
|
@ -213,8 +213,7 @@ open class SwipeActions(dragDirs: Int, private val fragment: Fragment, private v
|
|||
|
||||
@JvmField
|
||||
val swipeActions: List<SwipeAction> = Collections.unmodifiableList(
|
||||
listOf(AddToQueueSwipeAction(), RemoveFromInboxSwipeAction(),
|
||||
StartDownloadSwipeAction(), MarkFavoriteSwipeAction(),
|
||||
listOf(AddToQueueSwipeAction(), StartDownloadSwipeAction(), MarkFavoriteSwipeAction(),
|
||||
TogglePlaybackStateSwipeAction(), RemoveFromQueueSwipeAction(),
|
||||
DeleteSwipeAction(), RemoveFromHistorySwipeAction())
|
||||
)
|
||||
|
|
|
@ -34,6 +34,7 @@ object NotificationUtils {
|
|||
createChannelError(context),
|
||||
createChannelSyncError(context),
|
||||
createChannelEpisodeNotification(context))
|
||||
|
||||
mNotificationManager.createNotificationChannelsCompat(channels)
|
||||
}
|
||||
|
||||
|
|
|
@ -33,20 +33,20 @@ object FeedMenuHandler {
|
|||
val context = fragment.requireContext()
|
||||
if (menuItemId == R.id.rename_folder_item) {
|
||||
RenameItemDialog(fragment.activity as Activity, selectedFeed).show()
|
||||
} else if (menuItemId == R.id.remove_all_inbox_item) {
|
||||
val dialog: ConfirmationDialog = object : ConfirmationDialog(fragment.activity as Activity,
|
||||
R.string.remove_all_inbox_label, R.string.remove_all_inbox_confirmation_msg) {
|
||||
@OptIn(UnstableApi::class) @SuppressLint("CheckResult")
|
||||
override fun onConfirmButtonPressed(clickedDialog: DialogInterface) {
|
||||
clickedDialog.dismiss()
|
||||
Observable.fromCallable(Callable { DBWriter.removeFeedNewFlag(selectedFeed.id) } as Callable<Future<*>>)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ callback.run() },
|
||||
{ error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
}
|
||||
}
|
||||
dialog.createNewDialog().show()
|
||||
// } else if (menuItemId == R.id.remove_all_inbox_item) {
|
||||
// val dialog: ConfirmationDialog = object : ConfirmationDialog(fragment.activity as Activity,
|
||||
// R.string.remove_all_inbox_label, R.string.remove_all_inbox_confirmation_msg) {
|
||||
// @OptIn(UnstableApi::class) @SuppressLint("CheckResult")
|
||||
// override fun onConfirmButtonPressed(clickedDialog: DialogInterface) {
|
||||
// clickedDialog.dismiss()
|
||||
// Observable.fromCallable(Callable { DBWriter.removeFeedNewFlag(selectedFeed.id) } as Callable<Future<*>>)
|
||||
// .subscribeOn(Schedulers.io())
|
||||
// .observeOn(AndroidSchedulers.mainThread())
|
||||
// .subscribe({ callback.run() },
|
||||
// { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) })
|
||||
// }
|
||||
// }
|
||||
// dialog.createNewDialog().show()
|
||||
} else if (menuItemId == R.id.edit_tags) {
|
||||
if (selectedFeed.preferences != null) TagSettingsDialog.newInstance(listOf(selectedFeed.preferences!!))
|
||||
.show(fragment.childFragmentManager, TagSettingsDialog.TAG)
|
||||
|
|
|
@ -4,6 +4,7 @@ package ac.mdiq.podcini.ui.statistics
|
|||
import ac.mdiq.podcini.R
|
||||
import ac.mdiq.podcini.databinding.PagerFragmentBinding
|
||||
import ac.mdiq.podcini.storage.DBWriter
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.ui.common.PagedToolbarFragment
|
||||
import ac.mdiq.podcini.ui.dialog.ConfirmationDialog
|
||||
import ac.mdiq.podcini.ui.statistics.downloads.DownloadStatisticsFragment
|
||||
|
@ -17,6 +18,7 @@ import android.view.LayoutInflater
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
|
@ -33,29 +35,29 @@ import org.greenrobot.eventbus.EventBus
|
|||
* Displays the 'statistics' screen
|
||||
*/
|
||||
class StatisticsFragment : PagedToolbarFragment() {
|
||||
private var tabLayout: TabLayout? = null
|
||||
private var viewPager: ViewPager2? = null
|
||||
private var toolbar: MaterialToolbar? = null
|
||||
private lateinit var tabLayout: TabLayout
|
||||
private lateinit var viewPager: ViewPager2
|
||||
private lateinit var toolbar: MaterialToolbar
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
@OptIn(UnstableApi::class) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
val binding = PagerFragmentBinding.inflate(inflater)
|
||||
// val rootView = inflater.inflate(R.layout.pager_fragment, container, false)
|
||||
viewPager = binding.viewpager
|
||||
toolbar = binding.toolbar
|
||||
toolbar?.title = getString(R.string.statistics_label)
|
||||
toolbar?.inflateMenu(R.menu.statistics)
|
||||
toolbar?.setNavigationOnClickListener { parentFragmentManager.popBackStack() }
|
||||
viewPager?.adapter = StatisticsPagerAdapter(this)
|
||||
toolbar.title = getString(R.string.statistics_label)
|
||||
toolbar.inflateMenu(R.menu.statistics)
|
||||
toolbar.setNavigationOnClickListener { parentFragmentManager.popBackStack() }
|
||||
(activity as MainActivity).setupToolbarToggle(toolbar, false)
|
||||
|
||||
viewPager.adapter = StatisticsPagerAdapter(this)
|
||||
// Give the TabLayout the ViewPager
|
||||
tabLayout = binding.slidingTabs
|
||||
if (toolbar != null && viewPager != null) super.setupPagedToolbar(toolbar!!, viewPager!!)
|
||||
if (tabLayout == null || viewPager == null) return null
|
||||
super.setupPagedToolbar(toolbar, viewPager)
|
||||
|
||||
TabLayoutMediator(tabLayout!!, viewPager!!) { tab: TabLayout.Tab, position: Int ->
|
||||
TabLayoutMediator(tabLayout, viewPager) { tab: TabLayout.Tab, position: Int ->
|
||||
when (position) {
|
||||
POS_SUBSCRIPTIONS -> tab.setText(R.string.subscriptions_label)
|
||||
POS_YEARS -> tab.setText(R.string.years_statistics_label)
|
||||
|
|
|
@ -34,6 +34,8 @@ import ac.mdiq.podcini.preferences.UserPreferences
|
|||
import ac.mdiq.podcini.ui.adapter.actionbutton.ItemActionButton
|
||||
import ac.mdiq.podcini.ui.common.CircularProgressBar
|
||||
import ac.mdiq.podcini.ui.common.ThemeUtils
|
||||
import ac.mdiq.podcini.util.Converter
|
||||
import android.widget.LinearLayout
|
||||
import io.reactivex.functions.Consumer
|
||||
import kotlin.math.max
|
||||
|
||||
|
@ -56,7 +58,6 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou
|
|||
private val position: TextView
|
||||
private val duration: TextView
|
||||
private val size: TextView
|
||||
val isInbox: ImageView
|
||||
@JvmField
|
||||
val isInQueue: ImageView
|
||||
private val isVideo: ImageView
|
||||
|
@ -71,6 +72,8 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou
|
|||
private val leftPadding: View
|
||||
@JvmField
|
||||
val coverHolder: CardView
|
||||
@JvmField
|
||||
val infoCard: LinearLayout
|
||||
|
||||
private var item: FeedItem? = null
|
||||
|
||||
|
@ -84,7 +87,6 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou
|
|||
progressBar = binding.progressBar
|
||||
isInQueue = binding.ivInPlaylist
|
||||
isVideo = binding.ivIsVideo
|
||||
isInbox = binding.statusInbox
|
||||
isFavorite = binding.isFavorite
|
||||
size = binding.size
|
||||
separatorIcons = binding.separatorIcons
|
||||
|
@ -92,6 +94,7 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou
|
|||
secondaryActionButton = binding.secondaryActionButton.root
|
||||
secondaryActionIcon = binding.secondaryActionButton.secondaryActionIcon
|
||||
coverHolder = binding.coverHolder
|
||||
infoCard = binding.infoCard
|
||||
leftPadding = binding.leftPadding
|
||||
itemView.tag = this
|
||||
}
|
||||
|
@ -107,7 +110,6 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou
|
|||
}
|
||||
pubDate.text = DateFormatter.formatAbbrev(activity, item.getPubDate())
|
||||
pubDate.setContentDescription(DateFormatter.formatForAccessibility(item.getPubDate()))
|
||||
isInbox.visibility = if (item.isNew) View.VISIBLE else View.GONE
|
||||
isFavorite.visibility = if (item.isTagged(FeedItem.TAG_FAVORITE)) View.VISIBLE else View.GONE
|
||||
isInQueue.visibility = if (item.isTagged(FeedItem.TAG_QUEUE)) View.VISIBLE else View.GONE
|
||||
container.alpha = if (item.isPlayed()) 0.5f else 1.0f
|
||||
|
@ -210,7 +212,6 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou
|
|||
item = FeedItem()
|
||||
container.alpha = 0.1f
|
||||
secondaryActionIcon.setImageDrawable(null)
|
||||
isInbox.visibility = View.VISIBLE
|
||||
isVideo.visibility = View.GONE
|
||||
isFavorite.visibility = View.GONE
|
||||
isInQueue.visibility = View.GONE
|
||||
|
@ -235,9 +236,10 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou
|
|||
}
|
||||
|
||||
private fun updateDuration(event: PlaybackPositionEvent) {
|
||||
if (feedItem?.media != null) {
|
||||
feedItem!!.media!!.setPosition(event.position)
|
||||
feedItem!!.media!!.setDuration(event.duration)
|
||||
val media = feedItem?.media
|
||||
if (media != null) {
|
||||
media.setPosition(event.position)
|
||||
media.setDuration(event.duration)
|
||||
}
|
||||
val currentPosition: Int = event.position
|
||||
val timeDuration: Int = event.duration
|
||||
|
@ -248,9 +250,9 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou
|
|||
return
|
||||
}
|
||||
if (UserPreferences.shouldShowRemainingTime()) {
|
||||
duration.text = (if (remainingTime > 0) "-" else "") + ac.mdiq.podcini.util.Converter.getDurationStringLong(remainingTime)
|
||||
duration.text = (if (remainingTime > 0) "-" else "") + Converter.getDurationStringLong(remainingTime)
|
||||
} else {
|
||||
duration.text = ac.mdiq.podcini.util.Converter.getDurationStringLong(timeDuration)
|
||||
duration.text = Converter.getDurationStringLong(timeDuration)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,11 +273,9 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou
|
|||
* Hides the separator dot between icons and text if there are no icons.
|
||||
*/
|
||||
fun hideSeparatorIfNecessary() {
|
||||
val hasIcons = isInbox.visibility == View.VISIBLE ||
|
||||
isInQueue.visibility == View.VISIBLE ||
|
||||
val hasIcons = isInQueue.visibility == View.VISIBLE ||
|
||||
isVideo.visibility == View.VISIBLE ||
|
||||
isFavorite.visibility == View.VISIBLE ||
|
||||
isInbox.visibility == View.VISIBLE
|
||||
isFavorite.visibility == View.VISIBLE
|
||||
separatorIcons.visibility = if (hasIcons) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M3,9l4,-4l4,4m-4,-4v14"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/colorPrimary"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M21,15l-4,4l-4,-4m4,4v-14"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/colorPrimary"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -3,5 +3,6 @@
|
|||
android:width="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path android:fillColor="?attr/action_icon_color" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
|
||||
<path android:fillColor="?attr/action_icon_color"
|
||||
android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
|
||||
</vector>
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
android:height="48dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path android:fillColor="?attr/action_icon_color" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
|
||||
<path android:fillColor="?attr/action_icon_color"
|
||||
android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
|
||||
</vector>
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
android:height="48dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path android:fillColor="#ffffff" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
||||
<path android:fillColor="#ffffff"
|
||||
android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
||||
</vector>
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
android:height="48dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path android:fillColor="#ffffff" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
|
||||
<path android:fillColor="#ffffff"
|
||||
android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
|
||||
</vector>
|
||||
|
|
|
@ -75,7 +75,6 @@
|
|||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
tools:text="1:06:29" />
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
android:gravity="start"
|
||||
android:text="Title"
|
||||
android:textStyle="bold"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="15sp"/>
|
||||
android:textSize="13sp"/>
|
||||
|
||||
<ac.mdiq.podcini.ui.view.ChapterSeekBar
|
||||
android:id="@+id/sbPosition"
|
||||
|
@ -97,7 +98,8 @@
|
|||
android:padding="8dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_play_48dp"
|
||||
tools:srcCompat="@drawable/ic_play_48dp" />
|
||||
tools:srcCompat="@drawable/ic_play_48dp"
|
||||
app:tint="@color/medium_gray"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/butRev"
|
||||
|
@ -112,7 +114,8 @@
|
|||
android:contentDescription="@string/rewind_label"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_fast_rewind"
|
||||
tools:srcCompat="@drawable/ic_fast_rewind" />
|
||||
tools:srcCompat="@drawable/ic_fast_rewind"
|
||||
app:tint="@color/medium_gray"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvRev"
|
||||
|
@ -140,8 +143,8 @@
|
|||
android:layout_toLeftOf="@id/butRev"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/playback_speed"
|
||||
app:foregroundColor="?attr/action_icon_color"
|
||||
tools:srcCompat="@drawable/ic_playback_speed" />
|
||||
app:foregroundColor="@color/medium_gray"
|
||||
tools:srcCompat="@drawable/ic_playback_speed"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvPlaybackSpeed"
|
||||
|
@ -171,7 +174,8 @@
|
|||
android:contentDescription="@string/fast_forward_label"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_fast_forward"
|
||||
tools:srcCompat="@drawable/ic_fast_forward" />
|
||||
tools:srcCompat="@drawable/ic_fast_forward"
|
||||
app:tint="@color/medium_gray"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvFF"
|
||||
|
@ -201,7 +205,8 @@
|
|||
android:contentDescription="@string/skip_episode_label"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_skip_48dp"
|
||||
tools:srcCompat="@drawable/ic_skip_48dp" />
|
||||
tools:srcCompat="@drawable/ic_skip_48dp"
|
||||
app:tint="@color/medium_gray"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/info_card"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
|
||||
|
@ -104,13 +105,6 @@
|
|||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/statusInbox"
|
||||
android:layout_width="14sp"
|
||||
android:layout_height="14sp"
|
||||
android:contentDescription="@string/is_inbox_label"
|
||||
app:srcCompat="@drawable/ic_inbox" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivIsVideo"
|
||||
android:layout_width="14sp"
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/info_card"
|
||||
android:layout_width="0.dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:custom="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_search"
|
||||
android:icon="@drawable/ic_search"
|
||||
custom:showAsAction="always"
|
||||
android:title="@string/search_label"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/refresh_item"
|
||||
android:title="@string/refresh_label"
|
||||
android:menuCategory="container"
|
||||
custom:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/homesettings_items"
|
||||
android:icon="@drawable/ic_settings"
|
||||
android:menuCategory="container"
|
||||
android:title="@string/configure_home"
|
||||
custom:showAsAction="never"/>
|
||||
|
||||
</menu>
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:custom="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_search"
|
||||
android:icon="@drawable/ic_search"
|
||||
custom:showAsAction="always"
|
||||
android:title="@string/search_label"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/refresh_item"
|
||||
android:title="@string/refresh_label"
|
||||
android:menuCategory="container"
|
||||
custom:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/inbox_sort"
|
||||
android:title="@string/sort" />
|
||||
|
||||
<item
|
||||
android:id="@+id/remove_all_inbox_item"
|
||||
android:title="@string/remove_all_inbox_label"
|
||||
android:menuCategory="container"
|
||||
custom:showAsAction="collapseActionView"
|
||||
android:icon="@drawable/ic_check"/>
|
||||
|
||||
</menu>
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/remove_all_inbox_item"
|
||||
android:menuCategory="container"
|
||||
android:title="@string/remove_all_inbox_label" />
|
||||
<!-- <item-->
|
||||
<!-- android:id="@+id/remove_all_inbox_item"-->
|
||||
<!-- android:menuCategory="container"-->
|
||||
<!-- android:title="@string/remove_all_inbox_label" />-->
|
||||
|
||||
<item
|
||||
android:id="@+id/edit_tags"
|
||||
|
|
|
@ -6,10 +6,16 @@
|
|||
android:icon="@drawable/ic_search"
|
||||
custom:showAsAction="always"
|
||||
android:title="@string/search_label"/>
|
||||
<item
|
||||
android:id="@+id/subscriptions_sort"
|
||||
android:title="@string/sort"
|
||||
android:icon="@drawable/arrows_sort"
|
||||
custom:showAsAction="always" />
|
||||
<item
|
||||
android:id="@+id/action_statistics"
|
||||
android:icon="@drawable/ic_chart_box"
|
||||
android:title="@string/statistics_label"
|
||||
android:visible="false"
|
||||
custom:showAsAction="always" />
|
||||
<item
|
||||
android:id="@+id/refresh_item"
|
||||
|
@ -19,9 +25,6 @@
|
|||
<item
|
||||
android:id="@+id/subscriptions_filter"
|
||||
android:title="@string/filter"
|
||||
custom:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/subscriptions_sort"
|
||||
android:title="@string/sort"
|
||||
custom:showAsAction="never" />
|
||||
android:visible="false"
|
||||
custom:showAsAction="never"/>
|
||||
</menu>
|
||||
|
|
|
@ -195,9 +195,8 @@
|
|||
<item>@string/queue_label</item>
|
||||
<item>@string/episodes_label</item>
|
||||
<item>@string/downloads_label</item>
|
||||
<!-- <item>@string/inbox_label</item>-->
|
||||
<item>@string/playback_history_label</item>
|
||||
<!-- <item>@string/statistics_label</item>-->
|
||||
<item>@string/statistics_label</item>
|
||||
<item>@string/add_feed_label</item>
|
||||
</string-array>
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
android:key="prefSwipeQueue"
|
||||
android:title="@string/queue_label"/>
|
||||
|
||||
<Preference
|
||||
android:key="prefSwipeInbox"
|
||||
android:title="@string/inbox_label"/>
|
||||
<!-- <Preference-->
|
||||
<!-- android:key="prefSwipeInbox"-->
|
||||
<!-- android:title="@string/inbox_label"/>-->
|
||||
|
||||
<Preference
|
||||
android:key="prefSwipeEpisodes"
|
||||
|
@ -21,6 +21,10 @@
|
|||
android:key="prefSwipeHistory"
|
||||
android:title="@string/playback_history_label"/>
|
||||
|
||||
<Preference
|
||||
android:key="prefSwipeStatistics"
|
||||
android:title="@string/statistics_label"/>
|
||||
|
||||
<Preference
|
||||
android:key="prefSwipeFeed"
|
||||
android:title="@string/individual_subscription"/>
|
||||
|
|
14
changelog.md
|
@ -33,3 +33,17 @@
|
|||
* improvement on player UI
|
||||
* episode description now on first page of player popup page
|
||||
* localization updates
|
||||
|
||||
## 4.2.1
|
||||
|
||||
* Statistics moved to the drawer
|
||||
* tuned down color of player controller
|
||||
* Subscriptions menu adjustment
|
||||
* Subscriptions filter is disabled for now
|
||||
* more null safety tuning
|
||||
* fixed the refresh bug related to permissions
|
||||
* long-press operation has changed
|
||||
* long-press on title area would be the same as a click
|
||||
* click on an icon allows operation on the single item
|
||||
* long-press on an icon would allow for multi-select
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
|
||||
Version 4.1.0 sees the removal of the InBox feature, improvements on player UI, and localization updates
|
||||
Version 4.2.0 sees the removal of the InBox feature, improvements on player UI, and localization updates
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
Version 4.2.1 brings several changes:
|
||||
|
||||
* Statistics moved to the drawer
|
||||
* tuned down color of player controller
|
||||
* Subscriptions menu adjustment
|
||||
* Subscriptions filter is disabled for now
|
||||
* more null safety tuning
|
||||
* fixed the refresh bug related to permissions
|
||||
* long-press operation has changed
|
||||
* long-press on title area would be the same as a click
|
||||
* click on an icon allows operation on the single item
|
||||
* long-press on an icon would allow for multi-select
|
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 189 KiB |
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 177 KiB |
Before Width: | Height: | Size: 271 KiB After Width: | Height: | Size: 221 KiB |
Before Width: | Height: | Size: 326 KiB After Width: | Height: | Size: 273 KiB |
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 188 KiB |
Before Width: | Height: | Size: 342 KiB After Width: | Height: | Size: 287 KiB |
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 128 KiB |
After Width: | Height: | Size: 9.4 KiB |