6.15.2 commit
This commit is contained in:
parent
f2f4b8dcee
commit
dcbbbd745f
|
@ -8,12 +8,13 @@ An open source podcast instrument, attuned to Puccini ![Puccini](./images/Puccin
|
|||
|
||||
[<img src="./images/external/getItGithub.png" alt="GitHub" height="50">](https://github.com/XilinJia/Podcini/releases/latest)
|
||||
[<img src="./images/external/getItIzzyOnDroid.png" alt="IzzyOnDroid" height="50">](https://apt.izzysoft.de/fdroid/index/apk/ac.mdiq.podcini.R)
|
||||
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" alt="Google Play" height="50">](https://play.google.com/apps/testing/ac.mdiq.podcini.R)
|
||||
[<img src="./images/external/getItf-droid.png" alt="F-Droid" height="50">](https://f-droid.org/packages/ac.mdiq.podcini.R/)
|
||||
[<img src="./images/external/amazon.png" alt="Amazon" height="40">](https://www.amazon.com/%E8%B4%BE%E8%A5%BF%E6%9E%97-Podcini-R/dp/B0D9WR8P13)
|
||||
|
||||
### Note:
|
||||
|
||||
#### *** Podcini is being launched on Google Play and needs closed testing, please kindly support it through [here](https://github.com/XilinJia/Podcini/discussions/120)
|
||||
#### *** Podcini is being launched on Google Play. Early testers will become pre-IPO members on the contributors list. Start with a comment [here](https://github.com/XilinJia/Podcini/discussions/120)
|
||||
#### Podcini.R 6.5 as a major step forward brings YouTube contents in the app. Channels can be searched, received from share, subscribed. Podcasts, playlists as well as single media from Youtube and YT Music can be shared to Podcini. For more see the Youtube section below or the changelogs
|
||||
That means finally: [Nessun dorma](https://www.youtube.com/watch?v=cWc7vYjgnTs)
|
||||
#### For Podcini to show up on car's HUD with Android Auto, please read AnroidAuto.md for instructions.
|
||||
|
|
|
@ -26,8 +26,8 @@ android {
|
|||
vectorDrawables.useSupportLibrary false
|
||||
vectorDrawables.generatedDensities = []
|
||||
|
||||
versionCode 3020309
|
||||
versionName "6.15.1"
|
||||
versionCode 3020310
|
||||
versionName "6.15.2"
|
||||
|
||||
applicationId "ac.mdiq.podcini.R"
|
||||
def commit = ""
|
||||
|
|
|
@ -225,11 +225,11 @@
|
|||
|
||||
</activity>
|
||||
|
||||
<activity android:name=".ui.activity.SelectSubscriptionActivity"
|
||||
android:label="@string/shortcut_subscription_label"
|
||||
android:icon="@drawable/ic_subscriptions_shortcut"
|
||||
android:theme="@style/Theme.Podcini.Dark.Translucent"
|
||||
android:exported="true">
|
||||
<activity android:name=".ui.activity.SubscriptionShortcutActivity"
|
||||
android:label="@string/shortcut_subscription_label"
|
||||
android:icon="@drawable/ic_launcher_foreground"
|
||||
android:theme="@style/Theme.Podcini.Dark.Translucent"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.CREATE_SHORTCUT" />
|
||||
</intent-filter>
|
||||
|
|
|
@ -144,6 +144,7 @@ abstract class MediaPlayerBase protected constructor(protected val context: Cont
|
|||
}
|
||||
}
|
||||
val audioStream = audioStreamsList[audioIndex]
|
||||
media.bitrate = audioStream.bitrate
|
||||
Logd(TAG, "setDataSource1 use audio quality: ${audioStream.bitrate} forceVideo: ${media.forceVideo}")
|
||||
|
||||
media.audioUrl = audioStream.content
|
||||
|
|
|
@ -500,31 +500,13 @@ object Feeds {
|
|||
feed = createSynthetic(feedId, name)
|
||||
feed.type = Feed.FeedType.YOUTUBE.name
|
||||
feed.hasVideoMedia = video
|
||||
feed.preferences!!.audioTypeSetting = if (music) AudioType.MOVIE else AudioType.SPEECH
|
||||
feed.preferences!!.audioTypeSetting = if (music) AudioType.MUSIC else AudioType.SPEECH
|
||||
feed.preferences!!.videoModePolicy = if (video) VideoMode.WINDOW_VIEW else VideoMode.AUDIO_ONLY
|
||||
upsertBlk(feed) {}
|
||||
EventFlow.postEvent(FlowEvent.FeedListEvent(FlowEvent.FeedListEvent.Action.ADDED))
|
||||
return feed
|
||||
}
|
||||
|
||||
fun addToYoutubeSyndicate(episode: Episode, video: Boolean) : Int {
|
||||
val feed = getYoutubeSyndicate(video, episode.media?.downloadUrl?.contains("music") == true)
|
||||
Logd(TAG, "addToYoutubeSyndicate: feed: ${feed.title}")
|
||||
if (searchEpisodeByIdentifyingValue(feed.episodes, episode) != null) return 2
|
||||
|
||||
Logd(TAG, "addToYoutubeSyndicate adding new episode: ${episode.title}")
|
||||
episode.feed = feed
|
||||
episode.id = Feed.newId()
|
||||
episode.feedId = feed.id
|
||||
episode.media?.id = episode.id
|
||||
upsertBlk(episode) {}
|
||||
upsertBlk(feed) {
|
||||
it.episodes.add(episode)
|
||||
}
|
||||
EventFlow.postStickyEvent(FlowEvent.FeedUpdatingEvent(false))
|
||||
return 1
|
||||
}
|
||||
|
||||
fun addToSyndicate(episode: Episode, feed: Feed) : Int {
|
||||
Logd(TAG, "addToYoutubeSyndicate: feed: ${feed.title}")
|
||||
if (searchEpisodeByIdentifyingValue(feed.episodes, episode) != null) return 2
|
||||
|
|
|
@ -94,9 +94,8 @@ class EpisodeMedia: EmbeddedRealmObject, Playable {
|
|||
@Ignore
|
||||
var audioUrl = ""
|
||||
|
||||
/* Used for loading item when restoring from parcel. */
|
||||
// var episodeId: Long = 0
|
||||
// private set
|
||||
@Ignore
|
||||
var bitrate: Int = 0
|
||||
|
||||
constructor() {}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import ac.mdiq.podcini.ui.activity.starter.MainActivityStarter
|
|||
import ac.mdiq.podcini.ui.dialog.RatingDialog
|
||||
import ac.mdiq.podcini.ui.fragment.*
|
||||
import ac.mdiq.podcini.ui.fragment.AudioPlayerFragment.Companion.media3Controller
|
||||
import ac.mdiq.podcini.ui.fragment.NavDrawerFragment.Companion.getLastNavFragmentArg
|
||||
import ac.mdiq.podcini.ui.fragment.StatisticsFragment
|
||||
import ac.mdiq.podcini.ui.utils.ThemeUtils.getDrawableFromAttr
|
||||
import ac.mdiq.podcini.ui.utils.TransitionEffect
|
||||
|
@ -222,12 +223,9 @@ class MainActivity : CastEnabledActivity() {
|
|||
if (UserPreferences.DEFAULT_PAGE_REMEMBER != defaultPage) loadFragment(defaultPage, null)
|
||||
else {
|
||||
val lastFragment = NavDrawerFragment.getLastNavFragment()
|
||||
if (NavDrawerFragment.navMap.keys.contains(lastFragment)) loadFragment(lastFragment, null)
|
||||
else {
|
||||
// it's not a number, this happens if we removed a label from the NAV_DRAWER_TAGS give them a nice default...
|
||||
try { loadFeedFragmentById(lastFragment.toInt().toLong(), null) }
|
||||
catch (e: NumberFormatException) { loadFragment(SubscriptionsFragment.TAG, null) }
|
||||
}
|
||||
Logd(TAG, "lastFragment: $lastFragment")
|
||||
if (NavDrawerFragment.navMap.keys.contains(lastFragment) || lastFragment == FeedEpisodesFragment.TAG) loadFragment(lastFragment, null)
|
||||
else loadFragment(SubscriptionsFragment.TAG, null)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -437,8 +435,7 @@ class MainActivity : CastEnabledActivity() {
|
|||
val params = mainView.layoutParams as MarginLayoutParams
|
||||
val externalPlayerHeight = resources.getDimension(R.dimen.external_player_height).toInt()
|
||||
Logd(TAG, "externalPlayerHeight: $externalPlayerHeight ${navigationBarInsets.bottom}")
|
||||
params.setMargins(navigationBarInsets.left, 0, navigationBarInsets.right,
|
||||
navigationBarInsets.bottom + (if (visible) externalPlayerHeight else 0))
|
||||
params.setMargins(navigationBarInsets.left, 0, navigationBarInsets.right, navigationBarInsets.bottom + (if (visible) externalPlayerHeight else 0))
|
||||
mainView.layoutParams = params
|
||||
audioPlayerView.visibility = if (visible) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
@ -451,23 +448,23 @@ class MainActivity : CastEnabledActivity() {
|
|||
var tag = tag
|
||||
var args = args
|
||||
Logd(TAG, "loadFragment(tag: $tag, args: $args)")
|
||||
val fragment: Fragment
|
||||
when (tag) {
|
||||
QueuesFragment.TAG -> fragment = QueuesFragment()
|
||||
EpisodesFragment.TAG -> fragment = EpisodesFragment()
|
||||
// AllEpisodesFragment.TAG -> fragment = AllEpisodesFragment()
|
||||
// DownloadsFragment.TAG -> fragment = DownloadsFragment()
|
||||
LogsFragment.TAG -> fragment = LogsFragment()
|
||||
// HistoryFragment.TAG -> fragment = HistoryFragment()
|
||||
OnlineSearchFragment.TAG -> fragment = OnlineSearchFragment()
|
||||
SubscriptionsFragment.TAG -> fragment = SubscriptionsFragment()
|
||||
StatisticsFragment.TAG -> fragment = StatisticsFragment()
|
||||
FeedEpisodesFragment.TAG -> fragment = FeedEpisodesFragment()
|
||||
val fragment: Fragment = when (tag) {
|
||||
QueuesFragment.TAG -> QueuesFragment()
|
||||
EpisodesFragment.TAG -> EpisodesFragment()
|
||||
LogsFragment.TAG -> LogsFragment()
|
||||
OnlineSearchFragment.TAG -> OnlineSearchFragment()
|
||||
SubscriptionsFragment.TAG -> SubscriptionsFragment()
|
||||
StatisticsFragment.TAG -> StatisticsFragment()
|
||||
FeedEpisodesFragment.TAG -> {
|
||||
if (args == null) {
|
||||
val feedId = getLastNavFragmentArg().toLongOrNull()
|
||||
if (feedId != null) FeedEpisodesFragment.newInstance(feedId) else SubscriptionsFragment()
|
||||
} else FeedEpisodesFragment()
|
||||
}
|
||||
else -> {
|
||||
// default to subscriptions screen
|
||||
fragment = SubscriptionsFragment()
|
||||
tag = SubscriptionsFragment.TAG
|
||||
args = null
|
||||
SubscriptionsFragment()
|
||||
}
|
||||
}
|
||||
if (args != null) fragment.arguments = args
|
||||
|
@ -478,16 +475,14 @@ class MainActivity : CastEnabledActivity() {
|
|||
fun loadFeedFragmentById(feedId: Long, args: Bundle?) {
|
||||
val fragment: Fragment = FeedEpisodesFragment.newInstance(feedId)
|
||||
if (args != null) fragment.arguments = args
|
||||
NavDrawerFragment.saveLastNavFragment(feedId.toString())
|
||||
NavDrawerFragment.saveLastNavFragment(FeedEpisodesFragment.TAG, feedId.toString())
|
||||
loadFragment(fragment)
|
||||
}
|
||||
|
||||
private fun loadFragment(fragment: Fragment) {
|
||||
val fragmentManager = supportFragmentManager
|
||||
// clear back stack
|
||||
for (i in 0 until fragmentManager.backStackEntryCount) {
|
||||
fragmentManager.popBackStack()
|
||||
}
|
||||
for (i in 0 until fragmentManager.backStackEntryCount) fragmentManager.popBackStack()
|
||||
val t = fragmentManager.beginTransaction()
|
||||
t.replace(R.id.main_view, fragment, MAIN_FRAGMENT_TAG)
|
||||
fragmentManager.popBackStack()
|
||||
|
@ -609,10 +604,6 @@ class MainActivity : CastEnabledActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
fun openDrawer() {
|
||||
drawerLayout?.openDrawer(navDrawer)
|
||||
}
|
||||
|
||||
private var eventSink: Job? = null
|
||||
private var eventStickySink: Job? = null
|
||||
private fun cancelFlowEvents() {
|
||||
|
@ -635,12 +626,7 @@ class MainActivity : CastEnabledActivity() {
|
|||
}
|
||||
}
|
||||
if (eventStickySink == null) eventStickySink = lifecycleScope.launch {
|
||||
EventFlow.stickyEvents.collectLatest { event ->
|
||||
Logd(TAG, "Received sticky event: ${event.TAG}")
|
||||
// when (event) {
|
||||
// else -> {}
|
||||
// }
|
||||
}
|
||||
EventFlow.stickyEvents.collectLatest { event -> Logd(TAG, "Received sticky event: ${event.TAG}") }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -651,9 +637,11 @@ class MainActivity : CastEnabledActivity() {
|
|||
intent.hasExtra(Extras.fragment_feed_id.name) -> {
|
||||
val feedId = intent.getLongExtra(Extras.fragment_feed_id.name, 0)
|
||||
val args = intent.getBundleExtra(MainActivityStarter.Extras.fragment_args.name)
|
||||
Logd(TAG, "handleNavIntent: feedId: $feedId")
|
||||
if (feedId > 0) {
|
||||
val startedFromShare = intent.getBooleanExtra(Extras.started_from_share.name, false)
|
||||
val addToBackStack = intent.getBooleanExtra(Extras.add_to_back_stack.name, false)
|
||||
Logd(TAG, "handleNavIntent: startedFromShare: $startedFromShare addToBackStack: $addToBackStack")
|
||||
if (startedFromShare || addToBackStack) loadChildFragment(FeedEpisodesFragment.newInstance(feedId))
|
||||
else loadFeedFragmentById(feedId, args)
|
||||
}
|
||||
|
@ -704,7 +692,6 @@ class MainActivity : CastEnabledActivity() {
|
|||
s = Snackbar.make(mainView, text, duration)
|
||||
if (audioPlayerView.visibility == View.VISIBLE) s.anchorView = audioPlayerView
|
||||
} else s = Snackbar.make(binding.root, text, duration)
|
||||
|
||||
s.show()
|
||||
return s
|
||||
}
|
||||
|
@ -715,14 +702,11 @@ class MainActivity : CastEnabledActivity() {
|
|||
|
||||
/**
|
||||
* Handles the deep link incoming via App Actions.
|
||||
* Performs an in-app search or opens the relevant feature of the app
|
||||
* depending on the query.
|
||||
*
|
||||
* Performs an in-app search or opens the relevant feature of the app depending on the query
|
||||
* @param uri incoming deep link
|
||||
*/
|
||||
private fun handleDeeplink(uri: Uri?) {
|
||||
if (uri?.path == null) return
|
||||
|
||||
Logd(TAG, "Handling deeplink: $uri")
|
||||
when (uri.path) {
|
||||
"/deeplink/search" -> {
|
||||
|
@ -732,8 +716,6 @@ class MainActivity : CastEnabledActivity() {
|
|||
"/deeplink/main" -> {
|
||||
val feature = uri.getQueryParameter("page") ?: return
|
||||
when (feature) {
|
||||
// "DOWNLOADS" -> loadFragment(DownloadsFragment.TAG, null)
|
||||
// "HISTORY" -> loadFragment(HistoryFragment.TAG, null)
|
||||
"EPISODES" -> loadFragment(EpisodesFragment.TAG, null)
|
||||
"QUEUE" -> loadFragment(QueuesFragment.TAG, null)
|
||||
"SUBSCRIPTIONS" -> loadFragment(SubscriptionsFragment.TAG, null)
|
||||
|
|
|
@ -150,8 +150,7 @@ import kotlin.Throws
|
|||
import kotlin.math.round
|
||||
|
||||
/**
|
||||
* PreferenceActivity for API 11+. In order to change the behavior of the preference UI, see
|
||||
* PreferenceController.
|
||||
* PreferenceActivity for API 11+. In order to change the behavior of the preference UI, see PreferenceController.
|
||||
*/
|
||||
class PreferenceActivity : AppCompatActivity() {
|
||||
private var _binding: SettingsActivityBinding? = null
|
||||
|
@ -426,7 +425,6 @@ class PreferenceActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showLicenseText(licenseTextFile: String) {
|
||||
try {
|
||||
val reader = BufferedReader(InputStreamReader(requireContext().assets.open(licenseTextFile), "UTF-8"))
|
||||
|
@ -436,12 +434,10 @@ class PreferenceActivity : AppCompatActivity() {
|
|||
MaterialAlertDialogBuilder(requireContext()).setMessage(licenseText).show()
|
||||
} catch (e: IOException) { e.printStackTrace() }
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
(activity as PreferenceActivity).supportActionBar!!.setTitle(R.string.licenses)
|
||||
}
|
||||
|
||||
private class LicenseItem(val title: String, val subtitle: String, val licenseUrl: String, val licenseTextFile: String)
|
||||
}
|
||||
}
|
||||
|
@ -553,12 +549,6 @@ class PreferenceActivity : AppCompatActivity() {
|
|||
}
|
||||
TitleSummarySwitchPrefRow(R.string.pref_back_button_opens_drawer, R.string.pref_back_button_opens_drawer_summary, UserPreferences.Prefs.prefBackButtonOpensDrawer.name)
|
||||
TitleSummaryActionColumn(R.string.swipeactions_label, R.string.swipeactions_summary) { (activity as PreferenceActivity).openScreen(Screens.preferences_swipe) }
|
||||
// Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||
// (activity as PreferenceActivity).openScreen(Screens.preferences_swipe)
|
||||
// })) {
|
||||
// Text(stringResource(R.string.swipeactions_label), color = textColor, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
|
||||
// Text(stringResource(R.string.swipeactions_summary), color = textColor)
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -569,8 +559,6 @@ class PreferenceActivity : AppCompatActivity() {
|
|||
SubscriptionsFragment(R.string.subscriptions_label),
|
||||
QueuesFragment(R.string.queue_label),
|
||||
EpisodesFragment(R.string.episodes_label),
|
||||
// DownloadsFragment(R.string.downloads_label),
|
||||
PlaybackHistoryFragment(R.string.playback_history_label),
|
||||
AddFeedFragment(R.string.add_feed_label),
|
||||
StatisticsFragment(R.string.statistics_label),
|
||||
remember(R.string.remember_last_page);
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
package ac.mdiq.podcini.ui.activity
|
||||
|
||||
import ac.mdiq.podcini.R
|
||||
import ac.mdiq.podcini.databinding.SubscriptionSelectionActivityBinding
|
||||
import ac.mdiq.podcini.preferences.ThemeSwitcher
|
||||
import ac.mdiq.podcini.storage.database.Feeds.getFeedList
|
||||
import ac.mdiq.podcini.storage.model.Feed
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.ListView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import coil.imageLoader
|
||||
import coil.request.ErrorResult
|
||||
import coil.request.ImageRequest
|
||||
import coil.request.SuccessResult
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
// TODO: need to enable
|
||||
class SelectSubscriptionActivity : AppCompatActivity() {
|
||||
private var _binding: SubscriptionSelectionActivityBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
@Volatile
|
||||
private var listItems: List<Feed> = listOf()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(ThemeSwitcher.getTranslucentTheme(this))
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
_binding = SubscriptionSelectionActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
setTitle(R.string.shortcut_select_subscription)
|
||||
|
||||
binding.transparentBackground.setOnClickListener { finish() }
|
||||
binding.card.setOnClickListener(null)
|
||||
|
||||
loadSubscriptions()
|
||||
|
||||
val checkedPosition = arrayOfNulls<Int>(1)
|
||||
binding.list.choiceMode = ListView.CHOICE_MODE_SINGLE
|
||||
binding.list.onItemClickListener =
|
||||
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, position: Int, _: Long ->
|
||||
checkedPosition[0] = position
|
||||
}
|
||||
binding.shortcutBtn.setOnClickListener {
|
||||
if (checkedPosition[0] != null && Intent.ACTION_CREATE_SHORTCUT == intent.action) getBitmapFromUrl(listItems[checkedPosition[0]!!])
|
||||
}
|
||||
}
|
||||
|
||||
fun getFeedItems(items: List<Feed?>, result: MutableList<Feed>): List<Feed> {
|
||||
for (item in items) {
|
||||
if (item == null) continue
|
||||
val feed: Feed = item
|
||||
if (!result.contains(feed)) result.add(feed)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun addShortcut(feed: Feed, bitmap: Bitmap?) {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
intent.setAction(Intent.ACTION_MAIN)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
intent.putExtra(MainActivity.Extras.fragment_feed_id.name, feed.id.toString())
|
||||
val id = "subscription-" + feed.id
|
||||
|
||||
val icon: IconCompat = if (bitmap != null) IconCompat.createWithAdaptiveBitmap(bitmap)
|
||||
else IconCompat.createWithResource(this, R.drawable.ic_subscriptions_shortcut)
|
||||
|
||||
val shortcut: ShortcutInfoCompat = ShortcutInfoCompat.Builder(this, id)
|
||||
.setShortLabel(feed.title?:"")
|
||||
.setLongLabel(feed.eigenTitle?:"")
|
||||
.setIntent(intent)
|
||||
.setIcon(icon)
|
||||
.build()
|
||||
|
||||
setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this, shortcut))
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun getBitmapFromUrl(feed: Feed) {
|
||||
val iconSize = (128 * resources.displayMetrics.density).toInt()
|
||||
val request = ImageRequest.Builder(this)
|
||||
.data(feed.imageUrl)
|
||||
.setHeader("User-Agent", "Mozilla/5.0")
|
||||
.placeholder(R.color.light_gray)
|
||||
.listener(object : ImageRequest.Listener {
|
||||
override fun onError(request: ImageRequest, throwable: ErrorResult) {
|
||||
addShortcut(feed, null)
|
||||
}
|
||||
override fun onSuccess(request: ImageRequest, result: SuccessResult) {
|
||||
addShortcut(feed, (result.drawable as BitmapDrawable).bitmap)
|
||||
}
|
||||
})
|
||||
.size(iconSize, iconSize)
|
||||
.build()
|
||||
imageLoader.enqueue(request)
|
||||
}
|
||||
|
||||
private fun loadSubscriptions() {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
getFeedList()
|
||||
// val data: NavDrawerData = DBReader.getNavDrawerData()
|
||||
// getFeedItems(data.items, ArrayList())
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
listItems = result
|
||||
val titles = ArrayList<String>()
|
||||
for (feed in result) {
|
||||
if (feed.title != null) titles.add(feed.title!!)
|
||||
}
|
||||
val adapter: ArrayAdapter<String> = ArrayAdapter<String>(this@SelectSubscriptionActivity, R.layout.simple_list_item_multiple_choice_on_start, titles)
|
||||
binding.list.adapter = adapter
|
||||
}
|
||||
} catch (e: Throwable) { Log.e(TAG, Log.getStackTraceString(e)) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
_binding = null
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG: String = SelectSubscriptionActivity::class.simpleName ?: "Anonymous"
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import ac.mdiq.podcini.R
|
|||
import ac.mdiq.podcini.storage.database.RealmDB.realm
|
||||
import ac.mdiq.podcini.storage.database.RealmDB.upsertBlk
|
||||
import ac.mdiq.podcini.storage.model.ShareLog
|
||||
import ac.mdiq.podcini.ui.compose.ConfirmAddYoutubeEpisode1
|
||||
import ac.mdiq.podcini.ui.compose.ConfirmAddYoutubeEpisode
|
||||
import ac.mdiq.podcini.ui.compose.CustomTheme
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.vista.extractor.services.youtube.YoutubeParsingHelper.isYoutubeServiceURL
|
||||
|
@ -52,7 +52,7 @@ class ShareReceiverActivity : AppCompatActivity() {
|
|||
setContent {
|
||||
val showDialog = remember { mutableStateOf(true) }
|
||||
CustomTheme(this) {
|
||||
ConfirmAddYoutubeEpisode1(listOf(sharedUrl!!), showDialog.value, onDismissRequest = {
|
||||
ConfirmAddYoutubeEpisode(listOf(sharedUrl!!), showDialog.value, onDismissRequest = {
|
||||
showDialog.value = false
|
||||
finish()
|
||||
})
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package ac.mdiq.podcini.ui.activity
|
||||
|
||||
import ac.mdiq.podcini.R
|
||||
import ac.mdiq.podcini.storage.database.Feeds.getFeedList
|
||||
import ac.mdiq.podcini.storage.model.Feed
|
||||
import ac.mdiq.podcini.ui.compose.CustomTheme
|
||||
import android.app.ActivityManager
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import coil.imageLoader
|
||||
import coil.request.ImageRequest
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
class SubscriptionShortcutActivity : AppCompatActivity() {
|
||||
private val listItems = mutableStateListOf<Feed>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
CustomTheme(this) {
|
||||
Card(modifier = Modifier.padding(vertical = 30.dp, horizontal = 16.dp), shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary)) {
|
||||
val textColor = MaterialTheme.colorScheme.onSurface
|
||||
Column {
|
||||
Text(stringResource(R.string.shortcut_select_subscription), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 5.dp))
|
||||
var checkedIndex by remember { mutableIntStateOf(-1) }
|
||||
val lazyListState = rememberLazyListState()
|
||||
LazyColumn(state = lazyListState, modifier = Modifier.weight(1f).fillMaxWidth().padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 20.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
itemsIndexed(listItems) { index, item ->
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clickable(onClick = { checkedIndex = index })) {
|
||||
var checked by remember { mutableStateOf(false) }
|
||||
Checkbox(checked = checkedIndex == index, onCheckedChange = {
|
||||
checkedIndex = index
|
||||
checked = it
|
||||
})
|
||||
Text(item.title?: "No title", color = textColor, style = MaterialTheme.typography.titleSmall)
|
||||
}
|
||||
}
|
||||
}
|
||||
Button(onClick = { if (checkedIndex >= 0 && Intent.ACTION_CREATE_SHORTCUT == intent.action) getBitmapFromUrl(listItems[checkedIndex]) }) { Text(stringResource(R.string.add_shortcut)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loadSubscriptions()
|
||||
}
|
||||
|
||||
private fun addShortcut(feed: Feed, bitmap: Bitmap?) {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
intent.setAction(Intent.ACTION_MAIN)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
intent.putExtra(MainActivity.Extras.fragment_feed_id.name, feed.id)
|
||||
val id = "subscription-" + feed.id
|
||||
|
||||
// val icon: IconCompat = if (bitmap != null) IconCompat.createWithAdaptiveBitmap(bitmap)
|
||||
val icon: IconCompat = if (bitmap != null) bitmapToIconCompat(bitmap, getAppIconSize())
|
||||
else IconCompat.createWithResource(this, R.drawable.ic_subscriptions_shortcut)
|
||||
|
||||
val shortcut: ShortcutInfoCompat = ShortcutInfoCompat.Builder(this, id)
|
||||
.setShortLabel(feed.title?:"")
|
||||
.setLongLabel(feed.eigenTitle?:"")
|
||||
.setIntent(intent)
|
||||
.setIcon(icon)
|
||||
.build()
|
||||
setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this, shortcut))
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun getBitmapFromUrl(feed: Feed) {
|
||||
val iconSize = (128 * resources.displayMetrics.density).toInt()
|
||||
val request = ImageRequest.Builder(this)
|
||||
.data(feed.imageUrl)
|
||||
.setHeader("User-Agent", "Mozilla/5.0")
|
||||
.placeholder(R.mipmap.ic_launcher)
|
||||
.listener(onError = {_, e -> addShortcut(feed, null) }, onSuccess = { _, result -> addShortcut(feed, result.drawable.toBitmap()) })
|
||||
.size(iconSize, iconSize)
|
||||
.build()
|
||||
imageLoader.enqueue(request)
|
||||
}
|
||||
|
||||
fun getAppIconSize(): Int {
|
||||
val activityManager = getSystemService(ActivityManager::class.java)
|
||||
val appIconSize = try { activityManager.launcherLargeIconSize } catch (e: Exception) { TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48f, resources.displayMetrics).toInt() }
|
||||
return appIconSize
|
||||
}
|
||||
|
||||
fun bitmapToIconCompat(bitmap: Bitmap, desiredSizeDp: Int): IconCompat {
|
||||
val desiredSizePx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, desiredSizeDp.toFloat(), resources.displayMetrics).toInt()
|
||||
val resizedBitmap = Bitmap.createScaledBitmap(bitmap, desiredSizePx, desiredSizePx, true)
|
||||
return IconCompat.createWithBitmap(resizedBitmap)
|
||||
}
|
||||
|
||||
private fun loadSubscriptions() {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val result = withContext(Dispatchers.IO) { getFeedList() }
|
||||
withContext(Dispatchers.Main) { listItems.addAll(result) }
|
||||
} catch (e: Throwable) { Log.e(TAG, Log.getStackTraceString(e)) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG: String = SubscriptionShortcutActivity::class.simpleName ?: "Anonymous"
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ import ac.mdiq.podcini.storage.database.Episodes.prefRemoveFromQueueMarkedPlayed
|
|||
import ac.mdiq.podcini.storage.database.Episodes.setPlayState
|
||||
import ac.mdiq.podcini.storage.database.Episodes.setPlayStateSync
|
||||
import ac.mdiq.podcini.storage.database.Feeds.addToMiscSyndicate
|
||||
import ac.mdiq.podcini.storage.database.Feeds.addToYoutubeSyndicate
|
||||
import ac.mdiq.podcini.storage.database.Feeds.allowForAutoDelete
|
||||
import ac.mdiq.podcini.storage.database.Queues
|
||||
import ac.mdiq.podcini.storage.database.Queues.addToQueueSync
|
||||
|
@ -48,7 +47,7 @@ import ac.mdiq.podcini.util.EventFlow
|
|||
import ac.mdiq.podcini.util.FlowEvent
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.MiscFormatter.formatDateTimeFlex
|
||||
import ac.mdiq.podcini.util.MiscFormatter.formatNumber
|
||||
import ac.mdiq.podcini.util.MiscFormatter.formatLargeInteger
|
||||
import ac.mdiq.podcini.util.MiscFormatter.localDateTimeString
|
||||
import ac.mdiq.vista.extractor.Vista
|
||||
import ac.mdiq.vista.extractor.services.youtube.YoutubeParsingHelper.isYoutubeServiceURL
|
||||
|
@ -101,7 +100,6 @@ import androidx.documentfile.provider.DocumentFile
|
|||
import coil.compose.AsyncImage
|
||||
import coil.request.CachePolicy
|
||||
import coil.request.ImageRequest
|
||||
import com.skydoves.balloon.textForm
|
||||
import io.realm.kotlin.notifications.SingleQueryChange
|
||||
import io.realm.kotlin.notifications.UpdatedObject
|
||||
import kotlinx.coroutines.*
|
||||
|
@ -457,7 +455,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList<EpisodeVM>, feed:
|
|||
|
||||
val showConfirmYoutubeDialog = remember { mutableStateOf(false) }
|
||||
val youtubeUrls = remember { mutableListOf<String>() }
|
||||
ConfirmAddYoutubeEpisode1(youtubeUrls, showConfirmYoutubeDialog.value, onDismissRequest = { showConfirmYoutubeDialog.value = false })
|
||||
ConfirmAddYoutubeEpisode(youtubeUrls, showConfirmYoutubeDialog.value, onDismissRequest = { showConfirmYoutubeDialog.value = false })
|
||||
|
||||
var showChooseRatingDialog by remember { mutableStateOf(false) }
|
||||
if (showChooseRatingDialog) ChooseRatingDialog(selected) { showChooseRatingDialog = false }
|
||||
|
@ -664,7 +662,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList<EpisodeVM>, feed:
|
|||
val dateSizeText = " · " + formatDateTimeFlex(vm.episode.getPubDate()) +
|
||||
" · " + getDurationStringLong(vm.durationState) +
|
||||
(if ((vm.episode.media?.size ?: 0) > 0) " · " + Formatter.formatShortFileSize(curContext, vm.episode.media?.size ?: 0) else "") +
|
||||
(if (vm.viewCount > 0) " · " + formatNumber(vm.viewCount) else "")
|
||||
(if (vm.viewCount > 0) " · " + formatLargeInteger(vm.viewCount) else "")
|
||||
Text(dateSizeText, color = textColor, style = MaterialTheme.typography.bodySmall, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
}
|
||||
} else {
|
||||
|
@ -684,7 +682,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList<EpisodeVM>, feed:
|
|||
Text(dateSizeText, color = textColor, style = MaterialTheme.typography.bodySmall, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
val dateSizeText = formatDateTimeFlex(vm.episode.getPubDate()) + (if (vm.viewCount > 0) " · " + formatNumber(vm.viewCount) else "")
|
||||
val dateSizeText = formatDateTimeFlex(vm.episode.getPubDate()) + (if (vm.viewCount > 0) " · " + formatLargeInteger(vm.viewCount) else "")
|
||||
Text(dateSizeText, color = textColor, style = MaterialTheme.typography.bodySmall, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
if (vm.viewCount > 0)
|
||||
Icon(imageVector = ImageVector.vectorResource(R.drawable.baseline_people_alt_24), tint = textColor, contentDescription = "people", modifier = Modifier.width(16.dp).height(16.dp))
|
||||
|
@ -899,7 +897,7 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList<EpisodeVM>, feed:
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun ConfirmAddYoutubeEpisode1(sharedUrls: List<String>, showDialog: Boolean, onDismissRequest: () -> Unit) {
|
||||
fun ConfirmAddYoutubeEpisode(sharedUrls: List<String>, showDialog: Boolean, onDismissRequest: () -> Unit) {
|
||||
val TAG = "confirmAddEpisode"
|
||||
var showToast by remember { mutableStateOf(false) }
|
||||
var toastMassege by remember { mutableStateOf("")}
|
||||
|
|
|
@ -233,7 +233,7 @@ fun OnlineFeedItem(activity: MainActivity, feed: PodcastSearchResult, log: Subsc
|
|||
else -> ""
|
||||
}
|
||||
if (authorText.isNotEmpty()) Text(authorText, color = textColor, style = MaterialTheme.typography.bodyMedium)
|
||||
if (feed.subscriberCount > 0) Text(MiscFormatter.formatNumber(feed.subscriberCount) + " subscribers", color = textColor, style = MaterialTheme.typography.bodyMedium)
|
||||
if (feed.subscriberCount > 0) Text(MiscFormatter.formatLargeInteger(feed.subscriberCount) + " subscribers", color = textColor, style = MaterialTheme.typography.bodyMedium)
|
||||
Row {
|
||||
if (feed.count != null && feed.count > 0) Text(feed.count.toString() + " episodes", color = textColor, style = MaterialTheme.typography.bodyMedium)
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
|
|
@ -49,6 +49,7 @@ import ac.mdiq.podcini.util.EventFlow
|
|||
import ac.mdiq.podcini.util.FlowEvent
|
||||
import ac.mdiq.podcini.util.Logd
|
||||
import ac.mdiq.podcini.util.MiscFormatter
|
||||
import ac.mdiq.podcini.util.MiscFormatter.formatLargeInteger
|
||||
import android.app.Activity
|
||||
import android.content.*
|
||||
import android.os.Build
|
||||
|
@ -249,10 +250,6 @@ class AudioPlayerFragment : Fragment() {
|
|||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
SpeedometerWithArc(speed = curPlaybackSpeed*100, maxSpeed = 300f, trackColor = textColor,
|
||||
modifier = Modifier.width(43.dp).height(43.dp).clickable(onClick = { showSpeedDialog = true }))
|
||||
// Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_playback_speed), tint = textColor, contentDescription = "speed",
|
||||
// modifier = Modifier.width(43.dp).height(43.dp).clickable(onClick = {
|
||||
// VariableSpeedDialog.newInstance(booleanArrayOf(true, true, true), null)?.show(childFragmentManager, null)
|
||||
// }))
|
||||
Text(txtvPlaybackSpeed, color = textColor, style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
Spacer(Modifier.weight(0.1f))
|
||||
|
@ -337,6 +334,9 @@ class AudioPlayerFragment : Fragment() {
|
|||
Row {
|
||||
Text(DurationConverter.getDurationStringLong(currentPosition), color = textColor, style = MaterialTheme.typography.bodySmall)
|
||||
Spacer(Modifier.weight(1f))
|
||||
val bitrate = (curMedia as? EpisodeMedia)?.bitrate ?: 0
|
||||
if (bitrate > 0) Text(formatLargeInteger(bitrate) + "bits", color = textColor, style = MaterialTheme.typography.bodySmall)
|
||||
Spacer(Modifier.weight(1f))
|
||||
showTimeLeft = UserPreferences.shouldShowRemainingTime()
|
||||
Text(txtvLengtTexth, color = textColor, style = MaterialTheme.typography.bodySmall, modifier = Modifier.clickable {
|
||||
if (controller == null) return@clickable
|
||||
|
|
|
@ -125,23 +125,17 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
// }
|
||||
displayUpArrow = parentFragmentManager.backStackEntryCount != 0
|
||||
if (savedInstanceState != null) displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW)
|
||||
NavDrawerFragment.saveLastNavFragment(TAG, feedID.toString())
|
||||
|
||||
(activity as MainActivity).setupToolbarToggle(binding.toolbar, displayUpArrow)
|
||||
updateToolbar()
|
||||
|
||||
swipeActions = SwipeActions(this, TAG)
|
||||
fun filterClick() {
|
||||
if (enableFilter && feed != null) {
|
||||
showFilterDialog = true
|
||||
// val dialog = FeedEpisodeFilterDialog(feed)
|
||||
// dialog.filter = feed!!.episodeFilter
|
||||
// dialog.show(childFragmentManager, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun filterLongClick() {
|
||||
if (feed != null) {
|
||||
enableFilter = !enableFilter
|
||||
waitForLoading()
|
||||
while (loadItemsRunning) Thread.sleep(50)
|
||||
loadItemsRunning = true
|
||||
val etmp = mutableListOf<Episode>()
|
||||
if (enableFilter) {
|
||||
|
@ -153,8 +147,7 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
filterButtonColor.value = Color.Red
|
||||
etmp.addAll(feed!!.episodes)
|
||||
}
|
||||
val sortOrder = fromCode(feed?.preferences?.sortOrderCode ?: 0)
|
||||
if (sortOrder != null) getPermutor(sortOrder).reorder(etmp)
|
||||
getPermutor(fromCode(feed?.preferences?.sortOrderCode ?: 0)).reorder(etmp)
|
||||
episodes.clear()
|
||||
episodes.addAll(etmp)
|
||||
ieMap = episodes.withIndex().associate { (index, episode) -> episode.id to index }
|
||||
|
@ -198,7 +191,9 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
}
|
||||
Column {
|
||||
FeedEpisodesHeader(activity = (activity as MainActivity), filterButColor = filterButtonColor.value, filterClickCB = {filterClick()}, filterLongClickCB = {filterLongClick()})
|
||||
FeedEpisodesHeader(activity = (activity as MainActivity), filterButColor = filterButtonColor.value, filterClickCB = {
|
||||
if (enableFilter && feed != null) showFilterDialog = true
|
||||
}, filterLongClickCB = {filterLongClick()})
|
||||
InforBar(infoBarText, leftAction = leftActionState, rightAction = rightActionState, actionConfig = { swipeActions.showDialog() })
|
||||
EpisodeLazyColumn(activity as MainActivity, vms = vms, feed = feed, layoutMode = layoutMode,
|
||||
refreshCB = { FeedUpdateManager.runOnceOrAsk(requireContext(), feed) },
|
||||
|
@ -218,29 +213,12 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
lifecycle.addObserver(swipeActions)
|
||||
refreshSwipeTelltale()
|
||||
|
||||
// val iconTintManager: ToolbarIconTintManager = object : ToolbarIconTintManager(requireContext(), binding.toolbar, binding.collapsingToolbar) {
|
||||
// override fun doTint(themedContext: Context) {
|
||||
// binding.toolbar.menu.findItem(R.id.refresh_feed).setIcon(AppCompatResources.getDrawable(themedContext, R.drawable.ic_refresh))
|
||||
// binding.toolbar.menu.findItem(R.id.action_search).setIcon(AppCompatResources.getDrawable(themedContext, R.drawable.ic_search))
|
||||
// }
|
||||
// }
|
||||
// iconTintManager.updateTint()
|
||||
// binding.appBar.addOnOffsetChangedListener(iconTintManager)
|
||||
|
||||
// binding.swipeRefresh.setDistanceToTriggerSync(resources.getInteger(R.integer.swipe_refresh_distance))
|
||||
// binding.swipeRefresh.setProgressViewEndTarget(false, 0)
|
||||
// binding.swipeRefresh.setOnRefreshListener {
|
||||
// FeedUpdateManager.runOnceOrAsk(requireContext(), feed)
|
||||
// }
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
Logd(TAG, "onStart() called")
|
||||
super.onStart()
|
||||
// adapter.refreshFragPosCallback = ::refreshPosCallback
|
||||
loadFeed()
|
||||
procFlowEvents()
|
||||
}
|
||||
|
@ -262,41 +240,30 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
rating = feed!!.rating
|
||||
}
|
||||
ConstraintLayout(modifier = Modifier.fillMaxWidth().height(120.dp)) {
|
||||
val (bgImage, bgColor, controlRow, image1, image2, imgvCover, taColumn) = createRefs()
|
||||
AsyncImage(model = feed?.imageUrl?:"", contentDescription = "bgImage", contentScale = ContentScale.FillBounds,
|
||||
error = painterResource(R.drawable.teaser),
|
||||
modifier = Modifier.fillMaxSize().blur(radiusX = 15.dp, radiusY = 15.dp)
|
||||
.constrainAs(bgImage) {
|
||||
val (bgImage, bgColor, controlRow, imgvCover) = createRefs()
|
||||
AsyncImage(model = feed?.imageUrl?:"", contentDescription = "bgImage", contentScale = ContentScale.FillBounds, error = painterResource(R.drawable.teaser),
|
||||
modifier = Modifier.fillMaxSize().blur(radiusX = 15.dp, radiusY = 15.dp).constrainAs(bgImage) {
|
||||
bottom.linkTo(parent.bottom)
|
||||
top.linkTo(parent.top)
|
||||
start.linkTo(parent.start)
|
||||
end.linkTo(parent.end)
|
||||
})
|
||||
Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.surface.copy(alpha = 0.75f))
|
||||
.constrainAs(bgColor) {
|
||||
bottom.linkTo(parent.bottom)
|
||||
top.linkTo(parent.top)
|
||||
start.linkTo(parent.start)
|
||||
end.linkTo(parent.end)
|
||||
})
|
||||
Row(Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 2.dp)
|
||||
.constrainAs(controlRow) {
|
||||
bottom.linkTo(parent.bottom)
|
||||
start.linkTo(parent.start)
|
||||
width = Dimension.fillToConstraints
|
||||
}, verticalAlignment = Alignment.CenterVertically) {
|
||||
end.linkTo(parent.end) })
|
||||
Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.surface.copy(alpha = 0.75f)).constrainAs(bgColor) {
|
||||
bottom.linkTo(parent.bottom)
|
||||
top.linkTo(parent.top)
|
||||
start.linkTo(parent.start)
|
||||
end.linkTo(parent.end) })
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 2.dp).constrainAs(controlRow) {
|
||||
bottom.linkTo(parent.bottom)
|
||||
start.linkTo(parent.start)
|
||||
width = Dimension.fillToConstraints
|
||||
}) {
|
||||
Spacer(modifier = Modifier.weight(0.7f))
|
||||
val ratingIconRes = Rating.fromCode(rating).res
|
||||
Icon(imageVector = ImageVector.vectorResource(ratingIconRes), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "rating",
|
||||
modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).width(30.dp).height(30.dp).clickable(onClick = {
|
||||
showChooseRatingDialog = true
|
||||
}))
|
||||
modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).width(30.dp).height(30.dp).clickable(onClick = { showChooseRatingDialog = true }))
|
||||
Spacer(modifier = Modifier.weight(0.2f))
|
||||
Icon(imageVector = ImageVector.vectorResource(R.drawable.arrows_sort), tint = textColor, contentDescription = "butSort",
|
||||
modifier = Modifier.width(40.dp).height(40.dp).padding(3.dp).clickable(onClick = {
|
||||
showSortDialog = true
|
||||
// SingleFeedSortDialog(feed).show(childFragmentManager, "SortDialog")
|
||||
}))
|
||||
modifier = Modifier.width(40.dp).height(40.dp).padding(3.dp).clickable(onClick = { showSortDialog = true }))
|
||||
Spacer(modifier = Modifier.width(15.dp))
|
||||
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_filter_white), tint = if (filterButColor == Color.White) textColor else filterButColor, contentDescription = "butFilter",
|
||||
modifier = Modifier.width(40.dp).height(40.dp).padding(3.dp).combinedClickable(onClick = filterClickCB, onLongClick = filterLongClickCB))
|
||||
|
@ -311,16 +278,6 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
Spacer(modifier = Modifier.weight(0.4f))
|
||||
Text(episodes.size.toString() + " / " + feed?.episodes?.size?.toString(), textAlign = TextAlign.End, color = textColor, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyLarge)
|
||||
}
|
||||
// Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_rounded_corner_left), contentDescription = "left_corner",
|
||||
// Modifier.width(12.dp).height(12.dp).constrainAs(image1) {
|
||||
// bottom.linkTo(parent.bottom)
|
||||
// start.linkTo(parent.start)
|
||||
// })
|
||||
// Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_rounded_corner_right), contentDescription = "right_corner",
|
||||
// Modifier.width(12.dp).height(12.dp).constrainAs(image2) {
|
||||
// bottom.linkTo(parent.bottom)
|
||||
// end.linkTo(parent.end)
|
||||
// })
|
||||
Row(verticalAlignment = Alignment.Top, modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp).constrainAs(imgvCover) {
|
||||
top.linkTo(parent.top)
|
||||
start.linkTo(parent.start)
|
||||
|
@ -381,14 +338,6 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
super.onDestroyView()
|
||||
}
|
||||
|
||||
//
|
||||
// private fun refreshPosCallback(pos: Int, episode: Episode) {
|
||||
// Logd(TAG, "FeedEpisode refreshPosCallback: $pos ${episode.title}")
|
||||
//// if (pos >= 0 && pos < episodes.size) episodes[pos] = episode
|
||||
// redoFilter()
|
||||
//// adapter.notifyDataSetChanged()
|
||||
// }
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putBoolean(KEY_UP_ARROW, displayUpArrow)
|
||||
super.onSaveInstanceState(outState)
|
||||
|
@ -441,14 +390,12 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
(activity as MainActivity).loadChildFragment(qFrag)
|
||||
(activity as MainActivity).bottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
// R.id.init_tts -> initTTS()
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onPlayEvent(event: FlowEvent.PlayEvent) {
|
||||
// Logd(TAG, "onPlayEvent ${event.episode.title}")
|
||||
if (feed != null) {
|
||||
val pos: Int = ieMap[event.episode.id] ?: -1
|
||||
if (pos >= 0) {
|
||||
|
@ -467,7 +414,6 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
val pos: Int = ueMap[url] ?: -1
|
||||
if (pos >= 0) {
|
||||
Logd(TAG, "onEpisodeDownloadEvent $pos ${event.map[url]?.state} ${episodes[pos].media?.downloaded} ${episodes[pos].title}")
|
||||
// episodes[pos].downloadState.value = event.map[url]?.state ?: DownloadStatus.State.UNKNOWN.ordinal
|
||||
vms[pos].downloadState = event.map[url]?.state ?: DownloadStatus.State.UNKNOWN.ordinal
|
||||
}
|
||||
}
|
||||
|
@ -517,12 +463,9 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
|
||||
private fun onFeedUpdateRunningEvent(event: FlowEvent.FeedUpdatingEvent) {
|
||||
// nextPageLoader.setLoadingState(event.isRunning)
|
||||
// if (!event.isRunning) nextPageLoader.root.visibility = View.GONE
|
||||
infoTextUpdate = if (event.isRunning) getString(R.string.refreshing_label) else ""
|
||||
infoBarText.value = "$infoTextFiltered $infoTextUpdate"
|
||||
if (!event.isRunning) loadFeed()
|
||||
// binding.swipeRefresh.isRefreshing = event.isRunning
|
||||
}
|
||||
|
||||
private fun refreshSwipeTelltale() {
|
||||
|
@ -530,16 +473,12 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
rightActionState.value = swipeActions.actions.right[0]
|
||||
}
|
||||
|
||||
private fun refreshHeaderView() {
|
||||
setupHeaderView()
|
||||
private fun refreshHeaderView() {
|
||||
if (feed == null) {
|
||||
Log.e(TAG, "Unable to refresh header view")
|
||||
return
|
||||
}
|
||||
// loadFeedImage()
|
||||
// if (feed!!.lastUpdateFailed) binding.header.txtvFailure.visibility = View.VISIBLE
|
||||
// else binding.header.txtvFailure.visibility = View.GONE
|
||||
|
||||
if (!headerCreated) headerCreated = true
|
||||
infoTextFiltered = ""
|
||||
if (!feed?.preferences?.filterString.isNullOrEmpty()) {
|
||||
val filter: EpisodeFilter = feed!!.episodeFilter
|
||||
|
@ -548,36 +487,6 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
infoBarText.value = "$infoTextFiltered $infoTextUpdate"
|
||||
}
|
||||
|
||||
private fun setupHeaderView() {
|
||||
if (feed == null || headerCreated) return
|
||||
|
||||
// binding.imgvBackground.colorFilter = LightingColorFilter(-0x99999a, 0x000000)
|
||||
// binding.header.imgvCover.setOnClickListener { showFeedInfo() }
|
||||
|
||||
// binding.header.txtvFailure.setOnClickListener { showErrorDetails() }
|
||||
headerCreated = true
|
||||
}
|
||||
|
||||
// private fun showErrorDetails() {
|
||||
// lifecycleScope.launch {
|
||||
// val downloadResult = withContext(Dispatchers.IO) {
|
||||
// val feedDownloadLog: List<DownloadResult> = getFeedDownloadLog(feedID)
|
||||
// if (feedDownloadLog.isEmpty() || feedDownloadLog[0].isSuccessful) null else feedDownloadLog[0]
|
||||
// }
|
||||
// withContext(Dispatchers.Main) {
|
||||
// if (downloadResult != null) DownloadLogDetailsDialog(requireContext(), downloadResult).show()
|
||||
// else DownloadLogFragment().show(childFragmentManager, null)
|
||||
// }
|
||||
// }.invokeOnCompletion { throwable ->
|
||||
// throwable?.printStackTrace()
|
||||
// }
|
||||
// }
|
||||
|
||||
private var loadItemsRunning = false
|
||||
private fun waitForLoading() {
|
||||
while (loadItemsRunning) Thread.sleep(50)
|
||||
}
|
||||
|
||||
private fun isFilteredOut(episode: Episode): Boolean {
|
||||
if (enableFilter && !feed?.preferences?.filterString.isNullOrEmpty()) {
|
||||
val episodes_ = realm.query(Episode::class).query("feedId == ${feed!!.id}").query(feed!!.episodeFilter.queryString()).find()
|
||||
|
@ -591,7 +500,8 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
private var loadItemsRunning = false
|
||||
private fun loadFeed() {
|
||||
if (!loadItemsRunning) {
|
||||
loadItemsRunning = true
|
||||
|
@ -635,12 +545,7 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
withContext(Dispatchers.Main) {
|
||||
Logd(TAG, "loadItems subscribe called ${feed?.title}")
|
||||
rating = feed?.rating ?: Rating.UNRATED.code
|
||||
// swipeActions.setFilter(feed?.episodeFilter)
|
||||
refreshHeaderView()
|
||||
// if (feed != null) {
|
||||
// adapter.updateItems(episodes, feed)
|
||||
// binding.header.counts.text = episodes.size.toString()
|
||||
// }
|
||||
updateToolbar()
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
|
@ -676,7 +581,6 @@ class FeedEpisodesFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
|
||||
companion object {
|
||||
val TAG = FeedEpisodesFragment::class.simpleName ?: "Anonymous"
|
||||
|
||||
const val ARGUMENT_FEED_ID = "argument.ac.mdiq.podcini.feed_id"
|
||||
private const val KEY_UP_ARROW = "up_arrow"
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import ac.mdiq.podcini.storage.model.Rating.Companion.fromCode
|
|||
import ac.mdiq.podcini.ui.actions.DownloadActionButton
|
||||
import ac.mdiq.podcini.ui.activity.MainActivity
|
||||
import ac.mdiq.podcini.ui.activity.ShareReceiverActivity.Companion.receiveShared
|
||||
import ac.mdiq.podcini.ui.compose.ConfirmAddYoutubeEpisode1
|
||||
import ac.mdiq.podcini.ui.compose.ConfirmAddYoutubeEpisode
|
||||
import ac.mdiq.podcini.ui.compose.CustomTheme
|
||||
import ac.mdiq.podcini.util.EventFlow
|
||||
import ac.mdiq.podcini.util.FlowEvent
|
||||
|
@ -120,7 +120,7 @@ class LogsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
|
|||
var showYTMediaConfirmDialog by remember { mutableStateOf(false) }
|
||||
var sharedUrl by remember { mutableStateOf("") }
|
||||
if (showYTMediaConfirmDialog)
|
||||
ConfirmAddYoutubeEpisode1(listOf(sharedUrl), showYTMediaConfirmDialog, onDismissRequest = { showYTMediaConfirmDialog = false })
|
||||
ConfirmAddYoutubeEpisode(listOf(sharedUrl), showYTMediaConfirmDialog, onDismissRequest = { showYTMediaConfirmDialog = false })
|
||||
|
||||
LazyColumn(state = lazyListState, modifier = Modifier.padding(start = 10.dp, end = 6.dp, top = 5.dp, bottom = 5.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package ac.mdiq.podcini.ui.fragment
|
||||
|
||||
import ac.mdiq.podcini.R
|
||||
import ac.mdiq.podcini.playback.base.InTheatre.curQueue
|
||||
import ac.mdiq.podcini.preferences.UserPreferences.hiddenDrawerItems
|
||||
import ac.mdiq.podcini.storage.database.Episodes.getEpisodesCount
|
||||
import ac.mdiq.podcini.storage.database.Feeds.getFeedCount
|
||||
|
@ -135,7 +134,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
|
|||
}
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
Text("Currently launching on Google Play, please kindly support with closed testing. Thank you!", color = MaterialTheme.colorScheme.tertiary,
|
||||
Text("Currently launching on Google Play, early testers will become pre-IPO members. Click for details", color = MaterialTheme.colorScheme.tertiary,
|
||||
modifier = Modifier.clickable(onClick = {
|
||||
openInBrowser(requireContext(), "https://github.com/XilinJia/Podcini/discussions/120")
|
||||
}))
|
||||
|
@ -197,6 +196,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
|
|||
|
||||
@VisibleForTesting
|
||||
const val PREF_LAST_FRAGMENT_TAG: String = "prefLastFragmentTag"
|
||||
const val PREF_LAST_FRAGMENT_ARG: String = "prefLastFragmentArg"
|
||||
|
||||
@VisibleForTesting
|
||||
const val PREF_NAME: String = "NavDrawerPrefs"
|
||||
|
@ -220,26 +220,24 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
|
|||
SubscriptionsFragment.TAG to NavItem(SubscriptionsFragment.TAG, R.drawable.ic_subscriptions, R.string.subscriptions_label),
|
||||
QueuesFragment.TAG to NavItem(QueuesFragment.TAG, R.drawable.ic_playlist_play, R.string.queue_label),
|
||||
EpisodesFragment.TAG to NavItem(EpisodesFragment.TAG, R.drawable.ic_feed, R.string.episodes_label),
|
||||
// AllEpisodesFragment.TAG to NavItem(AllEpisodesFragment.TAG, R.drawable.ic_feed, R.string.episodes_label),
|
||||
// DownloadsFragment.TAG to NavItem(DownloadsFragment.TAG, R.drawable.ic_download, R.string.downloads_label),
|
||||
// HistoryFragment.TAG to NavItem(HistoryFragment.TAG, R.drawable.baseline_work_history_24, R.string.playback_history_label),
|
||||
LogsFragment.TAG to NavItem(LogsFragment.TAG, R.drawable.ic_history, R.string.logs_label),
|
||||
StatisticsFragment.TAG to NavItem(StatisticsFragment.TAG, R.drawable.ic_chart_box, R.string.statistics_label),
|
||||
OnlineSearchFragment.TAG to NavItem(OnlineSearchFragment.TAG, R.drawable.ic_add, R.string.add_feed_label)
|
||||
)
|
||||
|
||||
fun saveLastNavFragment(tag: String?) {
|
||||
fun saveLastNavFragment(tag: String?, arg: String? = null) {
|
||||
Logd(TAG, "saveLastNavFragment(tag: $tag)")
|
||||
val edit: SharedPreferences.Editor? = prefs?.edit()
|
||||
if (tag != null) edit?.putString(PREF_LAST_FRAGMENT_TAG, tag)
|
||||
if (tag != null) {
|
||||
edit?.putString(PREF_LAST_FRAGMENT_TAG, tag)
|
||||
if (arg != null) edit?.putString(PREF_LAST_FRAGMENT_ARG, arg)
|
||||
}
|
||||
else edit?.remove(PREF_LAST_FRAGMENT_TAG)
|
||||
edit?.apply()
|
||||
}
|
||||
|
||||
fun getLastNavFragment(): String {
|
||||
val lastFragment: String = prefs?.getString(PREF_LAST_FRAGMENT_TAG, SubscriptionsFragment.TAG)?:""
|
||||
return lastFragment
|
||||
}
|
||||
fun getLastNavFragment(): String = prefs?.getString(PREF_LAST_FRAGMENT_TAG, SubscriptionsFragment.TAG) ?: ""
|
||||
fun getLastNavFragmentArg(): String = prefs?.getString(PREF_LAST_FRAGMENT_ARG, "0") ?: ""
|
||||
|
||||
/**
|
||||
* Returns data necessary for displaying the navigation drawer. This includes
|
||||
|
@ -251,9 +249,6 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
|
|||
feedCount = getFeedCount()
|
||||
navMap[QueuesFragment.TAG]?.count = realm.query(PlayQueue::class).find().sumOf { it.size()}
|
||||
navMap[SubscriptionsFragment.TAG]?.count = feedCount
|
||||
// navMap[HistoryFragment.TAG]?.count = getNumberOfPlayed().toInt()
|
||||
// navMap[DownloadsFragment.TAG]?.count = getEpisodesCount(EpisodeFilter(EpisodeFilter.States.downloaded.name))
|
||||
// navMap[AllEpisodesFragment.TAG]?.count = numItems
|
||||
navMap[EpisodesFragment.TAG]?.count = numItems
|
||||
navMap[LogsFragment.TAG]?.count = realm.query(ShareLog::class).count().find().toInt() +
|
||||
realm.query(SubscriptionLog::class).count().find().toInt() +
|
||||
|
|
|
@ -74,7 +74,7 @@ object MiscFormatter {
|
|||
return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
|
||||
}
|
||||
|
||||
fun formatNumber(n: Int): String {
|
||||
fun formatLargeInteger(n: Int): String {
|
||||
return when {
|
||||
n < 1000 -> n.toString()
|
||||
n < 1_000_000 -> String.format(Locale.getDefault(), "%.2fK", n / 1000.0)
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Based on simple_list_item_multiple_choice.xml
|
||||
from The Android Open Source Project
|
||||
This one puts the check box at the start of the view (rather than at the end).
|
||||
-->
|
||||
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeightSmall"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSmall"
|
||||
android:gravity="center_vertical"
|
||||
android:drawableStart="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:drawableLeft="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
/>
|
|
@ -1,58 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/transparentBackground"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="32dp"
|
||||
android:elevation="16dp"
|
||||
app:cardCornerRadius="4dp">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/actionBarSize"
|
||||
android:theme="?attr/actionBarTheme"
|
||||
android:layout_alignParentTop="true" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@id/toolbar"
|
||||
android:background="?android:attr/listDivider" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:layout_below="@id/divider"
|
||||
android:paddingBottom="88dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/shortcutBtn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:text="@string/add_shortcut" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
|
@ -1,3 +1,10 @@
|
|||
# 6.15.2
|
||||
|
||||
* FeedEpisodes is remembered as last opened page and can be opened if default page set to "Remember"
|
||||
* revamped SubscriptionShortcut activity in Compose to create proper shortcuts on home screen of the phone
|
||||
* feed shortcut on home screen opens the feed (with FeedEpisodes) in Podcini
|
||||
* when playing a youtube audio, bitrate is shown in PlayerUI
|
||||
|
||||
# 6.15.1
|
||||
|
||||
* Consolidated Compose code blocks in PreferenceActivity with function calls
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Version 6.15.2
|
||||
|
||||
* FeedEpisodes is remembered as last opened page and can be opened if default page set to "Remember"
|
||||
* revamped SubscriptionShortcut activity in Compose to create proper shortcuts on home screen of the phone
|
||||
* feed shortcut on home screen opens the feed (with FeedEpisodes) in Podcini
|
||||
* when playing a youtube audio, bitrate is shown in PlayerUI
|
|
@ -466,7 +466,7 @@ QStatusBar QComboBox::down-arrow {
|
|||
padding: 0;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
image: url(/tmp/ZPXwRX.png)
|
||||
image: url(/tmp/ZBphJY.png)
|
||||
}
|
||||
|
||||
QStatusBar QComboBox::drop-down:hover {
|
||||
|
|
Loading…
Reference in New Issue