6.12.4 commit
This commit is contained in:
parent
4001693f10
commit
25893b79ea
|
@ -56,7 +56,6 @@ While podcast subscriptions' OPML files (from AntennaPod or any other sources) c
|
|||
* single tap not during play has no effect
|
||||
* Added preference "Fallback Speed" under "Playback" in settings with default value of 0.0, dialog allows setting a float number (capped between 0.0 and 1.5)
|
||||
* if the user customizes "Fallback speed" to a value greater than 0.1, long-press the Play button during play enters the fallback mode and plays at the set fallback speed, single tap exits the fallback mode
|
||||
* Various efficiency improvements
|
||||
* streamed media somewhat equivalent to downloaded media
|
||||
* enabled episode description on player detailed view
|
||||
* enabled intro- and end- skipping
|
||||
|
@ -65,7 +64,6 @@ While podcast subscriptions' OPML files (from AntennaPod or any other sources) c
|
|||
* new video episode view, with video player on top and episode descriptions in portrait mode
|
||||
* easy switches on video player to other video mode or audio only, in seamless way
|
||||
* video player automatically switch to audio when app invisible
|
||||
* default video player mode setting in preferences
|
||||
* when video mode is set to audio only, click on image on audio player on a video episode brings up the normal player detailed view
|
||||
* "Prefer streaming over download" is now on setting of individual feed
|
||||
* added setting in individual feed to play audio only for video feeds,
|
||||
|
@ -115,13 +113,11 @@ While podcast subscriptions' OPML files (from AntennaPod or any other sources) c
|
|||
* Every feed (podcast) can be associated with a queue allowing downloaded media to be added to the queue
|
||||
* FeedInfo view offers a link for direct search of feeds related to author
|
||||
* FeedInfo view has button showing number of episodes to open the FeedEpisodes view
|
||||
* FeedInfo view has feed setting in the header
|
||||
* instead of isFavorite, there is a new rating system for every episode: Trash, Bad, Neutral, Good, Favorite
|
||||
* instead of Played or Unplayed, there is a new play state system Unspecified, Building, New, Unplayed, Later, Soon, InQueue, InProgress, Skipped, Played, Ignored
|
||||
* among which Unplayed, Later, Soon, Skipped, Played, Ignored are settable by the user
|
||||
* when an episode is started to play, its state is set to InProgress
|
||||
* when episode is added to a queue, its state is set to InQueue, when it's removed from a queue, the state (if lower than Skipped) is set to Skipped
|
||||
* in EpisodeInfo view, "mark played/unplayed", "add to/remove from queue"
|
||||
* in EpisodeInfo view, one can enter personal comments/notes under "My opinion" for the episode
|
||||
* in FeedInfo view, one can enter personal comments/notes under "My opinion" for the feed
|
||||
* New episode home view with two display modes: webpage or reader
|
||||
|
@ -133,7 +129,7 @@ While podcast subscriptions' OPML files (from AntennaPod or any other sources) c
|
|||
|
||||
* Long-press on a feed in online feed list prompts to subscribe it straight out.
|
||||
* More info about feeds are shown in the online search view
|
||||
* Ability to open podcast from webpage address
|
||||
* Ability to open podcast with webpage address
|
||||
* Online feed info display is handled in similar ways as any local feed, and offers options to subscribe or view episodes
|
||||
* Online feed episodes can be freely played (streamed) without a subscription
|
||||
* Online feed episodes can be selectively reserved into synthetic podcasts
|
||||
|
|
|
@ -720,14 +720,6 @@ class OnlineFeedFragment : Fragment() {
|
|||
updateToolbar()
|
||||
return root
|
||||
}
|
||||
// override fun onStart() {
|
||||
// super.onStart()
|
||||
//// procFlowEvents()
|
||||
// }
|
||||
// override fun onStop() {
|
||||
// super.onStop()
|
||||
//// cancelFlowEvents()
|
||||
// }
|
||||
override fun onDestroyView() {
|
||||
episodeList.clear()
|
||||
super.onDestroyView()
|
||||
|
@ -760,23 +752,6 @@ class OnlineFeedFragment : Fragment() {
|
|||
else -> return false
|
||||
}
|
||||
}
|
||||
// private var eventSink: Job? = null
|
||||
// private fun cancelFlowEvents() {
|
||||
// eventSink?.cancel()
|
||||
// eventSink = null
|
||||
// }
|
||||
// private fun procFlowEvents() {
|
||||
// if (eventSink != null) return
|
||||
// eventSink = lifecycleScope.launch {
|
||||
// EventFlow.events.collectLatest { event ->
|
||||
// Logd(TAG, "Received event: ${event.TAG}")
|
||||
// when (event) {
|
||||
// is FlowEvent.AllEpisodesFilterEvent -> page = 1
|
||||
// else -> {}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
companion object {
|
||||
const val PREF_NAME: String = "EpisodesListFragment"
|
||||
|
|
|
@ -233,7 +233,6 @@ class SearchFragment : Fragment() {
|
|||
// results[pos].downloadState.value = event.map[url]?.state ?: DownloadStatus.State.UNKNOWN.ordinal
|
||||
vms[pos].downloadState = event.map[url]?.state ?: DownloadStatus.State.UNKNOWN.ordinal
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,6 +290,53 @@ class SearchFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FeedsRow() {
|
||||
val context = LocalContext.current
|
||||
val lazyGridState = rememberLazyListState()
|
||||
LazyRow (state = lazyGridState, horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = PaddingValues(start = 12.dp, top = 16.dp, end = 12.dp, bottom = 16.dp)
|
||||
) {
|
||||
items(resultFeeds.size, key = {index -> resultFeeds[index].id}) { index ->
|
||||
val feed by remember { mutableStateOf(resultFeeds[index]) }
|
||||
ConstraintLayout {
|
||||
val (coverImage, episodeCount, rating, error) = createRefs()
|
||||
val imgLoc = remember(feed) { feed.imageUrl }
|
||||
AsyncImage(model = ImageRequest.Builder(context).data(imgLoc)
|
||||
.memoryCachePolicy(CachePolicy.ENABLED).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).build(),
|
||||
contentDescription = "coverImage",
|
||||
modifier = Modifier.height(100.dp).aspectRatio(1f)
|
||||
.constrainAs(coverImage) {
|
||||
top.linkTo(parent.top)
|
||||
bottom.linkTo(parent.bottom)
|
||||
start.linkTo(parent.start)
|
||||
}.combinedClickable(onClick = {
|
||||
Logd(SubscriptionsFragment.TAG, "clicked: ${feed.title}")
|
||||
(activity as MainActivity).loadChildFragment(FeedEpisodesFragment.newInstance(feed.id))
|
||||
}, onLongClick = {
|
||||
Logd(SubscriptionsFragment.TAG, "long clicked: ${feed.title}")
|
||||
// val inflater: MenuInflater = (activity as MainActivity).menuInflater
|
||||
// inflater.inflate(R.menu.feed_context, contextMenu)
|
||||
// contextMenu.setHeaderTitle(feed.title)
|
||||
})
|
||||
)
|
||||
Text(NumberFormat.getInstance().format(feed.episodes.size.toLong()), color = Color.Green,
|
||||
modifier = Modifier.background(Color.Gray).constrainAs(episodeCount) {
|
||||
end.linkTo(parent.end)
|
||||
top.linkTo(coverImage.top)
|
||||
})
|
||||
if (feed.rating != Rating.UNRATED.code)
|
||||
Icon(imageVector = ImageVector.vectorResource(Rating.fromCode(feed.rating).res), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "rating",
|
||||
modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).constrainAs(rating) {
|
||||
start.linkTo(parent.start)
|
||||
centerVerticallyTo(coverImage)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@UnstableApi private fun performSearch(): Pair<List<Episode>, List<Feed>> {
|
||||
val query = searchView.query.toString()
|
||||
if (query.isEmpty()) return Pair<List<Episode>, List<Feed>>(emptyList(), emptyList())
|
||||
|
@ -393,53 +439,6 @@ class SearchFragment : Fragment() {
|
|||
(activity as MainActivity).loadChildFragment(SearchResultsFragment.newInstance(CombinedSearcher::class.java, query))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FeedsRow() {
|
||||
val context = LocalContext.current
|
||||
val lazyGridState = rememberLazyListState()
|
||||
LazyRow (state = lazyGridState, horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = PaddingValues(start = 12.dp, top = 16.dp, end = 12.dp, bottom = 16.dp)
|
||||
) {
|
||||
items(resultFeeds.size, key = {index -> resultFeeds[index].id}) { index ->
|
||||
val feed by remember { mutableStateOf(resultFeeds[index]) }
|
||||
ConstraintLayout {
|
||||
val (coverImage, episodeCount, rating, error) = createRefs()
|
||||
val imgLoc = remember(feed) { feed.imageUrl }
|
||||
AsyncImage(model = ImageRequest.Builder(context).data(imgLoc)
|
||||
.memoryCachePolicy(CachePolicy.ENABLED).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).build(),
|
||||
contentDescription = "coverImage",
|
||||
modifier = Modifier.height(100.dp).aspectRatio(1f)
|
||||
.constrainAs(coverImage) {
|
||||
top.linkTo(parent.top)
|
||||
bottom.linkTo(parent.bottom)
|
||||
start.linkTo(parent.start)
|
||||
}.combinedClickable(onClick = {
|
||||
Logd(SubscriptionsFragment.TAG, "clicked: ${feed.title}")
|
||||
(activity as MainActivity).loadChildFragment(FeedEpisodesFragment.newInstance(feed.id))
|
||||
}, onLongClick = {
|
||||
Logd(SubscriptionsFragment.TAG, "long clicked: ${feed.title}")
|
||||
// val inflater: MenuInflater = (activity as MainActivity).menuInflater
|
||||
// inflater.inflate(R.menu.feed_context, contextMenu)
|
||||
// contextMenu.setHeaderTitle(feed.title)
|
||||
})
|
||||
)
|
||||
Text(NumberFormat.getInstance().format(feed.episodes.size.toLong()), color = Color.Green,
|
||||
modifier = Modifier.background(Color.Gray).constrainAs(episodeCount) {
|
||||
end.linkTo(parent.end)
|
||||
top.linkTo(coverImage.top)
|
||||
})
|
||||
if (feed.rating != Rating.UNRATED.code)
|
||||
Icon(imageVector = ImageVector.vectorResource(Rating.fromCode(feed.rating).res), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "rating",
|
||||
modifier = Modifier.background(MaterialTheme.colorScheme.tertiaryContainer).constrainAs(rating) {
|
||||
start.linkTo(parent.start)
|
||||
centerVerticallyTo(coverImage)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG: String = SearchFragment::class.simpleName ?: "Anonymous"
|
||||
private const val ARG_QUERY = "query"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_search"
|
||||
android:icon="@drawable/ic_search"
|
||||
app:showAsAction="collapseActionView|always"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
android:title="@string/search_label"/>
|
||||
android:id="@+id/action_search"
|
||||
android:icon="@drawable/ic_search"
|
||||
app:showAsAction="collapseActionView|always"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
android:title="@string/search_label"/>
|
||||
</menu>
|
|
@ -204,7 +204,6 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
} while (nextPlayable != null && !CastUtils.isCastable(nextPlayable, castContext.sessionManager.currentCastSession))
|
||||
|
||||
if (nextPlayable != null) playMediaObject(nextPlayable, stream, startWhenPrepared, prepareImmediately, forceReset)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -224,11 +223,10 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
callback.onPlaybackPause(curMedia, pos)
|
||||
}
|
||||
|
||||
if (prevMedia != null && curMedia!!.getIdentifier() != prevMedia?.getIdentifier()) {
|
||||
if (prevMedia != null && curMedia!!.getIdentifier() != prevMedia?.getIdentifier())
|
||||
callback.onPostPlayback(prevMedia, false, skipped = false, playingNext = true)
|
||||
}
|
||||
prevMedia = curMedia
|
||||
|
||||
prevMedia = curMedia
|
||||
setPlayerStatus(PlayerStatus.INDETERMINATE, null)
|
||||
}
|
||||
}
|
||||
|
@ -275,7 +273,7 @@ class CastPsmp(context: Context, callback: MediaPlayerCallback) : MediaPlayerBas
|
|||
|
||||
override fun seekTo(t: Int) {
|
||||
Exception("Seeking to $t").printStackTrace()
|
||||
remoteMediaClient!!.seek(MediaSeekOptions.Builder().setPosition(t.toLong()).build())
|
||||
remoteMediaClient!!.seek(MediaSeekOptions.Builder().setPosition(t.toLong()).setResumeState(MediaSeekOptions.RESUME_STATE_PLAY).build())
|
||||
}
|
||||
|
||||
override fun getDuration(): Int {
|
||||
|
|
Loading…
Reference in New Issue