mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Fix toasting & some UI changes
This commit is contained in:
parent
cbc680da7d
commit
29e13cba0f
14 changed files with 137 additions and 77 deletions
|
|
@ -33,6 +33,7 @@ import com.github.damontecres.dolphin.ui.nav.ApplicationContent
|
|||
import com.github.damontecres.dolphin.ui.nav.Destination
|
||||
import com.github.damontecres.dolphin.ui.nav.NavigationManager
|
||||
import com.github.damontecres.dolphin.ui.theme.DolphinTheme
|
||||
import com.github.damontecres.dolphin.util.UpdateChecker
|
||||
import com.github.damontecres.dolphin.util.profile.createDeviceProfile
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import okhttp3.OkHttpClient
|
||||
|
|
@ -54,6 +55,9 @@ class MainActivity : AppCompatActivity() {
|
|||
@Inject
|
||||
lateinit var navigationManager: NavigationManager
|
||||
|
||||
@Inject
|
||||
lateinit var updateChecker: UpdateChecker
|
||||
|
||||
@OptIn(ExperimentalTvMaterial3Api::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -122,6 +126,15 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
val backStack = rememberNavBackStack(initialDestination)
|
||||
navigationManager.backStack = backStack
|
||||
if (appPreferences.autoCheckForUpdates) {
|
||||
LaunchedEffect(Unit) {
|
||||
try {
|
||||
updateChecker.maybeShowUpdateToast(appPreferences.updateUrl)
|
||||
} catch (ex: Exception) {
|
||||
Timber.w(ex, "Failed to check for update")
|
||||
}
|
||||
}
|
||||
}
|
||||
ApplicationContent(
|
||||
user = user,
|
||||
server = server,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import android.content.ContextWrapper
|
|||
import android.media.AudioManager
|
||||
import android.view.KeyEvent
|
||||
import android.view.WindowManager
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.MarqueeAnimationMode
|
||||
import androidx.compose.foundation.basicMarquee
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
|
@ -29,6 +30,8 @@ import coil3.request.ErrorResult
|
|||
import com.github.damontecres.dolphin.ui.data.RowColumn
|
||||
import com.github.damontecres.dolphin.ui.data.RowColumnSaver
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||
import org.jellyfin.sdk.model.extensions.ticks
|
||||
import timber.log.Timber
|
||||
|
|
@ -309,3 +312,21 @@ fun rememberPosition(initialPosition: RowColumn = RowColumn(-1, -1)) =
|
|||
initialPosition,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a [Toast]. Ensures it runs on the main thread.
|
||||
*/
|
||||
suspend fun showToast(
|
||||
context: Context,
|
||||
text: CharSequence,
|
||||
duration: Int,
|
||||
) = withContext(Dispatchers.Main) {
|
||||
Toast.makeText(context, text, duration).show()
|
||||
}
|
||||
|
||||
suspend fun showToast(
|
||||
context: Context,
|
||||
text: CharSequence,
|
||||
) = withContext(Dispatchers.Main) {
|
||||
Toast.makeText(context, text, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
package com.github.damontecres.dolphin.ui.components
|
||||
|
||||
import androidx.compose.foundation.focusable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import com.github.damontecres.dolphin.ui.tryRequestFocus
|
||||
|
||||
@Composable
|
||||
fun CircularProgress(modifier: Modifier = Modifier) {
|
||||
|
|
@ -21,13 +27,19 @@ fun CircularProgress(modifier: Modifier = Modifier) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Fill the space with a loading indicator
|
||||
* Fill the space with a loading indicator and take focus
|
||||
*/
|
||||
@Composable
|
||||
fun LoadingPage(modifier: Modifier = Modifier) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = modifier.fillMaxSize(),
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
.focusRequester(focusRequester)
|
||||
.focusable(),
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
color = MaterialTheme.colorScheme.border,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import com.github.damontecres.dolphin.preferences.UserPreferences
|
|||
import com.github.damontecres.dolphin.ui.letNotEmpty
|
||||
import com.github.damontecres.dolphin.ui.nav.Destination
|
||||
import com.github.damontecres.dolphin.ui.nav.NavigationManager
|
||||
import com.github.damontecres.dolphin.ui.showToast
|
||||
import com.github.damontecres.dolphin.util.ApiRequestPager
|
||||
import com.github.damontecres.dolphin.util.ExceptionHandler
|
||||
import com.github.damontecres.dolphin.util.GetEpisodesRequestHandler
|
||||
|
|
@ -297,9 +298,11 @@ class SeriesViewModel
|
|||
if (nextUp != null) {
|
||||
navigateTo(Destination.Playback(nextUp.id, 0L))
|
||||
} else {
|
||||
Toast
|
||||
.makeText(context, "Could not find an episode to play", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
showToast(
|
||||
context,
|
||||
"Could not find an episode to play",
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@ import com.github.damontecres.dolphin.ui.playOnClickSound
|
|||
import com.github.damontecres.dolphin.ui.playSoundOnFocus
|
||||
import com.github.damontecres.dolphin.ui.roundMinutes
|
||||
import com.github.damontecres.dolphin.ui.timeRemaining
|
||||
import com.github.damontecres.dolphin.util.formatSubtitleLang
|
||||
import org.jellyfin.sdk.model.api.MediaStreamType
|
||||
import org.jellyfin.sdk.model.api.PersonKind
|
||||
import org.jellyfin.sdk.model.extensions.ticks
|
||||
|
||||
@Composable
|
||||
|
|
@ -153,6 +155,14 @@ fun MovieDetailsHeader(
|
|||
)
|
||||
}
|
||||
}
|
||||
movie.data.people
|
||||
?.filter { it.type == PersonKind.DIRECTOR && it.name.isNotNullOrBlank() }
|
||||
?.joinToString(", ") { it.name!! }
|
||||
?.let {
|
||||
Text(
|
||||
text = "Directed by $it",
|
||||
)
|
||||
}
|
||||
// Key-Values
|
||||
Row(
|
||||
modifier =
|
||||
|
|
@ -185,10 +195,7 @@ fun MovieDetailsHeader(
|
|||
)
|
||||
}
|
||||
}
|
||||
dto.mediaStreams
|
||||
?.filter { it.type == MediaStreamType.SUBTITLE && it.language.isNotNullOrBlank() }
|
||||
?.mapNotNull { it.language }
|
||||
?.joinToString(", ")
|
||||
formatSubtitleLang(dto.mediaStreams)
|
||||
?.let {
|
||||
if (it.isNotNullOrBlank()) {
|
||||
TitleValueText(
|
||||
|
|
@ -199,13 +206,6 @@ fun MovieDetailsHeader(
|
|||
}
|
||||
}
|
||||
// TODO add writers, studio, etc to overview dialog
|
||||
// dto.people?.firstOrNull { it.type == PersonKind.DIRECTOR }?.name?.let {
|
||||
// TitleValueText(
|
||||
// stringResource(R.string.director),
|
||||
// it,
|
||||
// modifier = Modifier.widthIn(max = 80.dp),
|
||||
// )
|
||||
// }
|
||||
// dto.studios?.letNotEmpty {
|
||||
// TitleValueText(
|
||||
// stringResource(R.string.studios),
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.github.damontecres.dolphin.data.model.BaseItem
|
|||
import com.github.damontecres.dolphin.ui.components.ExpandablePlayButtons
|
||||
import com.github.damontecres.dolphin.ui.components.TitleValueText
|
||||
import com.github.damontecres.dolphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.dolphin.util.formatSubtitleLang
|
||||
import org.jellyfin.sdk.model.api.MediaStreamType
|
||||
import org.jellyfin.sdk.model.extensions.ticks
|
||||
import kotlin.time.Duration
|
||||
|
|
@ -75,10 +76,7 @@ fun FocusedEpisodeFooter(
|
|||
}
|
||||
}
|
||||
|
||||
dto.mediaStreams
|
||||
?.filter { it.type == MediaStreamType.SUBTITLE && it.language.isNotNullOrBlank() }
|
||||
?.mapNotNull { it.language }
|
||||
?.joinToString(", ")
|
||||
formatSubtitleLang(dto.mediaStreams)
|
||||
?.let {
|
||||
if (it.isNotNullOrBlank()) {
|
||||
TitleValueText(
|
||||
|
|
|
|||
|
|
@ -193,11 +193,7 @@ class HomeViewModel
|
|||
.filter { it.collectionType in supportedCollectionTypes }
|
||||
.map { view ->
|
||||
val title =
|
||||
view
|
||||
?.name
|
||||
?.let {
|
||||
"Recently Added in $it"
|
||||
}
|
||||
view.name?.let { "Recently Added in $it" }
|
||||
val request =
|
||||
GetLatestMediaRequest(
|
||||
fields = DefaultItemFields,
|
||||
|
|
@ -205,6 +201,7 @@ class HomeViewModel
|
|||
parentId = view.id,
|
||||
groupItems = true,
|
||||
limit = limit,
|
||||
isPlayed = null, // Server will handle user's preference
|
||||
)
|
||||
val latest =
|
||||
api.userLibraryApi
|
||||
|
|
|
|||
|
|
@ -66,11 +66,14 @@ import com.github.damontecres.dolphin.ui.tryRequestFocus
|
|||
import com.github.damontecres.dolphin.util.ExceptionHandler
|
||||
import com.github.damontecres.dolphin.util.supportedCollectionTypes
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.userViewsApi
|
||||
import org.jellyfin.sdk.model.api.CollectionType
|
||||
import org.jellyfin.sdk.model.api.DeviceProfile
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
|
|
@ -85,15 +88,19 @@ class NavDrawerViewModel
|
|||
val selectedIndex = MutableLiveData<Int>(-1)
|
||||
|
||||
init {
|
||||
viewModelScope.launch(ExceptionHandler()) {
|
||||
viewModelScope.launch(Dispatchers.IO + ExceptionHandler(true)) {
|
||||
val userViews =
|
||||
api.userViewsApi
|
||||
.getUserViews()
|
||||
.content.items
|
||||
libraries.value =
|
||||
val libraries =
|
||||
userViews
|
||||
.filter { it.collectionType in supportedCollectionTypes }
|
||||
.map { BaseItem.from(it, api) }
|
||||
Timber.d("Got ${userViews.size} user views filtered to ${libraries.size}")
|
||||
withContext(Dispatchers.Main) {
|
||||
this@NavDrawerViewModel.libraries.value = libraries
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -264,6 +264,7 @@ fun PlaybackOverlay(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(
|
||||
showDebugInfo && controllerViewState.controlsVisible,
|
||||
modifier =
|
||||
|
|
@ -283,7 +284,6 @@ fun PlaybackOverlay(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The view state of the overlay
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package com.github.damontecres.dolphin.util
|
|||
|
||||
import android.widget.Toast
|
||||
import com.github.damontecres.dolphin.DolphinApplication
|
||||
import com.github.damontecres.dolphin.ui.showToast
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import timber.log.Timber
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
|
|
@ -29,12 +31,13 @@ class ExceptionHandler(
|
|||
Timber.e(exception, "Exception in coroutine")
|
||||
|
||||
if (autoToast) {
|
||||
Toast
|
||||
.makeText(
|
||||
runBlocking {
|
||||
showToast(
|
||||
DolphinApplication.instance,
|
||||
"Error: ${exception.message}",
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
package com.github.damontecres.dolphin.util
|
||||
|
||||
import android.os.Build
|
||||
import com.github.damontecres.dolphin.ui.isNotNullOrBlank
|
||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||
import org.jellyfin.sdk.model.api.MediaStream
|
||||
import org.jellyfin.sdk.model.api.MediaStreamType
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
|
|
@ -48,3 +51,10 @@ val BaseItemDto.seasonEpisodePadded: String?
|
|||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
fun formatSubtitleLang(mediaStreams: List<MediaStream>?): String? =
|
||||
mediaStreams
|
||||
?.filter { it.type == MediaStreamType.SUBTITLE && it.language.isNotNullOrBlank() }
|
||||
?.mapNotNull { it.language }
|
||||
?.distinct()
|
||||
?.joinToString(", ") { languageName(it) }
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.github.damontecres.dolphin.util
|
|||
import android.widget.Toast
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.github.damontecres.dolphin.DolphinApplication
|
||||
import com.github.damontecres.dolphin.ui.showToast
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -39,15 +40,13 @@ class LoadingExceptionHandler(
|
|||
exception = exception,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (autoToast) {
|
||||
Toast
|
||||
.makeText(
|
||||
showToast(
|
||||
DolphinApplication.instance,
|
||||
"Error: ${exception.message}",
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ data class TrackSupport(
|
|||
if (split.size > 1) split[1] else codecs
|
||||
}
|
||||
}
|
||||
val language = languageName(context, format.language)
|
||||
val language = languageName(format.language)
|
||||
"$language ($type)"
|
||||
}
|
||||
}
|
||||
|
|
@ -135,10 +135,8 @@ fun checkForSupport(tracks: Tracks): List<TrackSupport> =
|
|||
}
|
||||
}
|
||||
|
||||
fun languageName(
|
||||
context: Context,
|
||||
code: String?,
|
||||
) = if (code != null && code != "00") {
|
||||
fun languageName(code: String?): String =
|
||||
if (code != null) {
|
||||
try {
|
||||
Locale(code).displayLanguage
|
||||
} catch (ex: Exception) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.github.damontecres.dolphin.R
|
|||
import com.github.damontecres.dolphin.hilt.StandardOkHttpClient
|
||||
import com.github.damontecres.dolphin.ui.findActivity
|
||||
import com.github.damontecres.dolphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.dolphin.ui.showToast
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -87,22 +88,20 @@ class UpdateChecker
|
|||
"Skipping update notification, threshold is $lastUpdateCheckThreshold",
|
||||
)
|
||||
} else {
|
||||
Toast
|
||||
.makeText(
|
||||
showToast(
|
||||
context,
|
||||
"Update available: $installedVersion => ${latestRelease.version}!",
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Timber.v("No update available for $installedVersion")
|
||||
if (showNegativeToast) {
|
||||
Toast
|
||||
.makeText(
|
||||
showToast(
|
||||
context,
|
||||
"No updates available, $installedVersion is the latest!",
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue