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