6.3.2 commit
This commit is contained in:
parent
81d4374a3f
commit
abdbf3dabd
|
@ -138,6 +138,7 @@ While podcast subscriptions' OPML files (from AntennaPod or any other sources) c
|
|||
* Play history/progress can be separately exported/imported as Json files
|
||||
* Downloaded media files can be exported/imported
|
||||
* Reconsile feature (accessed from Downloads view) is added to ensure downloaded media files are in sync with specs in DB
|
||||
* Podcasts can be selectively exported from Subscriptions view
|
||||
* There is a setting to disable/enable auto backup of OPML files to Google
|
||||
* Upon re-install of Podcini, the OPML file previously backed up to Google is not imported automatically but based on user confirmation.
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@ android {
|
|||
testApplicationId "ac.mdiq.podcini.tests"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
versionCode 3020225
|
||||
versionName "6.3.1"
|
||||
versionCode 3020226
|
||||
versionName "6.3.2"
|
||||
|
||||
applicationId "ac.mdiq.podcini.R"
|
||||
def commit = ""
|
||||
|
|
|
@ -68,6 +68,8 @@ class OpmlTransporter {
|
|||
|
||||
xs.startTag(null, OpmlSymbols.BODY)
|
||||
for (feed in feeds!!) {
|
||||
if (feed == null) continue
|
||||
Logd(TAG, "writeDocument ${feed?.title}")
|
||||
xs.startTag(null, OpmlSymbols.OUTLINE)
|
||||
xs.attribute(null, OpmlSymbols.TEXT, feed!!.title)
|
||||
xs.attribute(null, OpmlSymbols.TITLE, feed.title)
|
||||
|
|
|
@ -23,6 +23,8 @@ import ac.mdiq.podcini.storage.utils.FileNameGenerator.generateFileName
|
|||
import ac.mdiq.podcini.storage.utils.FilesUtils.getDataFolder
|
||||
import ac.mdiq.podcini.ui.activity.OpmlImportActivity
|
||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
||||
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment
|
||||
import ac.mdiq.podcini.ui.fragment.SubscriptionsFragment.Companion
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.app.ProgressDialog
|
||||
|
@ -264,7 +266,7 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
|
||||
private fun exportDatabase() {
|
||||
backupDatabaseLauncher.launch(dateStampFilename(DATABASE_EXPORT_FILENAME))
|
||||
backupDatabaseLauncher.launch(dateStampFilename("PodciniBackup-%s.realm"))
|
||||
}
|
||||
|
||||
private fun importDatabase() {
|
||||
|
@ -554,15 +556,16 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
}
|
||||
|
||||
private enum class Export(val contentType: String, val outputNameTemplate: String, @field:StringRes val labelResId: Int) {
|
||||
OPML(CONTENT_TYPE_OPML, DEFAULT_OPML_OUTPUT_NAME, R.string.opml_export_label),
|
||||
HTML(CONTENT_TYPE_HTML, DEFAULT_HTML_OUTPUT_NAME, R.string.html_export_label),
|
||||
FAVORITES(CONTENT_TYPE_HTML, DEFAULT_FAVORITES_OUTPUT_NAME, R.string.favorites_export_label),
|
||||
PROGRESS(CONTENT_TYPE_PROGRESS, DEFAULT_PROGRESS_OUTPUT_NAME, R.string.progress_export_label),
|
||||
enum class Export(val contentType: String, val outputNameTemplate: String, @field:StringRes val labelResId: Int) {
|
||||
OPML(CONTENT_TYPE_OPML, "podcini-feeds-%s.opml", R.string.opml_export_label),
|
||||
OPML_SELECTED(CONTENT_TYPE_OPML, "podcini-feeds-selected-%s.opml", R.string.opml_export_label),
|
||||
HTML(CONTENT_TYPE_HTML, "podcini-feeds-%s.html", R.string.html_export_label),
|
||||
FAVORITES(CONTENT_TYPE_HTML, "podcini-favorites-%s.html", R.string.favorites_export_label),
|
||||
PROGRESS(CONTENT_TYPE_PROGRESS, "podcini-progress-%s.json", R.string.progress_export_label),
|
||||
}
|
||||
|
||||
class DocumentFileExportWorker(private val exportWriter: ExportWriter, private val context: Context, private val outputFileUri: Uri) {
|
||||
suspend fun exportFile(): DocumentFile {
|
||||
suspend fun exportFile(feeds: List<Feed>? = null): DocumentFile {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val output = DocumentFile.fromSingleUri(context, outputFileUri)
|
||||
var outputStream: OutputStream? = null
|
||||
|
@ -573,7 +576,9 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
|
|||
outputStream = context.contentResolver.openOutputStream(uri, "wt")
|
||||
if (outputStream == null) throw IOException()
|
||||
writer = OutputStreamWriter(outputStream, Charset.forName("UTF-8"))
|
||||
exportWriter.writeDocument(getFeedList(), writer, context)
|
||||
val feeds_ = feeds ?: getFeedList()
|
||||
Logd(TAG, "feeds_: ${feeds_.size}")
|
||||
exportWriter.writeDocument(feeds_, writer, context)
|
||||
output
|
||||
} catch (e: IOException) {
|
||||
throw e
|
||||
|
@ -591,7 +596,7 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
|
|||
class ExportWorker private constructor(private val exportWriter: ExportWriter, private val output: File, private val context: Context) {
|
||||
constructor(exportWriter: ExportWriter, context: Context) : this(exportWriter, File(getDataFolder(EXPORT_DIR),
|
||||
DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension()), context)
|
||||
suspend fun exportFile(): File? {
|
||||
suspend fun exportFile(feeds: List<Feed>? = null): File? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
if (output.exists()) {
|
||||
val success = output.delete()
|
||||
|
@ -600,7 +605,9 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
|
|||
var writer: OutputStreamWriter? = null
|
||||
try {
|
||||
writer = OutputStreamWriter(FileOutputStream(output), Charset.forName("UTF-8"))
|
||||
exportWriter.writeDocument(getFeedList(), writer, context)
|
||||
val feeds_ = feeds ?: getFeedList()
|
||||
Logd(TAG, "feeds_: ${feeds_.size}")
|
||||
exportWriter.writeDocument(feeds_, writer, context)
|
||||
output // return the output file
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Error during file export", e)
|
||||
|
@ -1135,13 +1142,8 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
|
|||
companion object {
|
||||
private val TAG: String = ImportExportPreferencesFragment::class.simpleName ?: "Anonymous"
|
||||
|
||||
private const val DEFAULT_OPML_OUTPUT_NAME = "podcini-feeds-%s.opml"
|
||||
private const val CONTENT_TYPE_OPML = "text/x-opml"
|
||||
private const val DEFAULT_HTML_OUTPUT_NAME = "podcini-feeds-%s.html"
|
||||
private const val CONTENT_TYPE_HTML = "text/html"
|
||||
private const val DEFAULT_FAVORITES_OUTPUT_NAME = "podcini-favorites-%s.html"
|
||||
private const val CONTENT_TYPE_PROGRESS = "text/x-json"
|
||||
private const val DEFAULT_PROGRESS_OUTPUT_NAME = "podcini-progress-%s.json"
|
||||
private const val DATABASE_EXPORT_FILENAME = "PodciniBackup-%s.realm"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ac.mdiq.podcini.storage.model
|
||||
|
||||
import ac.mdiq.podcini.storage.database.Feeds.getFeed
|
||||
import ac.mdiq.podcini.storage.database.RealmDB.unmanaged
|
||||
import io.realm.kotlin.ext.realmListOf
|
||||
import io.realm.kotlin.ext.realmSetOf
|
||||
import io.realm.kotlin.types.RealmList
|
||||
|
@ -114,7 +115,7 @@ class Episode : RealmObject {
|
|||
val imageLocation: String?
|
||||
get() = when {
|
||||
imageUrl != null -> imageUrl
|
||||
media != null && media!!.hasEmbeddedPicture() -> EpisodeMedia.FILENAME_PREFIX_EMBEDDED_COVER + media!!.getLocalMediaUrl()
|
||||
media != null && unmanaged(media!!).hasEmbeddedPicture() -> EpisodeMedia.FILENAME_PREFIX_EMBEDDED_COVER + media!!.getLocalMediaUrl()
|
||||
feed != null -> {
|
||||
feed!!.imageUrl
|
||||
}
|
||||
|
|
|
@ -313,7 +313,7 @@ class EpisodeMedia: EmbeddedRealmObject, Playable {
|
|||
override fun getImageLocation(): String? {
|
||||
return when {
|
||||
episode != null -> episode!!.imageLocation
|
||||
hasEmbeddedPicture() -> FILENAME_PREFIX_EMBEDDED_COVER + getLocalMediaUrl()
|
||||
unmanaged(this).hasEmbeddedPicture() -> FILENAME_PREFIX_EMBEDDED_COVER + getLocalMediaUrl()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,10 @@ import ac.mdiq.podcini.storage.model.Episode
|
|||
import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload
|
||||
import ac.mdiq.podcini.storage.database.Episodes.persistEpisode
|
||||
import ac.mdiq.podcini.storage.database.RealmDB.upsert
|
||||
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
|
||||
import ac.mdiq.podcini.util.event.EventFlow
|
||||
import ac.mdiq.podcini.util.event.FlowEvent
|
||||
|
||||
class CancelDownloadActionButton(item: Episode) : EpisodeActionButton(item) {
|
||||
@StringRes
|
||||
|
@ -25,8 +29,9 @@ class CancelDownloadActionButton(item: Episode) : EpisodeActionButton(item) {
|
|||
val media = item.media
|
||||
if (media != null) DownloadServiceInterface.get()?.cancel(context, media)
|
||||
if (isEnableAutodownload) {
|
||||
item.disableAutoDownload()
|
||||
persistEpisode(item)
|
||||
}
|
||||
val item_ = upsertBlk(item) {
|
||||
it.disableAutoDownload()
|
||||
}
|
||||
EventFlow.postEvent(FlowEvent.EpisodeEvent.updated(item_)) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,9 +157,7 @@ abstract class SelectableAdapter<T : RecyclerView.ViewHolder?>(private val activ
|
|||
totalCount = totalNumberOfItems
|
||||
if (shouldSelectLazyLoadedItems) selectedCount += (totalNumberOfItems - itemCount)
|
||||
}
|
||||
actionMode!!.title = activity.resources
|
||||
.getQuantityString(R.plurals.num_selected_label, selectedIds.size,
|
||||
selectedCount, totalCount)
|
||||
actionMode!!.title = activity.resources.getQuantityString(R.plurals.num_selected_label, selectedIds.size, selectedCount, totalCount)
|
||||
}
|
||||
|
||||
fun setOnSelectModeListener(onSelectModeListener: OnSelectModeListener?) {
|
||||
|
|
|
@ -524,9 +524,10 @@ import kotlin.math.max
|
|||
}
|
||||
}
|
||||
// they didn't tell us the size, but we don't want to keep querying on it
|
||||
if (size <= 0) media.setCheckedOnSizeButUnknown()
|
||||
else media.size = size
|
||||
upsert(episode) {}
|
||||
upsert(episode) {
|
||||
if (size <= 0) it.media?.setCheckedOnSizeButUnknown()
|
||||
else it.media?.size = size
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
|
|
@ -238,7 +238,7 @@ import java.lang.ref.WeakReference
|
|||
private fun onMenuItemClicked(fragment: Fragment, menuItemId: Int, selectedFeed: Feed, callback: Runnable): Boolean {
|
||||
val context = fragment.requireContext()
|
||||
when (menuItemId) {
|
||||
R.id.rename_folder_item -> CustomFeedNameDialog(fragment.activity as Activity, selectedFeed).show()
|
||||
// R.id.rename_folder_item -> CustomFeedNameDialog(fragment.activity as Activity, selectedFeed).show()
|
||||
R.id.edit_tags -> if (selectedFeed.preferences != null) TagSettingsDialog.newInstance(listOf(selectedFeed))
|
||||
.show(fragment.childFragmentManager, TagSettingsDialog.TAG)
|
||||
R.id.rename_item -> CustomFeedNameDialog(fragment.activity as Activity, selectedFeed).show()
|
||||
|
@ -492,7 +492,7 @@ import java.lang.ref.WeakReference
|
|||
override fun onCreateContextMenu(contextMenu: ContextMenu, view: View, contextMenuInfo: ContextMenu.ContextMenuInfo?) {
|
||||
val inflater: MenuInflater = mainActivityRef.get()!!.menuInflater
|
||||
if (longPressedItem == null) return
|
||||
inflater.inflate(R.menu.nav_feed_context, contextMenu)
|
||||
inflater.inflate(R.menu.feed_context, contextMenu)
|
||||
contextMenu.setHeaderTitle(longPressedItem!!.title)
|
||||
}
|
||||
fun setEndButton(@StringRes text: Int, action: Runnable?) {
|
||||
|
|
|
@ -3,8 +3,10 @@ package ac.mdiq.podcini.ui.fragment
|
|||
import ac.mdiq.podcini.R
|
||||
import ac.mdiq.podcini.databinding.*
|
||||
import ac.mdiq.podcini.net.feed.FeedUpdateManager
|
||||
import ac.mdiq.podcini.preferences.OpmlTransporter.OpmlWriter
|
||||
import ac.mdiq.podcini.preferences.UserPreferences
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.appPrefs
|
||||
import ac.mdiq.podcini.preferences.fragments.ImportExportPreferencesFragment.*
|
||||
import ac.mdiq.podcini.storage.database.Feeds.getFeedList
|
||||
import ac.mdiq.podcini.storage.database.Feeds.getTags
|
||||
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
||||
|
@ -28,15 +30,21 @@ import ac.mdiq.podcini.ui.utils.LiftOnScrollListener
|
|||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.event.EventFlow
|
||||
import ac.mdiq.podcini.util.event.FlowEvent
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.*
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
|
@ -66,11 +74,8 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton
|
|||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.leinardi.android.speeddial.SpeedDialActionItem
|
||||
import com.leinardi.android.speeddial.SpeedDialView
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.text.NumberFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
@ -80,6 +85,14 @@ import java.util.*
|
|||
*/
|
||||
class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAdapter.OnSelectModeListener {
|
||||
|
||||
private val chooseOpmlExportPathLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
result: ActivityResult ->
|
||||
if (result.resultCode != RESULT_OK || result.data == null) return@registerForActivityResult
|
||||
val uri = result.data!!.data
|
||||
multiSelectHandler?.exportOPML(uri)
|
||||
}
|
||||
|
||||
private var multiSelectHandler: FeedMultiSelectActionHandler? = null
|
||||
private var _binding: FragmentSubscriptionsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
|
@ -181,7 +194,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
|||
val speedDialBinding = MultiSelectSpeedDialBinding.bind(binding.root)
|
||||
speedDialView = speedDialBinding.fabSD
|
||||
speedDialView.overlayLayout = speedDialBinding.fabSDOverlay
|
||||
speedDialView.inflate(R.menu.nav_feed_action_speeddial)
|
||||
speedDialView.inflate(R.menu.feed_action_speeddial)
|
||||
speedDialView.setOnChangeListener(object : SpeedDialView.OnChangeListener {
|
||||
override fun onMainActionSelected(): Boolean {
|
||||
return false
|
||||
|
@ -189,7 +202,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
|||
override fun onToggleChanged(isOpen: Boolean) {}
|
||||
})
|
||||
speedDialView.setOnActionSelectedListener { actionItem: SpeedDialActionItem ->
|
||||
FeedMultiSelectActionHandler(activity as MainActivity, adapter.selectedItems.filterIsInstance<Feed>()).handleAction(actionItem.id)
|
||||
multiSelectHandler = FeedMultiSelectActionHandler(activity as MainActivity, adapter.selectedItems.filterIsInstance<Feed>())
|
||||
multiSelectHandler?.handleAction(actionItem.id)
|
||||
true
|
||||
}
|
||||
loadSubscriptions()
|
||||
|
@ -242,7 +256,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
|||
private fun queryStringOfTags() : String {
|
||||
return when (tagFilterIndex) {
|
||||
1 -> "" // All feeds
|
||||
0 -> " preferences.tags.@count == 0 OR (preferences.tags.@count == 0 AND ALL preferences.tags == '#root' ) "
|
||||
// TODO: #root appears not used in RealmDB, is it a SQLite specialty
|
||||
0 -> " (preferences.tags.@count == 0 OR (preferences.tags.@count != 0 AND ALL preferences.tags == '#root' )) "
|
||||
else -> { // feeds with the chosen tag
|
||||
val tag = tags[tagFilterIndex]
|
||||
" ANY preferences.tags == '$tag' "
|
||||
|
@ -334,8 +349,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
|||
lifecycleScope.launch {
|
||||
try {
|
||||
withContext(Dispatchers.IO) {
|
||||
filterAndSort()
|
||||
resetTags()
|
||||
filterAndSort()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
// We have fewer items. This can result in items being selected that are no longer visible.
|
||||
|
@ -502,7 +517,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
|||
}
|
||||
|
||||
@UnstableApi
|
||||
private class FeedMultiSelectActionHandler(private val activity: MainActivity, private val selectedItems: List<Feed>) {
|
||||
private inner class FeedMultiSelectActionHandler(private val activity: MainActivity, private val selectedItems: List<Feed>) {
|
||||
fun handleAction(id: Int) {
|
||||
when (id) {
|
||||
R.id.remove_feed -> RemoveFeedDialog.show(activity, selectedItems)
|
||||
|
@ -510,11 +525,42 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
|||
R.id.autodownload -> autoDownloadPrefHandler()
|
||||
R.id.autoDeleteDownload -> autoDeleteEpisodesPrefHandler()
|
||||
R.id.playback_speed -> playbackSpeedPrefHandler()
|
||||
R.id.export_opml -> openExportPathPicker()
|
||||
R.id.associate_queue -> associatedQueuePrefHandler()
|
||||
R.id.edit_tags -> TagSettingsDialog.newInstance(selectedItems).show(activity.supportFragmentManager, TAG)
|
||||
else -> Log.e(TAG, "Unrecognized speed dial action item. Do nothing. id=$id")
|
||||
}
|
||||
}
|
||||
private fun openExportPathPicker() {
|
||||
val exportType = Export.OPML_SELECTED
|
||||
val title = String.format(exportType.outputNameTemplate, SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date()))
|
||||
val intentPickAction = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
.setType(exportType.contentType)
|
||||
.putExtra(Intent.EXTRA_TITLE, title)
|
||||
try {
|
||||
chooseOpmlExportPathLauncher.launch(intentPickAction)
|
||||
return
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e(TAG, "No activity found. Should never happen...")
|
||||
}
|
||||
// if on SDK lower than API 21 or the implicit intent failed, fallback to the legacy export process
|
||||
exportOPML(null)
|
||||
}
|
||||
fun exportOPML(uri: Uri?) {
|
||||
try {
|
||||
runBlocking {
|
||||
Logd(TAG, "selectedFeeds: ${selectedItems.size}")
|
||||
if (uri == null) ExportWorker(OpmlWriter(), requireContext()).exportFile(selectedItems)
|
||||
else {
|
||||
val worker = DocumentFileExportWorker(OpmlWriter(), requireContext(), uri)
|
||||
worker.exportFile(selectedItems)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "exportOPML error: ${e.message}")
|
||||
}
|
||||
}
|
||||
private fun autoDownloadPrefHandler() {
|
||||
val preferenceSwitchDialog = PreferenceSwitchDialog(activity, activity.getString(R.string.auto_download_settings_label), activity.getString(R.string.auto_download_label))
|
||||
preferenceSwitchDialog.setOnPreferenceChangedListener(@UnstableApi object: PreferenceSwitchDialog.OnPreferenceChangedListener {
|
||||
|
@ -761,10 +807,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener, Selec
|
|||
get() {
|
||||
val items = ArrayList<Feed>()
|
||||
for (i in 0 until itemCount) {
|
||||
if (isSelected(i)) {
|
||||
val feed: Feed = feedList[i]
|
||||
items.add(feed)
|
||||
}
|
||||
if (isSelected(i)) items.add(feedList[i])
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<vector android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="?attr/action_icon_color" android:pathData="M9,3L5,6.99h3L8,14h2L10,6.99h3L9,3zM16,17.01L16,10h-2v7.01h-3L15,21l4,-3.99h-3z"/>
|
||||
</vector>
|
|
@ -10,11 +10,6 @@
|
|||
android:menuCategory="container"
|
||||
android:title="@string/keep_updated"
|
||||
android:icon="@drawable/ic_refresh"/>
|
||||
<!-- <item-->
|
||||
<!-- android:id="@+id/notify_new_episodes"-->
|
||||
<!-- android:menuCategory="container"-->
|
||||
<!-- android:title="@string/episode_notification"-->
|
||||
<!-- android:icon="@drawable/ic_notifications"/>-->
|
||||
<item
|
||||
android:id="@+id/autodownload"
|
||||
android:menuCategory="container"
|
||||
|
@ -40,4 +35,9 @@
|
|||
android:menuCategory="container"
|
||||
android:title="@string/pref_feed_associated_queue"
|
||||
android:icon="@drawable/ic_playlist_play"/>
|
||||
<item
|
||||
android:id="@+id/export_opml"
|
||||
android:menuCategory="container"
|
||||
android:title="@string/opml_export_label"
|
||||
android:icon="@drawable/baseline_import_export_24"/>
|
||||
</menu>
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/rename_folder_item"
|
||||
android:menuCategory="container"
|
||||
android:title="@string/rename_tag_label" />
|
||||
</menu>
|
|
@ -1,3 +1,11 @@
|
|||
# 6.3.2
|
||||
|
||||
* fixed crash of opening FeedEpisode view when "Use episode cover" is set
|
||||
* fixed crash of opening EpisodeInfo view on episode with unknown media size
|
||||
* fixed crash when cancelling download in a auto-download enabled feed
|
||||
* fixed mis-behavior of "Untagged" filter in combination with other filters in Subscriptions view
|
||||
* added "export selected feeds" in multi-select menu in Subscriptions view
|
||||
|
||||
# 6.3.1
|
||||
|
||||
* fixed crash when playing episode with missing media file
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
Version 6.3.2 brings several changes:
|
||||
|
||||
* fixed crash of opening FeedEpisode view when "Use episode cover" is set
|
||||
* fixed crash of opening EpisodeInfo view on episode with unknown media size
|
||||
* fixed crash when cancelling download in a auto-download enabled feed
|
||||
* fixed mis-behavior of "Untagged" filter in combination with other filters in Subscriptions view
|
||||
* added "export selected feeds" in multi-select menu in Subscriptions view
|
Loading…
Reference in New Issue