This commit is contained in:
Jarne Demeulemeester 2021-12-28 18:26:43 +01:00
parent 293e71fc27
commit 1e9ddd1173
No known key found for this signature in database
GPG key ID: B61B7B150DB6A6D2
9 changed files with 36 additions and 128 deletions

View file

@ -6,16 +6,11 @@ import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import dev.jdtech.jellyfin.adapters.DownloadsListAdapter
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
import dev.jdtech.jellyfin.adapters.HomeItem
import dev.jdtech.jellyfin.adapters.PersonListAdapter
import dev.jdtech.jellyfin.adapters.ServerGridAdapter import dev.jdtech.jellyfin.adapters.ServerGridAdapter
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
import dev.jdtech.jellyfin.adapters.ViewListAdapter
import dev.jdtech.jellyfin.api.JellyfinApi import dev.jdtech.jellyfin.api.JellyfinApi
import dev.jdtech.jellyfin.database.Server import dev.jdtech.jellyfin.database.Server
import dev.jdtech.jellyfin.models.DownloadSection
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.BaseItemPerson import org.jellyfin.sdk.model.api.BaseItemPerson
import org.jellyfin.sdk.model.api.ImageType import org.jellyfin.sdk.model.api.ImageType
@ -57,12 +52,6 @@ fun bindItemBackdropById(imageView: ImageView, itemId: UUID) {
imageView.loadImage("/items/$itemId/Images/${ImageType.BACKDROP}") imageView.loadImage("/items/$itemId/Images/${ImageType.BACKDROP}")
} }
@BindingAdapter("people")
fun bindPeople(recyclerView: RecyclerView, data: List<BaseItemPerson>?) {
val adapter = recyclerView.adapter as PersonListAdapter
adapter.submitList(data)
}
@BindingAdapter("personImage") @BindingAdapter("personImage")
fun bindPersonImage(imageView: ImageView, person: BaseItemPerson) { fun bindPersonImage(imageView: ImageView, person: BaseItemPerson) {
imageView imageView

View file

@ -21,7 +21,6 @@ import java.util.UUID
* Jellyfin API class using org.jellyfin.sdk:jellyfin-platform-android * Jellyfin API class using org.jellyfin.sdk:jellyfin-platform-android
* *
* @param androidContext The context * @param androidContext The context
* @param baseUrl The url of the server
* @constructor Creates a new [JellyfinApi] instance * @constructor Creates a new [JellyfinApi] instance
*/ */
class JellyfinApi(androidContext: Context) { class JellyfinApi(androidContext: Context) {

View file

@ -815,11 +815,11 @@ class MPVPlayer(
override fun seekTo(windowIndex: Int, positionMs: Long) { override fun seekTo(windowIndex: Int, positionMs: Long) {
if (windowIndex == 0) { if (windowIndex == 0) {
val seekTo = if (positionMs != C.TIME_UNSET) positionMs / C.MILLIS_PER_SECOND else initialSeekTo val seekTo = if (positionMs != C.TIME_UNSET) positionMs / C.MILLIS_PER_SECOND else initialSeekTo
if (isPlayerReady) { initialSeekTo = if (isPlayerReady) {
MPVLib.command(arrayOf("seek", "$seekTo", "absolute")) MPVLib.command(arrayOf("seek", "$seekTo", "absolute"))
initialSeekTo = 0L 0L
} else { } else {
initialSeekTo = seekTo seekTo
} }
} }
} }

View file

@ -10,6 +10,7 @@ import android.widget.ImageButton
import android.widget.PopupWindow import android.widget.PopupWindow
import android.widget.TextView import android.widget.TextView
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.core.content.res.ResourcesCompat
import androidx.navigation.navArgs import androidx.navigation.navArgs
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -69,12 +70,14 @@ internal class TvPlayerActivity : BasePlayerActivity() {
when { when {
viewModel.player.isPlaying -> { viewModel.player.isPlaying -> {
viewModel.player.pause() viewModel.player.pause()
setImageDrawable(resources.getDrawable(R.drawable.ic_play)) setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_play, theme)
)
} }
viewModel.player.isLoading -> Unit viewModel.player.isLoading -> Unit
else -> { else -> {
viewModel.player.play() viewModel.player.play()
setImageDrawable(resources.getDrawable(R.drawable.ic_pause)) setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_play, theme)
)
} }
} }
} }
@ -91,7 +94,7 @@ internal class TvPlayerActivity : BasePlayerActivity() {
private fun bindAudioControl() { private fun bindAudioControl() {
val audioBtn = binding.playerView.findViewById<ImageButton>(R.id.btn_audio_track) val audioBtn = binding.playerView.findViewById<ImageButton>(R.id.btn_audio_track)
audioBtn.setOnFocusChangeListener { v, hasFocus -> audioBtn.setOnFocusChangeListener { _, hasFocus ->
displayedPopup = if (hasFocus) { displayedPopup = if (hasFocus) {
val items = viewModel.currentSubtitleTracks.toUiTrack() val items = viewModel.currentSubtitleTracks.toUiTrack()
audioBtn.showPopupWindowAbove(items, AUDIO) audioBtn.showPopupWindowAbove(items, AUDIO)

View file

@ -1,65 +0,0 @@
package dev.jdtech.jellyfin.tv.ui
import android.content.res.Resources
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.R
import dev.jdtech.jellyfin.models.ContentType.MOVIE
import org.jellyfin.sdk.model.api.BaseItemDto
import javax.inject.Inject
@HiltViewModel
internal class MediaDetailViewModel @Inject internal constructor() : ViewModel() {
fun transformData(
data: BaseItemDto,
resources: Resources,
transformed: (State) -> Unit
): State {
return State(
dto = data,
description = data.overview.orEmpty(),
year = data.productionYear.toString(),
officialRating = data.officialRating.orEmpty(),
communityRating = data.communityRating.toString(),
runtimeMinutes = String.format(
resources.getString(R.string.runtime_minutes),
data.runTimeTicks?.div(600_000_000)
),
genres = data.genres?.joinToString(" / ").orEmpty(),
trailerUrl = data.remoteTrailers?.firstOrNull()?.url,
isPlayed = data.userData?.played == true,
isFavorite = data.userData?.isFavorite == true,
media = if (data.type == MOVIE.type) {
State.Movie(
title = data.name.orEmpty()
)
} else {
State.TvShow(
episode = data.episodeTitle ?: data.name.orEmpty(),
show = data.seriesName.orEmpty()
)
}
).also(transformed)
}
data class State(
val dto: BaseItemDto,
val description: String,
val year: String,
val officialRating: String,
val communityRating: String,
val runtimeMinutes: String,
val genres: String,
val trailerUrl: String?,
val isPlayed: Boolean,
val isFavorite: Boolean,
val media: Media
) {
sealed class Media
data class Movie(val title: String): Media()
data class TvShow(val episode: String, val show: String): Media()
}
}

View file

@ -1,18 +0,0 @@
package dev.jdtech.jellyfin.utils
import android.content.Context
import android.media.AudioManager
import android.media.AudioManager.ADJUST_LOWER
import android.media.AudioManager.ADJUST_RAISE
import android.media.AudioManager.ADJUST_SAME
import android.media.AudioManager.FLAG_SHOW_UI
import android.media.AudioManager.STREAM_MUSIC
internal class AudioController internal constructor(context: Context) {
private val manager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
fun volumeUp() = manager.adjustStreamVolume(STREAM_MUSIC, ADJUST_RAISE, FLAG_SHOW_UI)
fun volumeDown() = manager.adjustStreamVolume(STREAM_MUSIC, ADJUST_LOWER, FLAG_SHOW_UI)
fun showVolumeSlider() = manager.adjustStreamVolume(STREAM_MUSIC, ADJUST_SAME, FLAG_SHOW_UI)
}

View file

@ -241,7 +241,7 @@ fun parseMetadataFile(metadataFile: List<String>): DownloadMetadata {
suspend fun syncPlaybackProgress(jellyfinRepository: JellyfinRepository) { suspend fun syncPlaybackProgress(jellyfinRepository: JellyfinRepository) {
val items = loadDownloadedEpisodes() val items = loadDownloadedEpisodes()
items.forEach() { items.forEach {
try { try {
val localPlaybackProgress = it.metadata?.playbackPosition val localPlaybackProgress = it.metadata?.playbackPosition
val localPlayedPercentage = it.metadata?.playedPercentage val localPlayedPercentage = it.metadata?.playedPercentage

View file

@ -80,22 +80,27 @@ constructor(
recommended recommended
.onCompletion { .onCompletion {
if (greatServers.isNotEmpty()) { when {
connectToServer(greatServers.first()) greatServers.isNotEmpty() -> {
} else if (goodServers.isNotEmpty()) { connectToServer(greatServers.first())
val issuesString = createIssuesString(goodServers.first()) }
Toast.makeText( goodServers.isNotEmpty() -> {
application, val issuesString = createIssuesString(goodServers.first())
issuesString, Toast.makeText(
Toast.LENGTH_LONG application,
).show() issuesString,
connectToServer(goodServers.first()) Toast.LENGTH_LONG
} else if (okServers.isNotEmpty()) { ).show()
val okServer = okServers.first() connectToServer(goodServers.first())
val issuesString = createIssuesString(okServer) }
throw Exception(issuesString) okServers.isNotEmpty() -> {
} else { val okServer = okServers.first()
throw Exception(resources.getString(R.string.add_server_error_not_found)) val issuesString = createIssuesString(okServer)
throw Exception(issuesString)
}
else -> {
throw Exception(resources.getString(R.string.add_server_error_not_found))
}
} }
} }
.collect { recommendedServerInfo -> .collect { recommendedServerInfo ->

View file

@ -49,15 +49,15 @@ constructor(
} }
var item: BaseItemDto? = null var item: BaseItemDto? = null
var runTime: String = "" private var runTime: String = ""
var dateString: String = "" private var dateString: String = ""
var played: Boolean = false var played: Boolean = false
var favorite: Boolean = false var favorite: Boolean = false
var downloaded: Boolean = false private var downloaded: Boolean = false
var downloadEpisode: Boolean = false private var downloadEpisode: Boolean = false
var playerItems: MutableList<PlayerItem> = mutableListOf() var playerItems: MutableList<PlayerItem> = mutableListOf()
lateinit var downloadRequestItem: DownloadRequestItem private lateinit var downloadRequestItem: DownloadRequestItem
fun loadEpisode(episodeId: UUID) { fun loadEpisode(episodeId: UUID) {
viewModelScope.launch { viewModelScope.launch {
@ -161,9 +161,4 @@ constructor(
item.premiereDate.toString() item.premiereDate.toString()
} }
} }
fun doneDownloadEpisode() {
downloadEpisode = false
downloaded = true
}
} }