From 19b712d54b84ef98c90b3892d3a3a7df62b161a0 Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:12:42 -0500 Subject: [PATCH 1/5] More favorite tabs & add people to favorites (#210) Adds Video, Playlists, & People tabs to the favorites page Also adds ability to add people to favorites via context menu or on their page Closes #199 --- .../wholphin/data/model/GetItemsFilter.kt | 12 ++ .../damontecres/wholphin/data/model/Person.kt | 16 ++ .../wholphin/ui/cards/PersonCard.kt | 4 +- .../ui/components/CollectionFolderGrid.kt | 175 +++++++++++------- .../wholphin/ui/data/SortAndDirection.kt | 1 + .../wholphin/ui/detail/DetailUtils.kt | 20 +- .../wholphin/ui/detail/FavoritesPage.kt | 83 +++++++++ .../wholphin/ui/detail/PersonPage.kt | 53 +++++- .../ui/detail/movie/MovieViewModel.kt | 19 +- .../ui/detail/series/SeriesViewModel.kt | 18 +- .../wholphin/util/ApiRequestPager.kt | 21 +++ .../wholphin/util/PeopleFavorites.kt | 34 ++++ app/src/main/res/values/strings.xml | 2 + 13 files changed, 356 insertions(+), 102 deletions(-) create mode 100644 app/src/main/java/com/github/damontecres/wholphin/util/PeopleFavorites.kt diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/model/GetItemsFilter.kt b/app/src/main/java/com/github/damontecres/wholphin/data/model/GetItemsFilter.kt index 09acd57e..6330113e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/data/model/GetItemsFilter.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/data/model/GetItemsFilter.kt @@ -6,6 +6,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.request.GetItemsRequest +import org.jellyfin.sdk.model.api.request.GetPersonsRequest import org.jellyfin.sdk.model.serializer.UUIDSerializer import java.util.UUID @@ -19,6 +20,7 @@ data class GetItemsFilter( val studios: List? = null, val tags: List? = null, val includeItemTypes: List? = null, + val override: GetItemsFilterOverride = GetItemsFilterOverride.NONE, ) { fun applyTo(req: GetItemsRequest) = req.copy( @@ -31,4 +33,14 @@ data class GetItemsFilter( studioIds = studios, tags = tags, ) + + fun applyTo(req: GetPersonsRequest) = + req.copy( + isFavorite = favorite, + ) +} + +enum class GetItemsFilterOverride { + NONE, + PERSON, } diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/model/Person.kt b/app/src/main/java/com/github/damontecres/wholphin/data/model/Person.kt index 398d333d..3430640e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/data/model/Person.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/data/model/Person.kt @@ -13,6 +13,7 @@ data class Person( val role: String?, val type: PersonKind, val imageUrl: String?, + val favorite: Boolean, ) { companion object { fun fromDto( @@ -25,6 +26,21 @@ data class Person( role = dto.role, type = dto.type, imageUrl = api.imageApi.getItemImageUrl(dto.id, ImageType.PRIMARY), + favorite = false, + ) + + fun fromDto( + dto: BaseItemPerson, + favorite: Boolean, + api: ApiClient, + ): Person = + Person( + id = dto.id, + name = dto.name, + role = dto.role, + type = dto.type, + imageUrl = api.imageApi.getItemImageUrl(dto.id, ImageType.PRIMARY), + favorite = favorite, ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonCard.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonCard.kt index 07545a53..dcde2ff0 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonCard.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonCard.kt @@ -72,8 +72,8 @@ fun PersonCard( ItemCardImage( imageUrl = item.imageUrl, name = item.name, - showOverlay = false, - favorite = false, + showOverlay = true, + favorite = item.favorite, watched = false, unwatchedCount = -1, watchedPercent = null, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt index 2426c44a..53a4988a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt @@ -34,6 +34,7 @@ import com.github.damontecres.wholphin.data.LibraryDisplayInfoDao import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.GetItemsFilter +import com.github.damontecres.wholphin.data.model.GetItemsFilterOverride import com.github.damontecres.wholphin.data.model.LibraryDisplayInfo import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect @@ -53,6 +54,7 @@ import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.FavoriteWatchManager import com.github.damontecres.wholphin.util.GetItemsRequestHandler +import com.github.damontecres.wholphin.util.GetPersonsHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState import dagger.hilt.android.lifecycle.HiltViewModel @@ -69,6 +71,7 @@ import org.jellyfin.sdk.model.api.ItemSortBy import org.jellyfin.sdk.model.api.MediaType import org.jellyfin.sdk.model.api.SortOrder import org.jellyfin.sdk.model.api.request.GetItemsRequest +import org.jellyfin.sdk.model.api.request.GetPersonsRequest import org.jellyfin.sdk.model.serializer.toUUIDOrNull import java.util.UUID import javax.inject.Inject @@ -107,13 +110,12 @@ class CollectionFolderViewModel } val sortAndDirection = - if (initialSortAndDirection == null) { - serverRepository.currentUser.value?.let { user -> - libraryDisplayInfoDao.getItem(user, itemId)?.sortAndDirection - } ?: SortAndDirection.DEFAULT - } else { - SortAndDirection.DEFAULT - } + initialSortAndDirection + ?: ( + serverRepository.currentUser.value?.let { user -> + libraryDisplayInfoDao.getItem(user, itemId)?.sortAndDirection + } ?: SortAndDirection.DEFAULT + ) loadResults(sortAndDirection, recursive, filter) } @@ -143,7 +145,6 @@ class CollectionFolderViewModel recursive: Boolean, filter: GetItemsFilter, ) { - val item = item.value viewModelScope.launch(Dispatchers.IO) { withContext(Dispatchers.Main) { pager.value = listOf() @@ -151,62 +152,7 @@ class CollectionFolderViewModel this@CollectionFolderViewModel.sortAndDirection.value = sortAndDirection this@CollectionFolderViewModel.filter.value = filter } - val includeItemTypes = - when (item?.data?.collectionType) { - CollectionType.MOVIES -> listOf(BaseItemKind.MOVIE) - CollectionType.TVSHOWS -> listOf(BaseItemKind.SERIES) - CollectionType.HOMEVIDEOS -> listOf(BaseItemKind.VIDEO) - CollectionType.MUSIC -> - listOf( - BaseItemKind.AUDIO, - BaseItemKind.MUSIC_ARTIST, - BaseItemKind.MUSIC_ALBUM, - ) - - CollectionType.BOXSETS -> listOf(BaseItemKind.BOX_SET) - CollectionType.PLAYLISTS -> listOf(BaseItemKind.PLAYLIST) - - else -> listOf() - } - val request = - filter.applyTo( - GetItemsRequest( - parentId = item?.id, - enableImageTypes = listOf(ImageType.PRIMARY, ImageType.THUMB), - includeItemTypes = includeItemTypes, - recursive = recursive, - excludeItemIds = item?.let { listOf(item.id) }, - sortBy = - buildList { - add(sortAndDirection.sort) - if (sortAndDirection.sort != ItemSortBy.SORT_NAME) { - add(ItemSortBy.SORT_NAME) - } - if (item?.data?.collectionType == CollectionType.MOVIES) { - add(ItemSortBy.PRODUCTION_YEAR) - } - }, - sortOrder = - buildList { - add(sortAndDirection.direction) - if (sortAndDirection.sort != ItemSortBy.SORT_NAME) { - add(SortOrder.ASCENDING) - } - if (item?.data?.collectionType == CollectionType.MOVIES) { - add(SortOrder.ASCENDING) - } - }, - fields = SlimItemFields, - ), - ) - val newPager = - ApiRequestPager( - api, - request, - GetItemsRequestHandler, - viewModelScope, - useSeriesForPrimary = true, - ) + val newPager = createPager(sortAndDirection, recursive, filter) newPager.init() if (newPager.isNotEmpty()) newPager.getBlocking(0) withContext(Dispatchers.Main) { @@ -216,6 +162,93 @@ class CollectionFolderViewModel } } + private fun createPager( + sortAndDirection: SortAndDirection, + recursive: Boolean, + filter: GetItemsFilter, + ): ApiRequestPager { + val item = item.value + return when (filter.override) { + GetItemsFilterOverride.NONE -> { + val includeItemTypes = + when (item?.data?.collectionType) { + CollectionType.MOVIES -> listOf(BaseItemKind.MOVIE) + CollectionType.TVSHOWS -> listOf(BaseItemKind.SERIES) + CollectionType.HOMEVIDEOS -> listOf(BaseItemKind.VIDEO) + CollectionType.MUSIC -> + listOf( + BaseItemKind.AUDIO, + BaseItemKind.MUSIC_ARTIST, + BaseItemKind.MUSIC_ALBUM, + ) + + CollectionType.BOXSETS -> listOf(BaseItemKind.BOX_SET) + CollectionType.PLAYLISTS -> listOf(BaseItemKind.PLAYLIST) + + else -> listOf() + } + val request = + filter.applyTo( + GetItemsRequest( + parentId = item?.id, + enableImageTypes = listOf(ImageType.PRIMARY, ImageType.THUMB), + includeItemTypes = includeItemTypes, + recursive = recursive, + excludeItemIds = item?.let { listOf(item.id) }, + sortBy = + buildList { + add(sortAndDirection.sort) + if (sortAndDirection.sort != ItemSortBy.SORT_NAME) { + add(ItemSortBy.SORT_NAME) + } + if (item?.data?.collectionType == CollectionType.MOVIES) { + add(ItemSortBy.PRODUCTION_YEAR) + } + }, + sortOrder = + buildList { + add(sortAndDirection.direction) + if (sortAndDirection.sort != ItemSortBy.SORT_NAME) { + add(SortOrder.ASCENDING) + } + if (item?.data?.collectionType == CollectionType.MOVIES) { + add(SortOrder.ASCENDING) + } + }, + fields = SlimItemFields, + ), + ) + val newPager = + ApiRequestPager( + api, + request, + GetItemsRequestHandler, + viewModelScope, + useSeriesForPrimary = true, + ) + newPager + } + + GetItemsFilterOverride.PERSON -> { + val request = + filter.applyTo( + GetPersonsRequest( + enableImageTypes = listOf(ImageType.PRIMARY, ImageType.THUMB), + ), + ) + val newPager = + ApiRequestPager( + api, + request, + GetPersonsHandler, + viewModelScope, + useSeriesForPrimary = true, + ) + newPager + } + } + } + suspend fun positionOfLetter(letter: Char): Int? = withContext(Dispatchers.IO) { item.value?.let { item -> @@ -437,12 +470,14 @@ fun CollectionFolderGridContent( modifier = Modifier.fillMaxWidth(), ) } - SortByButton( - sortOptions = sortOptions, - current = sortAndDirection, - onSortChange = onSortChange, - modifier = Modifier, - ) + if (sortOptions.isNotEmpty()) { + SortByButton( + sortOptions = sortOptions, + current = sortAndDirection, + onSortChange = onSortChange, + modifier = Modifier, + ) + } } CardGrid( pager = pager, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/data/SortAndDirection.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/data/SortAndDirection.kt index 3cb6635b..634be82f 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/data/SortAndDirection.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/data/SortAndDirection.kt @@ -79,5 +79,6 @@ fun getStringRes(sort: ItemSortBy): Int = ItemSortBy.CRITIC_RATING -> R.string.critic_rating ItemSortBy.PLAY_COUNT -> R.string.play_count ItemSortBy.AIRED_EPISODE_ORDER -> R.string.aired_episode_order + ItemSortBy.DEFAULT -> R.string.default_track else -> throw IllegalArgumentException("Unsupported sort option: $sort") } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/DetailUtils.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/DetailUtils.kt index 5fabe479..d62d0f73 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/DetailUtils.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/DetailUtils.kt @@ -288,7 +288,6 @@ fun buildMoreDialogItemsForHome( fun buildMoreDialogItemsForPerson( context: Context, person: Person, -// favorite: Boolean, actions: MoreDialogActions, ): List = buildList { @@ -301,14 +300,13 @@ fun buildMoreDialogItemsForPerson( actions.navigateTo(Destination.MediaItem(itemId, BaseItemKind.PERSON)) }, ) - // TODO need way to get person's favorite status -// add( -// DialogItem( -// text = if (favorite) R.string.remove_favorite else R.string.add_favorite, -// iconStringRes = R.string.fa_heart, -// iconColor = if (favorite) Color.Red else Color.Unspecified, -// ) { -// actions.onClickFavorite.invoke(itemId, !favorite) -// }, -// ) + add( + DialogItem( + text = if (person.favorite) R.string.remove_favorite else R.string.add_favorite, + iconStringRes = R.string.fa_heart, + iconColor = if (person.favorite) Color.Red else Color.Unspecified, + ) { + actions.onClickFavorite.invoke(itemId, !person.favorite) + }, + ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt index 0afa076d..dac2b915 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt @@ -25,6 +25,7 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.GetItemsFilter +import com.github.damontecres.wholphin.data.model.GetItemsFilterOverride import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.components.CollectionFolderGrid import com.github.damontecres.wholphin.ui.components.ErrorMessage @@ -32,11 +33,15 @@ import com.github.damontecres.wholphin.ui.components.TabRow import com.github.damontecres.wholphin.ui.data.EpisodeSortOptions import com.github.damontecres.wholphin.ui.data.MovieSortOptions import com.github.damontecres.wholphin.ui.data.SeriesSortOptions +import com.github.damontecres.wholphin.ui.data.SortAndDirection +import com.github.damontecres.wholphin.ui.data.VideoSortOptions import com.github.damontecres.wholphin.ui.logTab import com.github.damontecres.wholphin.ui.nav.NavDrawerItem import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel import com.github.damontecres.wholphin.ui.tryRequestFocus import org.jellyfin.sdk.model.api.BaseItemKind +import org.jellyfin.sdk.model.api.ItemSortBy +import org.jellyfin.sdk.model.api.SortOrder @Composable fun FavoritesPage( @@ -59,6 +64,9 @@ fun FavoritesPage( stringResource(R.string.movies), stringResource(R.string.tv_shows), stringResource(R.string.episodes), + stringResource(R.string.videos), + stringResource(R.string.playlists), + stringResource(R.string.people), ) var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } val focusRequester = remember { FocusRequester() } @@ -165,6 +173,81 @@ fun FavoritesPage( }, ) } + 3 -> { + CollectionFolderGrid( + preferences = preferences, + onClickItem = { _, item -> onClickItem.invoke(item) }, + itemId = "${NavDrawerItem.Favorites.id}_videos", + initialFilter = + GetItemsFilter( + favorite = true, + includeItemTypes = listOf(BaseItemKind.VIDEO), + ), + showTitle = false, + recursive = true, + sortOptions = VideoSortOptions, + modifier = + Modifier + .padding(start = 16.dp) + .fillMaxSize() + .focusRequester(focusRequester), + positionCallback = { columns, position -> + showHeader = position < columns + }, + ) + } + 4 -> { + CollectionFolderGrid( + preferences = preferences, + onClickItem = { _, item -> onClickItem.invoke(item) }, + itemId = "${NavDrawerItem.Favorites.id}_playlists", + initialFilter = + GetItemsFilter( + favorite = true, + includeItemTypes = listOf(BaseItemKind.PLAYLIST), + ), + showTitle = false, + recursive = true, + sortOptions = VideoSortOptions, + modifier = + Modifier + .padding(start = 16.dp) + .fillMaxSize() + .focusRequester(focusRequester), + positionCallback = { columns, position -> + showHeader = position < columns + }, + ) + } + + 5 -> { + CollectionFolderGrid( + preferences = preferences, + onClickItem = { _, item -> onClickItem.invoke(item) }, + itemId = "${NavDrawerItem.Favorites.id}_people", + initialFilter = + GetItemsFilter( + favorite = true, + override = GetItemsFilterOverride.PERSON, + ), + initialSortAndDirection = + SortAndDirection( + ItemSortBy.DEFAULT, + SortOrder.ASCENDING, + ), + showTitle = false, + recursive = true, + sortOptions = listOf(), + modifier = + Modifier + .padding(start = 16.dp) + .fillMaxSize() + .focusRequester(focusRequester), + positionCallback = { columns, position -> + showHeader = position < columns + }, + ) + } else -> ErrorMessage("Invalid tab index $selectedTabIndex", null) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt index db2ff356..d89e71d8 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt @@ -26,6 +26,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -43,11 +45,13 @@ import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect import com.github.damontecres.wholphin.ui.PreviewTvSpec import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.components.ErrorMessage +import com.github.damontecres.wholphin.ui.components.ExpandableFaButton import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.components.LoadingRow import com.github.damontecres.wholphin.ui.components.OverviewText import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo +import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.NavigationManager @@ -56,6 +60,7 @@ import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ApiRequestPager +import com.github.damontecres.wholphin.util.FavoriteWatchManager import com.github.damontecres.wholphin.util.GetItemsRequestHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState @@ -78,6 +83,7 @@ class PersonViewModel constructor( api: ApiClient, val navigationManager: NavigationManager, + private val favoriteWatchManager: FavoriteWatchManager, ) : LoadingItemViewModel(api) { val movies = MutableLiveData(RowLoadingState.Pending) val series = MutableLiveData(RowLoadingState.Pending) @@ -144,6 +150,13 @@ class PersonViewModel } } } + + fun setFavorite(favorite: Boolean) { + viewModelScope.launchIO { + favoriteWatchManager.setFavorite(itemUuid, favorite) + fetchAndSetItem(itemUuid) + } + } } @Composable @@ -180,6 +193,7 @@ fun PersonPage( birthdate = person.data.premiereDate?.toLocalDate(), deathdate = person.data.endDate?.toLocalDate(), birthPlace = person.data.productionLocations?.firstOrNull(), + favorite = person.favorite, movies = movies, series = series, episodes = episodes, @@ -187,6 +201,9 @@ fun PersonPage( viewModel.navigationManager.navigateTo(item.destination()) }, overviewOnClick = { showOverviewDialog = true }, + favoriteOnClick = { + viewModel.setFavorite(!person.favorite) + }, modifier = modifier, ) AnimatedVisibility(showOverviewDialog) { @@ -220,11 +237,13 @@ fun PersonPageContent( birthdate: LocalDate?, deathdate: LocalDate?, birthPlace: String?, + favorite: Boolean, movies: RowLoadingState, series: RowLoadingState, episodes: RowLoadingState, onClickItem: (Int, BaseItem) -> Unit, overviewOnClick: () -> Unit, + favoriteOnClick: () -> Unit, modifier: Modifier = Modifier, ) { val focusRequester = remember { FocusRequester() } @@ -237,8 +256,10 @@ fun PersonPageContent( headerFocusRequester.tryRequestFocus() } } + var focusedOnHeader by remember { mutableStateOf(true) } LazyColumn( verticalArrangement = Arrangement.spacedBy(16.dp), + userScrollEnabled = !focusedOnHeader, modifier = modifier .padding(start = 16.dp), @@ -251,13 +272,18 @@ fun PersonPageContent( birthdate = birthdate, birthPlace = birthPlace, deathdate = deathdate, + favorite = favorite, overviewOnClick = overviewOnClick, + favoriteOnClick = favoriteOnClick, modifier = Modifier .fillMaxWidth() .heightIn(max = 480.dp) .padding(top = 16.dp, bottom = 40.dp) - .focusRequester(headerFocusRequester), + .focusRequester(headerFocusRequester) + .onFocusChanged { + focusedOnHeader = it.hasFocus + }, ) } item { @@ -321,7 +347,9 @@ fun PersonHeader( birthdate: LocalDate?, deathdate: LocalDate?, birthPlace: String?, + favorite: Boolean, overviewOnClick: () -> Unit, + favoriteOnClick: () -> Unit, modifier: Modifier = Modifier, ) { val bringIntoViewRequester = remember { BringIntoViewRequester() } @@ -380,9 +408,9 @@ fun PersonHeader( overflow = TextOverflow.Ellipsis, ) } - birthPlace?.let { + if (birthPlace.isNotNullOrBlank()) { Text( - text = stringResource(R.string.birthplace) + ": $it", + text = stringResource(R.string.birthplace) + ": $birthPlace", color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.titleSmall, maxLines = 1, @@ -411,9 +439,12 @@ fun PersonHeader( ) } val interactionSource = remember { MutableInteractionSource() } - val focused by interactionSource.collectIsFocusedAsState() - if (focused) { - LaunchedEffect(Unit) { + val buttonInteractionSource = remember { MutableInteractionSource() } + val focused = + interactionSource.collectIsFocusedAsState().value || + buttonInteractionSource.collectIsFocusedAsState().value + LaunchedEffect(focused) { + if (focused) { bringIntoViewRequester.bringIntoView() } } @@ -424,6 +455,14 @@ fun PersonHeader( interactionSource = interactionSource, modifier = Modifier.padding(top = 8.dp), ) + ExpandableFaButton( + title = if (favorite) R.string.remove_favorite else R.string.add_favorite, + iconStringRes = R.string.fa_heart, + onClick = favoriteOnClick, + iconColor = if (favorite) Color.Red else Color.Unspecified, + interactionSource = buttonInteractionSource, + modifier = Modifier, + ) } } } @@ -440,6 +479,8 @@ private fun PersonPreview() { birthPlace = "Phoenix, Arizona, USA", deathdate = LocalDate.of(2025, 2, 1), overviewOnClick = {}, + favorite = true, + favoriteOnClick = {}, modifier = Modifier.fillMaxWidth(), ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt index 51c7dafa..0c29ae3c 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt @@ -18,7 +18,6 @@ import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.preferences.ThemeSongVolume import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.launchIO -import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.setValueOnMain @@ -26,6 +25,7 @@ import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.FavoriteWatchManager import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState +import com.github.damontecres.wholphin.util.PeopleFavorites import com.github.damontecres.wholphin.util.ThemeSongPlayer import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -56,6 +56,7 @@ class MovieViewModel val itemPlaybackRepository: ItemPlaybackRepository, private val themeSongPlayer: ThemeSongPlayer, private val favoriteWatchManager: FavoriteWatchManager, + private val peopleFavorites: PeopleFavorites, @Assisted val itemId: UUID, ) : ViewModel() { @AssistedFactory @@ -134,12 +135,11 @@ class MovieViewModel this@MovieViewModel.trailers.value = localTrailers + remoteTrailers } } + viewModelScope.launchIO { + val people = peopleFavorites.getPeopleFor(item) + this@MovieViewModel.people.setValueOnMain(people) + } withContext(Dispatchers.Main) { - people.value = - item.data.people - ?.letNotEmpty { people -> - people.map { Person.fromDto(it, api) } - }.orEmpty() chapters.value = Chapter.fromDto(item.data, api) } if (!similar.isInitialized) { @@ -171,7 +171,14 @@ class MovieViewModel favorite: Boolean, ) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) { favoriteWatchManager.setFavorite(itemId, favorite) + val item = item.value fetchAndSetItem() + if (item != null && itemId != item.id) { + viewModelScope.launchIO { + val people = peopleFavorites.getPeopleFor(item) + this@MovieViewModel.people.setValueOnMain(people) + } + } } fun savePlayVersion( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt index 33a5e476..43ca6f55 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt @@ -16,7 +16,6 @@ import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.detail.ItemViewModel import com.github.damontecres.wholphin.ui.equalsNotNull import com.github.damontecres.wholphin.ui.launchIO -import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.setValueOnMain @@ -28,6 +27,7 @@ import com.github.damontecres.wholphin.util.GetEpisodesRequestHandler import com.github.damontecres.wholphin.util.GetItemsRequestHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState +import com.github.damontecres.wholphin.util.PeopleFavorites import com.github.damontecres.wholphin.util.ThemeSongPlayer import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext @@ -61,6 +61,7 @@ class SeriesViewModel private val itemPlaybackRepository: ItemPlaybackRepository, private val themeSongPlayer: ThemeSongPlayer, private val favoriteWatchManager: FavoriteWatchManager, + private val peopleFavorites: PeopleFavorites, ) : ItemViewModel(api) { private lateinit var seriesId: UUID private lateinit var prefs: UserPreferences @@ -109,11 +110,10 @@ class SeriesViewModel this@SeriesViewModel.seasons.value = seasons episodes.value = episodeInfo loading.value = LoadingState.Success - people.value = - item.data.people - ?.letNotEmpty { people -> - people.map { Person.fromDto(it, api) } - }.orEmpty() + } + viewModelScope.launchIO { + val people = peopleFavorites.getPeopleFor(item) + this@SeriesViewModel.people.setValueOnMain(people) } if (!similar.isInitialized) { viewModelScope.launchIO { @@ -255,7 +255,11 @@ class SeriesViewModel if (listIndex != null) { refreshEpisode(itemId, listIndex) } else { - fetchItem(seriesId) + val item = fetchItem(seriesId) + viewModelScope.launchIO { + val people = peopleFavorites.getPeopleFor(item) + this@SeriesViewModel.people.setValueOnMain(people) + } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/ApiRequestPager.kt b/app/src/main/java/com/github/damontecres/wholphin/util/ApiRequestPager.kt index 2cc5bb3d..3156bb3d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/ApiRequestPager.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/ApiRequestPager.kt @@ -18,6 +18,7 @@ import org.jellyfin.sdk.api.client.Response import org.jellyfin.sdk.api.client.extensions.genresApi import org.jellyfin.sdk.api.client.extensions.itemsApi import org.jellyfin.sdk.api.client.extensions.liveTvApi +import org.jellyfin.sdk.api.client.extensions.personsApi import org.jellyfin.sdk.api.client.extensions.playlistsApi import org.jellyfin.sdk.api.client.extensions.suggestionsApi import org.jellyfin.sdk.api.client.extensions.tvShowsApi @@ -28,6 +29,7 @@ import org.jellyfin.sdk.model.api.request.GetEpisodesRequest import org.jellyfin.sdk.model.api.request.GetGenresRequest import org.jellyfin.sdk.model.api.request.GetItemsRequest import org.jellyfin.sdk.model.api.request.GetNextUpRequest +import org.jellyfin.sdk.model.api.request.GetPersonsRequest import org.jellyfin.sdk.model.api.request.GetPlaylistItemsRequest import org.jellyfin.sdk.model.api.request.GetResumeItemsRequest import org.jellyfin.sdk.model.api.request.GetSuggestionsRequest @@ -365,3 +367,22 @@ val GetProgramsDtoHandler = request: GetProgramsDto, ): Response = api.liveTvApi.getPrograms(request) } + +val GetPersonsHandler = + object : RequestHandler { + override fun prepare( + request: GetPersonsRequest, + startIndex: Int, + limit: Int, + enableTotalRecordCount: Boolean, + ): GetPersonsRequest = + request.copy( +// startIndex = startIndex, + limit = limit, + ) + + override suspend fun execute( + api: ApiClient, + request: GetPersonsRequest, + ): Response = api.personsApi.getPersons((request)) + } diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/PeopleFavorites.kt b/app/src/main/java/com/github/damontecres/wholphin/util/PeopleFavorites.kt new file mode 100644 index 00000000..0ca4f0ba --- /dev/null +++ b/app/src/main/java/com/github/damontecres/wholphin/util/PeopleFavorites.kt @@ -0,0 +1,34 @@ +package com.github.damontecres.wholphin.util + +import com.github.damontecres.wholphin.data.model.BaseItem +import com.github.damontecres.wholphin.data.model.Person +import com.github.damontecres.wholphin.ui.letNotEmpty +import org.jellyfin.sdk.api.client.ApiClient +import org.jellyfin.sdk.api.client.extensions.itemsApi +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class PeopleFavorites + @Inject + constructor( + private val api: ApiClient, + ) { + suspend fun getPeopleFor(item: BaseItem): List = + item.data.people.orEmpty().map { it.id }.chunked(50).let { chunks -> + val favorites = + chunks + .map { + api.itemsApi + .getItems(ids = it) + .content.items + }.flatten() + .associateBy({ it.id }, { it.userData?.isFavorite ?: false }) + val people = + item.data.people + ?.letNotEmpty { people -> + people.map { Person.fromDto(it, favorites[it.id] ?: false, api) } + }.orEmpty() + people + } + } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ac425f5e..53eb9b05 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -82,6 +82,7 @@ Playback Speed Playback Playlist + Playlists Preview User Profile Settings Queue @@ -135,6 +136,7 @@ Version Video Scale Video + Videos Watch live %1$d years old Play with transcoding From f61e819a986f3f15cafe99059618c201281d3147 Mon Sep 17 00:00:00 2001 From: Jakub Syty Date: Thu, 13 Nov 2025 23:27:20 +0100 Subject: [PATCH 2/5] Add FLAG_ACTIVITY_NEW_TASK to fix updates for some older devices (#211) Without this the installation intent is crashing with an error on my Xiaomi Mi Box S (Android 8.1) --- .../java/com/github/damontecres/wholphin/util/UpdateChecker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/UpdateChecker.kt b/app/src/main/java/com/github/damontecres/wholphin/util/UpdateChecker.kt index 144e566d..8ad876f3 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/UpdateChecker.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/UpdateChecker.kt @@ -236,7 +236,7 @@ class UpdateChecker val targetFile = fallbackDownload(it, callback) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { val intent = Intent(Intent.ACTION_INSTALL_PACKAGE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK) intent.data = FileProvider.getUriForFile( context, From fe29b0cd88a39c5c52c4d45adca35aea0ae1e601 Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Thu, 13 Nov 2025 18:31:23 -0500 Subject: [PATCH 3/5] Show local & remote trailers for TV Shows (#212) Shows local & remote trailers for TV Shows similar to how its shown for Movies Closes #141 --- .../wholphin/ui/detail/movie/MovieDetails.kt | 22 +---- .../ui/detail/movie/MovieViewModel.kt | 31 +------ .../ui/detail/series/SeriesDetails.kt | 28 ++++++- .../ui/detail/series/SeriesViewModel.kt | 10 +++ .../wholphin/util/TrailerService.kt | 81 +++++++++++++++++++ 5 files changed, 126 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/com/github/damontecres/wholphin/util/TrailerService.kt diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt index 5dc0993c..9f1d5f51 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt @@ -1,6 +1,5 @@ package com.github.damontecres.wholphin.ui.detail.movie -import android.content.Intent import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -82,6 +81,7 @@ import com.github.damontecres.wholphin.ui.rememberInt import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingState +import com.github.damontecres.wholphin.util.TrailerService import kotlinx.coroutines.launch import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.MediaType @@ -277,22 +277,8 @@ fun MovieDetails( items = items, ) }, - trailerOnClick = { trailer -> - when (trailer) { - is LocalTrailer -> - viewModel.navigateTo( - Destination.Playback( - itemId = trailer.baseItem.id, - item = trailer.baseItem, - positionMs = 0L, - ), - ) - - is RemoteTrailer -> { - val intent = Intent(Intent.ACTION_VIEW, trailer.url.toUri()) - context.startActivity(intent) - } - } + trailerOnClick = { + TrailerService.onClick(context, it, viewModel::navigateTo) }, modifier = modifier, ) @@ -567,7 +553,7 @@ fun TrailerRow( LazyRow( state = state, horizontalArrangement = Arrangement.spacedBy(16.dp), - contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), + contentPadding = PaddingValues(horizontal = 24.dp, vertical = 8.dp), modifier = Modifier .fillMaxWidth() diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt index 0c29ae3c..30a75ef8 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt @@ -4,16 +4,13 @@ import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.ChosenStreams import com.github.damontecres.wholphin.data.ItemPlaybackRepository import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.Chapter import com.github.damontecres.wholphin.data.model.ItemPlayback -import com.github.damontecres.wholphin.data.model.LocalTrailer import com.github.damontecres.wholphin.data.model.Person -import com.github.damontecres.wholphin.data.model.RemoteTrailer import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.preferences.ThemeSongVolume import com.github.damontecres.wholphin.ui.SlimItemFields @@ -27,6 +24,7 @@ import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.PeopleFavorites import com.github.damontecres.wholphin.util.ThemeSongPlayer +import com.github.damontecres.wholphin.util.TrailerService import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -57,6 +55,7 @@ class MovieViewModel private val themeSongPlayer: ThemeSongPlayer, private val favoriteWatchManager: FavoriteWatchManager, private val peopleFavorites: PeopleFavorites, + private val trailerService: TrailerService, @Assisted val itemId: UUID, ) : ViewModel() { @AssistedFactory @@ -108,31 +107,9 @@ class MovieViewModel loading.value = LoadingState.Success } viewModelScope.launchIO { - val remoteTrailers = - item.data.remoteTrailers - ?.mapNotNull { t -> - t.url?.let { url -> - val name = - t.name - // TODO would be nice to clean up the trailer name -// ?.replace(item.name ?: "", "") -// ?.removePrefix(" - ") - ?: context.getString(R.string.trailer) - RemoteTrailer(name, url) - } - }.orEmpty() - .sortedBy { it.name } - val localTrailerCount = item.data.localTrailerCount ?: 0 - val localTrailers = - if (localTrailerCount > 0) { - api.userLibraryApi.getLocalTrailers(itemId).content.map { - LocalTrailer(BaseItem.Companion.from(it, api)) - } - } else { - listOf() - } + val trailers = trailerService.getTrailers(item) withContext(Dispatchers.Main) { - this@MovieViewModel.trailers.value = localTrailers + remoteTrailers + this@MovieViewModel.trailers.value = trailers } } viewModelScope.launchIO { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt index 983e5798..91377f91 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt @@ -46,6 +46,7 @@ import coil3.compose.AsyncImage import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.Person +import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.Cards import com.github.damontecres.wholphin.ui.cards.ItemRow @@ -71,6 +72,7 @@ import com.github.damontecres.wholphin.ui.detail.PlaylistDialog import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForPerson +import com.github.damontecres.wholphin.ui.detail.movie.TrailerRow import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.nav.Destination @@ -79,6 +81,7 @@ import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingState +import com.github.damontecres.wholphin.util.TrailerService import kotlinx.coroutines.launch import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.MediaType @@ -102,6 +105,7 @@ fun SeriesDetails( val item by viewModel.item.observeAsState() val seasons by viewModel.seasons.observeAsState(listOf()) + val trailers by viewModel.trailers.observeAsState(listOf()) val people by viewModel.people.observeAsState(listOf()) val similar by viewModel.similar.observeAsState(listOf()) @@ -133,6 +137,7 @@ fun SeriesDetails( preferences = preferences, series = item, seasons = seasons, + trailers = trailers, people = people, similar = similar, played = played, @@ -174,6 +179,9 @@ fun SeriesDetails( val favorite = item.data.userData?.isFavorite ?: false viewModel.setFavorite(item.id, !favorite, null) }, + trailerOnClick = { + TrailerService.onClick(context, it, viewModel::navigateTo) + }, moreActions = MoreDialogActions( navigateTo = { viewModel.navigateTo(it) }, @@ -244,7 +252,8 @@ fun SeriesDetails( private const val HEADER_ROW = 0 private const val SEASONS_ROW = HEADER_ROW + 1 private const val PEOPLE_ROW = SEASONS_ROW + 1 -private const val SIMILAR_ROW = PEOPLE_ROW + 1 +private const val TRAILER_ROW = PEOPLE_ROW + 1 +private const val SIMILAR_ROW = TRAILER_ROW + 1 @Composable fun SeriesDetailsContent( @@ -252,6 +261,7 @@ fun SeriesDetailsContent( series: BaseItem, seasons: List, similar: List, + trailers: List, people: List, played: Boolean, favorite: Boolean, @@ -262,6 +272,7 @@ fun SeriesDetailsContent( playOnClick: () -> Unit, watchOnClick: () -> Unit, favoriteOnClick: () -> Unit, + trailerOnClick: (Trailer) -> Unit, moreActions: MoreDialogActions, modifier: Modifier = Modifier, ) { @@ -452,6 +463,21 @@ fun SeriesDetailsContent( ) } } + if (trailers.isNotEmpty()) { + item { + TrailerRow( + trailers = trailers, + onClickTrailer = { + position = TRAILER_ROW + trailerOnClick.invoke(it) + }, + modifier = + Modifier + .fillMaxWidth() + .focusRequester(focusRequesters[TRAILER_ROW]), + ) + } + } if (similar.isNotEmpty()) { item { ItemRow( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt index 43ca6f55..8a1a96bf 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt @@ -10,6 +10,7 @@ import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.ItemPlayback import com.github.damontecres.wholphin.data.model.Person +import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.preferences.ThemeSongVolume import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.SlimItemFields @@ -29,6 +30,7 @@ import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.PeopleFavorites import com.github.damontecres.wholphin.util.ThemeSongPlayer +import com.github.damontecres.wholphin.util.TrailerService import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers @@ -62,12 +64,14 @@ class SeriesViewModel private val themeSongPlayer: ThemeSongPlayer, private val favoriteWatchManager: FavoriteWatchManager, private val peopleFavorites: PeopleFavorites, + private val trailerService: TrailerService, ) : ItemViewModel(api) { private lateinit var seriesId: UUID private lateinit var prefs: UserPreferences val loading = MutableLiveData(LoadingState.Loading) val seasons = MutableLiveData>(listOf()) val episodes = MutableLiveData(EpisodeList.Loading) + val trailers = MutableLiveData>(listOf()) val people = MutableLiveData>(listOf()) val similar = MutableLiveData>() @@ -111,6 +115,12 @@ class SeriesViewModel episodes.value = episodeInfo loading.value = LoadingState.Success } + viewModelScope.launchIO { + val trailers = trailerService.getTrailers(item) + withContext(Dispatchers.Main) { + this@SeriesViewModel.trailers.value = trailers + } + } viewModelScope.launchIO { val people = peopleFavorites.getPeopleFor(item) this@SeriesViewModel.people.setValueOnMain(people) diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/TrailerService.kt b/app/src/main/java/com/github/damontecres/wholphin/util/TrailerService.kt new file mode 100644 index 00000000..4a60dea5 --- /dev/null +++ b/app/src/main/java/com/github/damontecres/wholphin/util/TrailerService.kt @@ -0,0 +1,81 @@ +package com.github.damontecres.wholphin.util + +import android.content.Context +import android.content.Intent +import androidx.core.net.toUri +import com.github.damontecres.wholphin.R +import com.github.damontecres.wholphin.data.model.BaseItem +import com.github.damontecres.wholphin.data.model.LocalTrailer +import com.github.damontecres.wholphin.data.model.RemoteTrailer +import com.github.damontecres.wholphin.data.model.Trailer +import com.github.damontecres.wholphin.ui.nav.Destination +import dagger.hilt.android.qualifiers.ApplicationContext +import org.jellyfin.sdk.api.client.ApiClient +import org.jellyfin.sdk.api.client.extensions.userLibraryApi +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TrailerService + @Inject + constructor( + @param:ApplicationContext private val context: Context, + private val api: ApiClient, + ) { + suspend fun getTrailers(item: BaseItem): List { + val remoteTrailers = + item.data.remoteTrailers + ?.mapNotNull { t -> + t.url?.let { url -> + val name = + t.name + // TODO would be nice to clean up the trailer name +// ?.replace(item.name ?: "", "") +// ?.removePrefix(" - ") + ?: context.getString(R.string.trailer) + RemoteTrailer(name, url) + } + }.orEmpty() + .sortedBy { it.name } + val localTrailerCount = item.data.localTrailerCount ?: 0 + val localTrailers = + if (localTrailerCount > 0) { + api.userLibraryApi.getLocalTrailers(item.id).content.map { + LocalTrailer(BaseItem.Companion.from(it, api)) + } + } else { + listOf() + } + return localTrailers + remoteTrailers + } + + companion object { + /** + * Note: This is explicitly not a member function because the injected Context is not an Activity. + * We want to start the intent without a new task which requires the Activity context + * + * This can be provided by LocalContext.current from Compose + */ + fun onClick( + context: Context, + trailer: Trailer, + navigateTo: (Destination) -> Unit, + ) { + when (trailer) { + is LocalTrailer -> + navigateTo.invoke( + Destination.Playback( + itemId = trailer.baseItem.id, + item = trailer.baseItem, + positionMs = 0L, + ), + ) + + is RemoteTrailer -> { + val intent = Intent(Intent.ACTION_VIEW, trailer.url.toUri()) + context.startActivity(intent) + } + } + } + } + } From 76b7a3332892f182bcf753252e7bda779376ba3e Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Thu, 13 Nov 2025 18:40:52 -0500 Subject: [PATCH 4/5] Fix two issues on recommended pages (#213) Fix an issue where if there are no items for the "Continue Watching" row, the tab row would disappear Fix an issue where some rows (eg Recently released, recently added, suggestions, etc) on the recommended pages might never show up Fixes #207 --- .../wholphin/ui/components/RecommendedContent.kt | 2 +- .../wholphin/ui/components/RecommendedMovie.kt | 9 ++++++--- .../wholphin/ui/components/RecommendedTvShow.kt | 6 +++--- .../com/github/damontecres/wholphin/ui/main/HomePage.kt | 8 +++++++- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedContent.kt index 55e37c73..f2199333 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedContent.kt @@ -42,7 +42,7 @@ abstract class RecommendedViewModel( ) : ViewModel() { abstract fun init() - abstract val rows: MutableStateFlow> + abstract val rows: MutableStateFlow> val loading = MutableLiveData(LoadingState.Loading) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt index 48388419..7eb676d0 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt @@ -57,13 +57,13 @@ class RecommendedMovieViewModel } override val rows = - MutableStateFlow>( + MutableStateFlow>( rowTitles .map { HomeRowLoadingState.Pending( context.getString(it), ) - }.toMutableList(), + }, ) override fun init() { @@ -166,6 +166,9 @@ class RecommendedMovieViewModel val result = try { pager.init() + if (pager.isNotEmpty()) { + pager.getBlocking(0) + } HomeRowLoadingState.Success(title, pager) } catch (ex: Exception) { Timber.e(ex, "Error fetching %s", title) @@ -182,7 +185,7 @@ class RecommendedMovieViewModel row: HomeRowLoadingState, ) { rows.update { current -> - current.apply { set(position, row) } + current.toMutableList().apply { set(position, row) } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt index f504c614..cf5580eb 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt @@ -59,13 +59,13 @@ class RecommendedTvShowViewModel } override val rows = - MutableStateFlow>( + MutableStateFlow>( rowTitles .map { HomeRowLoadingState.Pending( context.getString(it), ) - }.toMutableList(), + }, ) override fun init() { @@ -210,7 +210,7 @@ class RecommendedTvShowViewModel row: HomeRowLoadingState, ) { rows.update { current -> - current.apply { set(position, row) } + current.toMutableList().apply { set(position, row) } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt index e73f0cb6..cf1fb36f 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt @@ -360,9 +360,15 @@ fun HomePageContent( ), ).onFocusChanged { if (it.isFocused) { + val nonEmptyRowBefore = + homeRows + .subList(0, rowIndex) + .count { + it is HomeRowLoadingState.Success && it.items.isEmpty() + } onFocusPosition?.invoke( RowColumn( - rowIndex, + rowIndex - nonEmptyRowBefore, index, ), ) From b7cb6efc4f7e9514e2b8f6fb9f00017c031c8464 Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Thu, 13 Nov 2025 19:39:37 -0500 Subject: [PATCH 5/5] Dev: Move services into their own package (#214) Moves service classes into their own files and package for easier organization and discovery No user facing changes in the PR --- DEVELOPMENT.md | 9 ++ .../damontecres/wholphin/MainActivity.kt | 12 +- .../wholphin/data/model/BaseItem.kt | 2 +- .../wholphin/data/model/Playlist.kt | 128 ----------------- .../{util => services}/AppUpgradeHandler.kt | 15 +- .../FavoriteWatchManager.kt | 2 +- .../{ui/nav => services}/NavigationManager.kt | 7 +- .../{util => services}/PeopleFavorites.kt | 2 +- .../PlaybackLifecycleObserver.kt | 3 +- .../{util => services}/PlayerFactory.kt | 2 +- .../wholphin/services/PlaylistCreator.kt | 135 ++++++++++++++++++ .../{util => services}/ServerEventListener.kt | 2 +- .../{util => services}/ThemeSongPlayer.kt | 4 +- .../{util => services}/TrailerService.kt | 2 +- .../{util => services}/UpdateChecker.kt | 9 +- .../wholphin/{ => services}/hilt/AppModule.kt | 6 +- .../{ => services}/hilt/DatabaseModule.kt | 2 +- .../wholphin/{util => ui}/Formatting.kt | 4 +- .../wholphin/ui/cards/EpisodeCard.kt | 12 +- .../ui/components/CollectionFolderGrid.kt | 4 +- .../wholphin/ui/components/GenreCardGrid.kt | 2 +- .../ui/components/RecommendedContent.kt | 4 +- .../ui/components/RecommendedMovie.kt | 4 +- .../ui/components/RecommendedTvShow.kt | 4 +- .../wholphin/ui/data/AddPlaylistViewModel.kt | 2 +- .../wholphin/ui/data/ItemDetailsDialogInfo.kt | 4 +- .../ui/detail/CollectionFolderLiveTv.kt | 2 +- .../wholphin/ui/detail/PersonPage.kt | 6 +- .../wholphin/ui/detail/PlaylistDetails.kt | 2 +- .../wholphin/ui/detail/livetv/DvrSchedule.kt | 4 +- .../ui/detail/livetv/LiveTvViewModel.kt | 2 +- .../ui/detail/livetv/ProgramDialog.kt | 2 +- .../wholphin/ui/detail/movie/MovieDetails.kt | 2 +- .../ui/detail/movie/MovieDetailsHeader.kt | 4 +- .../ui/detail/movie/MovieViewModel.kt | 10 +- .../ui/detail/series/FocusedEpisodeFooter.kt | 4 +- .../ui/detail/series/FocusedEpisodeHeader.kt | 4 +- .../ui/detail/series/SeriesDetails.kt | 2 +- .../ui/detail/series/SeriesOverview.kt | 2 +- .../ui/detail/series/SeriesOverviewContent.kt | 2 +- .../ui/detail/series/SeriesViewModel.kt | 10 +- .../damontecres/wholphin/ui/main/HomePage.kt | 4 +- .../wholphin/ui/main/HomeViewModel.kt | 4 +- .../wholphin/ui/main/SearchPage.kt | 2 +- .../wholphin/ui/nav/ApplicationContent.kt | 1 + .../damontecres/wholphin/ui/nav/NavDrawer.kt | 3 +- .../ui/playback/DownloadSubtitlesDialog.kt | 2 +- .../wholphin/ui/playback/PlaybackControls.kt | 2 +- .../wholphin/ui/playback/PlaybackOverlay.kt | 2 +- .../wholphin/ui/playback/PlaybackPage.kt | 4 +- .../wholphin/ui/playback/PlaybackViewModel.kt | 10 +- .../ui/preferences/PreferencesViewModel.kt | 2 +- .../wholphin/ui/setup/InstallUpdatePage.kt | 10 +- .../ui/setup/SwitchServerViewModel.kt | 2 +- .../wholphin/ui/setup/SwitchUserViewModel.kt | 2 +- .../wholphin/util/CrashReportSenderFactory.kt | 2 +- 56 files changed, 259 insertions(+), 233 deletions(-) rename app/src/main/java/com/github/damontecres/wholphin/{util => services}/AppUpgradeHandler.kt (87%) rename app/src/main/java/com/github/damontecres/wholphin/{util => services}/FavoriteWatchManager.kt (95%) rename app/src/main/java/com/github/damontecres/wholphin/{ui/nav => services}/NavigationManager.kt (88%) rename app/src/main/java/com/github/damontecres/wholphin/{util => services}/PeopleFavorites.kt (96%) rename app/src/main/java/com/github/damontecres/wholphin/{util => services}/PlaybackLifecycleObserver.kt (93%) rename app/src/main/java/com/github/damontecres/wholphin/{util => services}/PlayerFactory.kt (98%) create mode 100644 app/src/main/java/com/github/damontecres/wholphin/services/PlaylistCreator.kt rename app/src/main/java/com/github/damontecres/wholphin/{util => services}/ServerEventListener.kt (98%) rename app/src/main/java/com/github/damontecres/wholphin/{util => services}/ThemeSongPlayer.kt (96%) rename app/src/main/java/com/github/damontecres/wholphin/{util => services}/TrailerService.kt (98%) rename app/src/main/java/com/github/damontecres/wholphin/{util => services}/UpdateChecker.kt (98%) rename app/src/main/java/com/github/damontecres/wholphin/{ => services}/hilt/AppModule.kt (96%) rename app/src/main/java/com/github/damontecres/wholphin/{ => services}/hilt/DatabaseModule.kt (97%) rename app/src/main/java/com/github/damontecres/wholphin/{util => ui}/Formatting.kt (98%) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index aa6a830f..44cba5bc 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -25,6 +25,15 @@ After forking and cloning your fork, you can import the project into Android Stu You need a compatible Android Studio version for the configured AGP. This is generally `Narwhal 3 Feature Drop | 2025.1.3` or newer. See https://developer.android.com/build/releases/gradle-plugin and [`libs.versions.toml](./gradle/libs.versions.toml). +### Code organization + +Code is split into several packages: +- `data` - app-specific data models and services +- `preferences` - Non-UI related code for user settings and preferences +- `services` - hilt injectable services often used by ViewModels for API calls +- `ui` - User interface code and ViewModels +- `util` - Utility classes and functions + ### FFmpeg decoder module The app ships with [media3 ffmpeg decoder module](https://github.com/androidx/media/blob/release/libraries/decoder_ffmpeg/README.md). diff --git a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt index ac72c3b5..3753ce25 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt @@ -27,20 +27,20 @@ import androidx.tv.material3.ExperimentalTvMaterial3Api import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Surface import com.github.damontecres.wholphin.data.ServerRepository -import com.github.damontecres.wholphin.hilt.AuthOkHttpClient import com.github.damontecres.wholphin.preferences.AppPreferences import com.github.damontecres.wholphin.preferences.DefaultUserConfiguration import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.AppUpgradeHandler +import com.github.damontecres.wholphin.services.NavigationManager +import com.github.damontecres.wholphin.services.PlaybackLifecycleObserver +import com.github.damontecres.wholphin.services.ServerEventListener +import com.github.damontecres.wholphin.services.UpdateChecker +import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient import com.github.damontecres.wholphin.ui.CoilConfig import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.ApplicationContent import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.theme.WholphinTheme -import com.github.damontecres.wholphin.util.AppUpgradeHandler -import com.github.damontecres.wholphin.util.PlaybackLifecycleObserver -import com.github.damontecres.wholphin.util.ServerEventListener -import com.github.damontecres.wholphin.util.UpdateChecker import com.github.damontecres.wholphin.util.profile.createDeviceProfile import dagger.hilt.android.AndroidEntryPoint import okhttp3.OkHttpClient diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt b/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt index e3ffbcbd..2188e9b7 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt @@ -2,7 +2,7 @@ package com.github.damontecres.wholphin.data.model import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.util.seasonEpisode +import com.github.damontecres.wholphin.ui.seasonEpisode import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import org.jellyfin.sdk.api.client.ApiClient diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/model/Playlist.kt b/app/src/main/java/com/github/damontecres/wholphin/data/model/Playlist.kt index a4ab91ad..c4cd3792 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/data/model/Playlist.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/data/model/Playlist.kt @@ -1,34 +1,10 @@ package com.github.damontecres.wholphin.data.model -import android.content.Context import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.setValue -import com.github.damontecres.wholphin.R -import com.github.damontecres.wholphin.data.ServerRepository -import com.github.damontecres.wholphin.ui.DefaultItemFields -import com.github.damontecres.wholphin.ui.indexOfFirstOrNull -import com.github.damontecres.wholphin.ui.toServerString -import com.github.damontecres.wholphin.util.ApiRequestPager -import com.github.damontecres.wholphin.util.GetEpisodesRequestHandler -import com.github.damontecres.wholphin.util.GetItemsRequestHandler -import com.github.damontecres.wholphin.util.GetPlaylistItemsRequestHandler -import com.github.damontecres.wholphin.util.TransformList -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope -import org.jellyfin.sdk.api.client.ApiClient -import org.jellyfin.sdk.api.client.extensions.playlistsApi -import org.jellyfin.sdk.model.api.BaseItemKind -import org.jellyfin.sdk.model.api.CreatePlaylistDto import org.jellyfin.sdk.model.api.MediaType -import org.jellyfin.sdk.model.api.PlaylistUserPermissions -import org.jellyfin.sdk.model.api.request.GetEpisodesRequest -import org.jellyfin.sdk.model.api.request.GetItemsRequest -import org.jellyfin.sdk.model.api.request.GetPlaylistItemsRequest -import org.jellyfin.sdk.model.serializer.toUUIDOrNull import java.util.UUID -import javax.inject.Inject -import javax.inject.Singleton class Playlist( items: List, @@ -71,107 +47,3 @@ data class PlaylistInfo( val count: Int, val mediaType: MediaType, ) - -@Singleton -class PlaylistCreator - @Inject - constructor( - @param:ApplicationContext private val context: Context, - private val api: ApiClient, - private val serverRepository: ServerRepository, - ) { - suspend fun createFromEpisode( - seriesId: UUID, - episodeId: UUID, - ): Playlist { - val request = - GetEpisodesRequest( - seriesId = seriesId, - fields = DefaultItemFields, - startItemId = episodeId, - limit = Playlist.MAX_SIZE, - ) - val episodes = GetEpisodesRequestHandler.execute(api, request).content.items - val startIndex = - episodes.indexOfFirstOrNull { it.id == episodeId } - ?: throw IllegalStateException("Episode $episodeId was not returned") - return Playlist(episodes.map { BaseItem.from(it, api) }, startIndex) - } - - suspend fun createFromPlaylistId( - playlistId: UUID, - startIndex: Int?, - shuffled: Boolean, - ): Playlist { - val request = - GetPlaylistItemsRequest( - playlistId = playlistId, - fields = DefaultItemFields, - startIndex = startIndex, - limit = Playlist.MAX_SIZE, - ) - val items = GetPlaylistItemsRequestHandler.execute(api, request).content.items - var baseItems = items.map { BaseItem.from(it, api) } - if (shuffled) { - baseItems = baseItems.shuffled() - } - return Playlist(baseItems, 0) - } - - suspend fun getPlaylists( - mediaType: MediaType?, - scope: CoroutineScope, - ): List { - val request = - GetItemsRequest( - includeItemTypes = listOf(BaseItemKind.PLAYLIST), - mediaTypes = mediaType?.let { listOf(mediaType) }, - recursive = true, - ) - val pager = ApiRequestPager(api, request, GetItemsRequestHandler, scope).init() - return TransformList(pager) { - it?.let { - PlaylistInfo( - id = it.id, - name = it.name ?: context.getString(R.string.unknown), - count = it.data.childCount ?: 0, - mediaType = it.data.mediaType, - ) - } - } - } - - suspend fun createPlaylist( - name: String, - initialItems: List, - ): UUID? = - serverRepository.currentUser.value?.let { user -> - api.playlistsApi - .createPlaylist( - CreatePlaylistDto( - name = name, - ids = initialItems, - users = listOf(PlaylistUserPermissions(user.id, true)), - isPublic = false, - ), - ).content.id - .toUUIDOrNull() - } - - suspend fun addToPlaylist( - playlistId: UUID, - itemId: UUID, - ) { - api.playlistsApi.addItemToPlaylist(playlistId, listOf(itemId)) - } - - suspend fun removeFromPlaylist( - playlistId: UUID, - itemId: UUID, - ) { - api.playlistsApi.removeItemFromPlaylist( - playlistId.toServerString(), - listOf(itemId.toServerString()), - ) - } - } diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/AppUpgradeHandler.kt b/app/src/main/java/com/github/damontecres/wholphin/services/AppUpgradeHandler.kt similarity index 87% rename from app/src/main/java/com/github/damontecres/wholphin/util/AppUpgradeHandler.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/AppUpgradeHandler.kt index 263ced7a..b2399e05 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/AppUpgradeHandler.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/AppUpgradeHandler.kt @@ -1,4 +1,4 @@ -package com.github.damontecres.wholphin.util +package com.github.damontecres.wholphin.services import android.content.Context import android.os.Build @@ -11,6 +11,7 @@ import com.github.damontecres.wholphin.preferences.AppPreferences import com.github.damontecres.wholphin.preferences.updateInterfacePreferences import com.github.damontecres.wholphin.preferences.updatePlaybackOverrides import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel +import com.github.damontecres.wholphin.util.Version import dagger.hilt.android.qualifiers.ApplicationContext import timber.log.Timber import javax.inject.Inject @@ -49,8 +50,8 @@ class AppUpgradeHandler try { upgradeApp( context, - Version.fromString(previousVersion ?: "0.0.0"), - Version.fromString(newVersion), + Version.Companion.fromString(previousVersion ?: "0.0.0"), + Version.Companion.fromString(newVersion), appPreferences, ) } catch (ex: Exception) { @@ -73,7 +74,7 @@ suspend fun upgradeApp( current: Version, appPreferences: DataStore, ) { - if (previous.isEqualOrBefore(Version.fromString("0.1.0-2-g0"))) { + if (previous.isEqualOrBefore(Version.Companion.fromString("0.1.0-2-g0"))) { appPreferences.updateData { it.updatePlaybackOverrides { ac3Supported = AppPreference.Ac3Supported.defaultValue @@ -83,21 +84,21 @@ suspend fun upgradeApp( } } } - if (previous.isEqualOrBefore(Version.fromString("0.2.3-6-g0"))) { + if (previous.isEqualOrBefore(Version.Companion.fromString("0.2.3-6-g0"))) { appPreferences.updateData { it.updateInterfacePreferences { navDrawerSwitchOnFocus = AppPreference.NavDrawerSwitchOnFocus.defaultValue } } } - if (previous.isEqualOrBefore(Version.fromString("0.2.5-11-g0"))) { + if (previous.isEqualOrBefore(Version.Companion.fromString("0.2.5-11-g0"))) { appPreferences.updateData { it.updateInterfacePreferences { showClock = AppPreference.ShowClock.defaultValue } } } - if (previous.isEqualOrBefore(Version.fromString("0.2.7-1-g0"))) { + if (previous.isEqualOrBefore(Version.Companion.fromString("0.2.7-1-g0"))) { PreferencesViewModel.resetSubtitleSettings(appPreferences) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/FavoriteWatchManager.kt b/app/src/main/java/com/github/damontecres/wholphin/services/FavoriteWatchManager.kt similarity index 95% rename from app/src/main/java/com/github/damontecres/wholphin/util/FavoriteWatchManager.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/FavoriteWatchManager.kt index 72540435..6a3e9a68 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/FavoriteWatchManager.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/FavoriteWatchManager.kt @@ -1,4 +1,4 @@ -package com.github.damontecres.wholphin.util +package com.github.damontecres.wholphin.services import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.extensions.playStateApi diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavigationManager.kt b/app/src/main/java/com/github/damontecres/wholphin/services/NavigationManager.kt similarity index 88% rename from app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavigationManager.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/NavigationManager.kt index d07a9f9b..d9e18ef5 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavigationManager.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/NavigationManager.kt @@ -1,6 +1,7 @@ -package com.github.damontecres.wholphin.ui.nav +package com.github.damontecres.wholphin.services import androidx.navigation3.runtime.NavKey +import com.github.damontecres.wholphin.ui.nav.Destination import org.acra.ACRA import timber.log.Timber import javax.inject.Inject @@ -16,7 +17,7 @@ class NavigationManager var backStack: MutableList = mutableListOf() /** - * Go to the specified [Destination] + * Go to the specified [com.github.damontecres.wholphin.ui.nav.Destination] */ fun navigateTo(destination: Destination) { backStack.add(destination) @@ -70,7 +71,7 @@ class NavigationManager private fun log() { val dest = backStack.lastOrNull().toString() - Timber.i("Current Destination: %s", dest) + Timber.Forest.i("Current Destination: %s", dest) ACRA.errorReporter.putCustomData("destination", dest) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/PeopleFavorites.kt b/app/src/main/java/com/github/damontecres/wholphin/services/PeopleFavorites.kt similarity index 96% rename from app/src/main/java/com/github/damontecres/wholphin/util/PeopleFavorites.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/PeopleFavorites.kt index 0ca4f0ba..b4967b03 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/PeopleFavorites.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/PeopleFavorites.kt @@ -1,4 +1,4 @@ -package com.github.damontecres.wholphin.util +package com.github.damontecres.wholphin.services import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.Person diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/PlaybackLifecycleObserver.kt b/app/src/main/java/com/github/damontecres/wholphin/services/PlaybackLifecycleObserver.kt similarity index 93% rename from app/src/main/java/com/github/damontecres/wholphin/util/PlaybackLifecycleObserver.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/PlaybackLifecycleObserver.kt index be93c292..86906280 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/PlaybackLifecycleObserver.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/PlaybackLifecycleObserver.kt @@ -1,9 +1,8 @@ -package com.github.damontecres.wholphin.util +package com.github.damontecres.wholphin.services import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.nav.NavigationManager import dagger.hilt.android.scopes.ActivityRetainedScoped import javax.inject.Inject diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/PlayerFactory.kt b/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt similarity index 98% rename from app/src/main/java/com/github/damontecres/wholphin/util/PlayerFactory.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt index 9501fc01..45bf8e64 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/PlayerFactory.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt @@ -1,6 +1,6 @@ @file:OptIn(markerClass = [UnstableApi::class]) -package com.github.damontecres.wholphin.util +package com.github.damontecres.wholphin.services import android.content.Context import androidx.annotation.OptIn diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/PlaylistCreator.kt b/app/src/main/java/com/github/damontecres/wholphin/services/PlaylistCreator.kt new file mode 100644 index 00000000..c7730d9e --- /dev/null +++ b/app/src/main/java/com/github/damontecres/wholphin/services/PlaylistCreator.kt @@ -0,0 +1,135 @@ +package com.github.damontecres.wholphin.services + +import android.content.Context +import com.github.damontecres.wholphin.R +import com.github.damontecres.wholphin.data.ServerRepository +import com.github.damontecres.wholphin.data.model.BaseItem +import com.github.damontecres.wholphin.data.model.Playlist +import com.github.damontecres.wholphin.data.model.PlaylistInfo +import com.github.damontecres.wholphin.ui.DefaultItemFields +import com.github.damontecres.wholphin.ui.indexOfFirstOrNull +import com.github.damontecres.wholphin.ui.toServerString +import com.github.damontecres.wholphin.util.ApiRequestPager +import com.github.damontecres.wholphin.util.GetEpisodesRequestHandler +import com.github.damontecres.wholphin.util.GetItemsRequestHandler +import com.github.damontecres.wholphin.util.GetPlaylistItemsRequestHandler +import com.github.damontecres.wholphin.util.TransformList +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.CoroutineScope +import org.jellyfin.sdk.api.client.ApiClient +import org.jellyfin.sdk.api.client.extensions.playlistsApi +import org.jellyfin.sdk.model.api.BaseItemKind +import org.jellyfin.sdk.model.api.CreatePlaylistDto +import org.jellyfin.sdk.model.api.MediaType +import org.jellyfin.sdk.model.api.PlaylistUserPermissions +import org.jellyfin.sdk.model.api.request.GetEpisodesRequest +import org.jellyfin.sdk.model.api.request.GetItemsRequest +import org.jellyfin.sdk.model.api.request.GetPlaylistItemsRequest +import org.jellyfin.sdk.model.serializer.toUUIDOrNull +import java.util.UUID +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class PlaylistCreator + @Inject + constructor( + @param:ApplicationContext private val context: Context, + private val api: ApiClient, + private val serverRepository: ServerRepository, + ) { + suspend fun createFromEpisode( + seriesId: UUID, + episodeId: UUID, + ): Playlist { + val request = + GetEpisodesRequest( + seriesId = seriesId, + fields = DefaultItemFields, + startItemId = episodeId, + limit = Playlist.Companion.MAX_SIZE, + ) + val episodes = GetEpisodesRequestHandler.execute(api, request).content.items + val startIndex = + episodes.indexOfFirstOrNull { it.id == episodeId } + ?: throw IllegalStateException("Episode $episodeId was not returned") + return Playlist(episodes.map { BaseItem.Companion.from(it, api) }, startIndex) + } + + suspend fun createFromPlaylistId( + playlistId: UUID, + startIndex: Int?, + shuffled: Boolean, + ): Playlist { + val request = + GetPlaylistItemsRequest( + playlistId = playlistId, + fields = DefaultItemFields, + startIndex = startIndex, + limit = Playlist.Companion.MAX_SIZE, + ) + val items = GetPlaylistItemsRequestHandler.execute(api, request).content.items + var baseItems = items.map { BaseItem.Companion.from(it, api) } + if (shuffled) { + baseItems = baseItems.shuffled() + } + return Playlist(baseItems, 0) + } + + suspend fun getPlaylists( + mediaType: MediaType?, + scope: CoroutineScope, + ): List { + val request = + GetItemsRequest( + includeItemTypes = listOf(BaseItemKind.PLAYLIST), + mediaTypes = mediaType?.let { listOf(mediaType) }, + recursive = true, + ) + val pager = ApiRequestPager(api, request, GetItemsRequestHandler, scope).init() + return TransformList(pager) { + it?.let { + PlaylistInfo( + id = it.id, + name = it.name ?: context.getString(R.string.unknown), + count = it.data.childCount ?: 0, + mediaType = it.data.mediaType, + ) + } + } + } + + suspend fun createPlaylist( + name: String, + initialItems: List, + ): UUID? = + serverRepository.currentUser.value?.let { user -> + api.playlistsApi + .createPlaylist( + CreatePlaylistDto( + name = name, + ids = initialItems, + users = listOf(PlaylistUserPermissions(user.id, true)), + isPublic = false, + ), + ).content.id + .toUUIDOrNull() + } + + suspend fun addToPlaylist( + playlistId: UUID, + itemId: UUID, + ) { + api.playlistsApi.addItemToPlaylist(playlistId, listOf(itemId)) + } + + suspend fun removeFromPlaylist( + playlistId: UUID, + itemId: UUID, + ) { + api.playlistsApi.removeItemFromPlaylist( + playlistId.toServerString(), + listOf(itemId.toServerString()), + ) + } + } diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/ServerEventListener.kt b/app/src/main/java/com/github/damontecres/wholphin/services/ServerEventListener.kt similarity index 98% rename from app/src/main/java/com/github/damontecres/wholphin/util/ServerEventListener.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/ServerEventListener.kt index f792f2f9..f71c902a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/ServerEventListener.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/ServerEventListener.kt @@ -1,4 +1,4 @@ -package com.github.damontecres.wholphin.util +package com.github.damontecres.wholphin.services import android.content.Context import android.widget.Toast diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/ThemeSongPlayer.kt b/app/src/main/java/com/github/damontecres/wholphin/services/ThemeSongPlayer.kt similarity index 96% rename from app/src/main/java/com/github/damontecres/wholphin/util/ThemeSongPlayer.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/ThemeSongPlayer.kt index bb9ec887..f43df5cc 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/ThemeSongPlayer.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/ThemeSongPlayer.kt @@ -1,4 +1,4 @@ -package com.github.damontecres.wholphin.util +package com.github.damontecres.wholphin.services import android.content.Context import androidx.annotation.OptIn @@ -8,8 +8,8 @@ import androidx.media3.common.util.UnstableApi import androidx.media3.datasource.okhttp.OkHttpDataSource import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.DefaultMediaSourceFactory -import com.github.damontecres.wholphin.hilt.AuthOkHttpClient import com.github.damontecres.wholphin.preferences.ThemeSongVolume +import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient import com.github.damontecres.wholphin.util.profile.Codec import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/TrailerService.kt b/app/src/main/java/com/github/damontecres/wholphin/services/TrailerService.kt similarity index 98% rename from app/src/main/java/com/github/damontecres/wholphin/util/TrailerService.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/TrailerService.kt index 4a60dea5..42f56b04 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/TrailerService.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/TrailerService.kt @@ -1,4 +1,4 @@ -package com.github.damontecres.wholphin.util +package com.github.damontecres.wholphin.services import android.content.Context import android.content.Intent diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/UpdateChecker.kt b/app/src/main/java/com/github/damontecres/wholphin/services/UpdateChecker.kt similarity index 98% rename from app/src/main/java/com/github/damontecres/wholphin/util/UpdateChecker.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/UpdateChecker.kt index 8ad876f3..4b6b2b42 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/UpdateChecker.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/UpdateChecker.kt @@ -1,4 +1,4 @@ -package com.github.damontecres.wholphin.util +package com.github.damontecres.wholphin.services import android.Manifest import android.content.ContentValues @@ -15,9 +15,10 @@ import androidx.core.content.FileProvider import androidx.core.content.edit import androidx.preference.PreferenceManager import com.github.damontecres.wholphin.R -import com.github.damontecres.wholphin.hilt.StandardOkHttpClient +import com.github.damontecres.wholphin.services.hilt.StandardOkHttpClient import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.showToast +import com.github.damontecres.wholphin.util.Version import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -110,7 +111,7 @@ class UpdateChecker fun getInstalledVersion(): Version { val pkgInfo = context.packageManager.getPackageInfo(context.packageName, 0) - return Version.fromString(pkgInfo.versionName!!) + return Version.Companion.fromString(pkgInfo.versionName!!) } suspend fun getLatestRelease(updateUrl: String): Release? { @@ -135,7 +136,7 @@ class UpdateChecker if (it.isSuccessful && it.body != null) { val result = Json.parseToJsonElement(it.body!!.string()) val name = result.jsonObject["name"]?.jsonPrimitive?.contentOrNull - val version = Version.tryFromString(name) + val version = Version.Companion.tryFromString(name) val publishedAt = result.jsonObject["published_at"]?.jsonPrimitive?.contentOrNull val body = result.jsonObject["body"]?.jsonPrimitive?.contentOrNull diff --git a/app/src/main/java/com/github/damontecres/wholphin/hilt/AppModule.kt b/app/src/main/java/com/github/damontecres/wholphin/services/hilt/AppModule.kt similarity index 96% rename from app/src/main/java/com/github/damontecres/wholphin/hilt/AppModule.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/hilt/AppModule.kt index 6d55525d..8d2d2e79 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/hilt/AppModule.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/hilt/AppModule.kt @@ -1,4 +1,4 @@ -package com.github.damontecres.wholphin.hilt +package com.github.damontecres.wholphin.services.hilt import android.annotation.SuppressLint import android.content.Context @@ -10,9 +10,9 @@ import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.preferences.AppPreferences import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.preferences.updateInterfacePreferences +import com.github.damontecres.wholphin.services.FavoriteWatchManager +import com.github.damontecres.wholphin.services.FavoriteWatchManagerImpl import com.github.damontecres.wholphin.util.ExceptionHandler -import com.github.damontecres.wholphin.util.FavoriteWatchManager -import com.github.damontecres.wholphin.util.FavoriteWatchManagerImpl import com.github.damontecres.wholphin.util.RememberTabManager import dagger.Module import dagger.Provides diff --git a/app/src/main/java/com/github/damontecres/wholphin/hilt/DatabaseModule.kt b/app/src/main/java/com/github/damontecres/wholphin/services/hilt/DatabaseModule.kt similarity index 97% rename from app/src/main/java/com/github/damontecres/wholphin/hilt/DatabaseModule.kt rename to app/src/main/java/com/github/damontecres/wholphin/services/hilt/DatabaseModule.kt index 441a9033..dbf61912 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/hilt/DatabaseModule.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/hilt/DatabaseModule.kt @@ -1,4 +1,4 @@ -package com.github.damontecres.wholphin.hilt +package com.github.damontecres.wholphin.services.hilt import android.content.Context import androidx.datastore.core.DataStore diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/Formatting.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/Formatting.kt similarity index 98% rename from app/src/main/java/com/github/damontecres/wholphin/util/Formatting.kt rename to app/src/main/java/com/github/damontecres/wholphin/ui/Formatting.kt index 9622dee7..1e5c1ae3 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/Formatting.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/Formatting.kt @@ -1,4 +1,4 @@ -package com.github.damontecres.wholphin.util +package com.github.damontecres.wholphin.ui import android.os.Build import androidx.annotation.StringRes @@ -10,7 +10,7 @@ import com.github.damontecres.wholphin.data.model.ItemPlayback import com.github.damontecres.wholphin.data.model.chooseSource import com.github.damontecres.wholphin.data.model.chooseStream import com.github.damontecres.wholphin.preferences.UserPreferences -import com.github.damontecres.wholphin.ui.isNotNullOrBlank +import com.github.damontecres.wholphin.util.languageName import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.MediaSegmentType import org.jellyfin.sdk.model.api.MediaStream diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/EpisodeCard.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/EpisodeCard.kt index 364daccb..9eafb1ca 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/EpisodeCard.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/EpisodeCard.kt @@ -31,7 +31,7 @@ import androidx.tv.material3.Text import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.ui.AppColors import com.github.damontecres.wholphin.ui.enableMarquee -import com.github.damontecres.wholphin.util.seasonEpisode +import com.github.damontecres.wholphin.ui.seasonEpisode import kotlinx.coroutines.delay @Composable @@ -105,7 +105,10 @@ fun EpisodeCard( modifier = Modifier .align(Alignment.TopEnd) - .background(AppColors.TransparentBlack50, shape = RoundedCornerShape(8.dp)), + .background( + AppColors.TransparentBlack50, + shape = RoundedCornerShape(8.dp), + ), ) { Text( text = dto?.seasonEpisode ?: "", @@ -116,7 +119,10 @@ fun EpisodeCard( } Column( verticalArrangement = Arrangement.spacedBy(0.dp), - modifier = Modifier.padding(bottom = spaceBelow).fillMaxWidth(), + modifier = + Modifier + .padding(bottom = spaceBelow) + .fillMaxWidth(), ) { Text( text = dto?.seriesName ?: "", diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt index 53a4988a..17ddd578 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt @@ -37,6 +37,8 @@ import com.github.damontecres.wholphin.data.model.GetItemsFilter import com.github.damontecres.wholphin.data.model.GetItemsFilterOverride import com.github.damontecres.wholphin.data.model.LibraryDisplayInfo import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.FavoriteWatchManager +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel @@ -47,12 +49,10 @@ import com.github.damontecres.wholphin.ui.detail.MoreDialogActions import com.github.damontecres.wholphin.ui.detail.PlaylistDialog import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.toServerString import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.ExceptionHandler -import com.github.damontecres.wholphin.util.FavoriteWatchManager import com.github.damontecres.wholphin.util.GetItemsRequestHandler import com.github.damontecres.wholphin.util.GetPersonsHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/GenreCardGrid.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/GenreCardGrid.kt index 2949428d..40e74e8d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/GenreCardGrid.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/GenreCardGrid.kt @@ -16,12 +16,12 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.GetItemsFilter +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.cards.GridCard import com.github.damontecres.wholphin.ui.detail.CardGrid import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.GetGenresRequestHandler diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedContent.kt index f2199333..3a561785 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedContent.kt @@ -18,6 +18,8 @@ import androidx.lifecycle.viewModelScope import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.FavoriteWatchManager +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel import com.github.damontecres.wholphin.ui.data.RowColumn @@ -27,9 +29,7 @@ import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.main.HomePageContent -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.util.ApiRequestPager -import com.github.damontecres.wholphin.util.FavoriteWatchManager import com.github.damontecres.wholphin.util.HomeRowLoadingState import com.github.damontecres.wholphin.util.LoadingState import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt index 7eb676d0..22cd6175 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt @@ -8,13 +8,13 @@ import androidx.lifecycle.viewModelScope import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.FavoriteWatchManager +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.data.RowColumn import com.github.damontecres.wholphin.ui.launchIO -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.ExceptionHandler -import com.github.damontecres.wholphin.util.FavoriteWatchManager import com.github.damontecres.wholphin.util.GetItemsRequestHandler import com.github.damontecres.wholphin.util.GetResumeItemsRequestHandler import com.github.damontecres.wholphin.util.GetSuggestionsRequestHandler diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt index cf5580eb..7144d010 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt @@ -8,13 +8,13 @@ import androidx.lifecycle.viewModelScope import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.FavoriteWatchManager +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.data.RowColumn import com.github.damontecres.wholphin.ui.launchIO -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.ExceptionHandler -import com.github.damontecres.wholphin.util.FavoriteWatchManager import com.github.damontecres.wholphin.util.GetItemsRequestHandler import com.github.damontecres.wholphin.util.GetNextUpRequestHandler import com.github.damontecres.wholphin.util.GetResumeItemsRequestHandler diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/data/AddPlaylistViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/data/AddPlaylistViewModel.kt index 2287804c..555ac5fa 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/data/AddPlaylistViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/data/AddPlaylistViewModel.kt @@ -5,7 +5,7 @@ import android.widget.Toast import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.github.damontecres.wholphin.data.model.PlaylistCreator +import com.github.damontecres.wholphin.services.PlaylistCreator import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.setValueOnMain diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt index 63b904b6..d305bfe1 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt @@ -14,11 +14,11 @@ import androidx.tv.material3.MaterialTheme import androidx.tv.material3.ProvideTextStyle import androidx.tv.material3.Text import com.github.damontecres.wholphin.R +import com.github.damontecres.wholphin.ui.byteRateSuffixes import com.github.damontecres.wholphin.ui.components.ScrollableDialog +import com.github.damontecres.wholphin.ui.formatBytes import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.letNotEmpty -import com.github.damontecres.wholphin.util.byteRateSuffixes -import com.github.damontecres.wholphin.util.formatBytes import com.github.damontecres.wholphin.util.languageName import org.jellyfin.sdk.model.api.AudioSpatialFormat import org.jellyfin.sdk.model.api.MediaSourceInfo diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt index d7e364fa..83974283 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt @@ -31,6 +31,7 @@ import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.GetItemsFilter import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.components.CollectionFolderGrid import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.TabRow @@ -40,7 +41,6 @@ import com.github.damontecres.wholphin.ui.detail.livetv.TvGuideGrid import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.logTab import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.RememberTabManager diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt index d89e71d8..5a33edcb 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt @@ -41,6 +41,8 @@ import coil3.compose.AsyncImage import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.FavoriteWatchManager +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect import com.github.damontecres.wholphin.ui.PreviewTvSpec import com.github.damontecres.wholphin.ui.SlimItemFields @@ -51,21 +53,19 @@ import com.github.damontecres.wholphin.ui.components.LoadingRow import com.github.damontecres.wholphin.ui.components.OverviewText import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo +import com.github.damontecres.wholphin.ui.formatDate import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.rememberPosition import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ApiRequestPager -import com.github.damontecres.wholphin.util.FavoriteWatchManager import com.github.damontecres.wholphin.util.GetItemsRequestHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.RowLoadingState -import com.github.damontecres.wholphin.util.formatDate import dagger.hilt.android.lifecycle.HiltViewModel import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.model.api.BaseItemKind diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt index 5ab05521..9d3b6eb5 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt @@ -56,6 +56,7 @@ import androidx.tv.material3.surfaceColorAtElevation import coil3.compose.AsyncImage import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.DefaultItemFields import com.github.damontecres.wholphin.ui.cards.ItemCardImage import com.github.damontecres.wholphin.ui.components.DialogItem @@ -70,7 +71,6 @@ import com.github.damontecres.wholphin.ui.enableMarquee import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ApiRequestPager diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt index 45578f4b..0b124977 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt @@ -34,17 +34,17 @@ import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.nav.NavigationManager +import com.github.damontecres.wholphin.ui.seasonEpisode import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState -import com.github.damontecres.wholphin.util.seasonEpisode import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt index e8040056..6e4be864 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt @@ -8,12 +8,12 @@ import androidx.lifecycle.viewModelScope import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.WholphinApplication import com.github.damontecres.wholphin.data.model.BaseItem +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.AppColors import com.github.damontecres.wholphin.ui.data.RowColumn import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisode import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.launchIO -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.ui.toServerString import com.github.damontecres.wholphin.util.ExceptionHandler diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/ProgramDialog.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/ProgramDialog.kt index 933b20ef..c8f39df7 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/ProgramDialog.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/ProgramDialog.kt @@ -36,9 +36,9 @@ import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.ui.components.CircularProgress import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.isNotNullOrBlank +import com.github.damontecres.wholphin.ui.seasonEpisode import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.LoadingState -import com.github.damontecres.wholphin.util.seasonEpisode import java.time.LocalDateTime import java.time.ZoneId diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt index 9f1d5f51..c7d329a9 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt @@ -53,6 +53,7 @@ import com.github.damontecres.wholphin.data.model.RemoteTrailer import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.data.model.chooseSource import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.TrailerService import com.github.damontecres.wholphin.ui.Cards import com.github.damontecres.wholphin.ui.cards.ChapterRow import com.github.damontecres.wholphin.ui.cards.ItemRow @@ -81,7 +82,6 @@ import com.github.damontecres.wholphin.ui.rememberInt import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingState -import com.github.damontecres.wholphin.util.TrailerService import kotlinx.coroutines.launch import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.MediaType diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt index 45382fc6..c4d12ff7 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt @@ -32,11 +32,11 @@ import com.github.damontecres.wholphin.ui.components.DotSeparatedRow import com.github.damontecres.wholphin.ui.components.OverviewText import com.github.damontecres.wholphin.ui.components.SimpleStarRating import com.github.damontecres.wholphin.ui.components.TitleValueText +import com.github.damontecres.wholphin.ui.getAudioDisplay +import com.github.damontecres.wholphin.ui.getSubtitleDisplay import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.ui.timeRemaining -import com.github.damontecres.wholphin.util.getAudioDisplay -import com.github.damontecres.wholphin.util.getSubtitleDisplay import org.jellyfin.sdk.model.api.MediaStreamType import org.jellyfin.sdk.model.api.PersonKind import org.jellyfin.sdk.model.extensions.ticks diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt index 30a75ef8..4318f2a7 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt @@ -13,18 +13,18 @@ import com.github.damontecres.wholphin.data.model.ItemPlayback import com.github.damontecres.wholphin.data.model.Person import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.preferences.ThemeSongVolume +import com.github.damontecres.wholphin.services.FavoriteWatchManager +import com.github.damontecres.wholphin.services.NavigationManager +import com.github.damontecres.wholphin.services.PeopleFavorites +import com.github.damontecres.wholphin.services.ThemeSongPlayer +import com.github.damontecres.wholphin.services.TrailerService import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.util.ExceptionHandler -import com.github.damontecres.wholphin.util.FavoriteWatchManager import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState -import com.github.damontecres.wholphin.util.PeopleFavorites -import com.github.damontecres.wholphin.util.ThemeSongPlayer -import com.github.damontecres.wholphin.util.TrailerService import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt index ec4c4b6f..42d8d991 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt @@ -17,9 +17,9 @@ import com.github.damontecres.wholphin.data.model.chooseStream import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.components.ExpandablePlayButtons import com.github.damontecres.wholphin.ui.components.TitleValueText +import com.github.damontecres.wholphin.ui.getAudioDisplay +import com.github.damontecres.wholphin.ui.getSubtitleDisplay import com.github.damontecres.wholphin.ui.isNotNullOrBlank -import com.github.damontecres.wholphin.util.getAudioDisplay -import com.github.damontecres.wholphin.util.getSubtitleDisplay import org.jellyfin.sdk.model.api.MediaStreamType import org.jellyfin.sdk.model.extensions.ticks import kotlin.time.Duration diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt index 77d7e386..664aab2f 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeHeader.kt @@ -16,10 +16,10 @@ import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.ui.components.DotSeparatedRow import com.github.damontecres.wholphin.ui.components.OverviewText import com.github.damontecres.wholphin.ui.components.SimpleStarRating +import com.github.damontecres.wholphin.ui.formatDateTime import com.github.damontecres.wholphin.ui.roundMinutes +import com.github.damontecres.wholphin.ui.seasonEpisode import com.github.damontecres.wholphin.ui.timeRemaining -import com.github.damontecres.wholphin.util.formatDateTime -import com.github.damontecres.wholphin.util.seasonEpisode import org.jellyfin.sdk.model.extensions.ticks @Composable diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt index 91377f91..25806ce1 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt @@ -48,6 +48,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.Person import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.TrailerService import com.github.damontecres.wholphin.ui.Cards import com.github.damontecres.wholphin.ui.cards.ItemRow import com.github.damontecres.wholphin.ui.cards.PersonRow @@ -81,7 +82,6 @@ import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingState -import com.github.damontecres.wholphin.util.TrailerService import kotlinx.coroutines.launch import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.MediaType diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverview.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverview.kt index 7d18dfec..8fc9ff2d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverview.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverview.kt @@ -39,9 +39,9 @@ import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItems import com.github.damontecres.wholphin.ui.equalsNotNull import com.github.damontecres.wholphin.ui.indexOfFirstOrNull import com.github.damontecres.wholphin.ui.nav.Destination +import com.github.damontecres.wholphin.ui.seasonEpisode import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.LoadingState -import com.github.damontecres.wholphin.util.seasonEpisode import kotlinx.serialization.Serializable import kotlinx.serialization.UseSerializers import org.jellyfin.sdk.model.api.ImageType diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt index cc326b24..302da4e3 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt @@ -49,11 +49,11 @@ import com.github.damontecres.wholphin.ui.cards.BannerCard import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.components.TabRow +import com.github.damontecres.wholphin.ui.formatDateTime import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.logTab import com.github.damontecres.wholphin.ui.tryRequestFocus -import com.github.damontecres.wholphin.util.formatDateTime import kotlin.time.Duration @Composable diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt index 8a1a96bf..16335b2d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt @@ -13,24 +13,24 @@ import com.github.damontecres.wholphin.data.model.Person import com.github.damontecres.wholphin.data.model.Trailer import com.github.damontecres.wholphin.preferences.ThemeSongVolume import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.FavoriteWatchManager +import com.github.damontecres.wholphin.services.NavigationManager +import com.github.damontecres.wholphin.services.PeopleFavorites +import com.github.damontecres.wholphin.services.ThemeSongPlayer +import com.github.damontecres.wholphin.services.TrailerService import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.detail.ItemViewModel import com.github.damontecres.wholphin.ui.equalsNotNull import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.ui.showToast import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.ExceptionHandler -import com.github.damontecres.wholphin.util.FavoriteWatchManager import com.github.damontecres.wholphin.util.GetEpisodesRequestHandler import com.github.damontecres.wholphin.util.GetItemsRequestHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState -import com.github.damontecres.wholphin.util.PeopleFavorites -import com.github.damontecres.wholphin.util.ThemeSongPlayer -import com.github.damontecres.wholphin.util.TrailerService import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt index cf1fb36f..72b84490 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt @@ -62,15 +62,15 @@ import com.github.damontecres.wholphin.ui.detail.MoreDialogActions import com.github.damontecres.wholphin.ui.detail.PlaylistDialog import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome +import com.github.damontecres.wholphin.ui.formatDateTime import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.roundMinutes +import com.github.damontecres.wholphin.ui.seasonEpisode import com.github.damontecres.wholphin.ui.timeRemaining import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.HomeRowLoadingState import com.github.damontecres.wholphin.util.LoadingState -import com.github.damontecres.wholphin.util.formatDateTime -import com.github.damontecres.wholphin.util.seasonEpisode import kotlinx.coroutines.delay import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.MediaType diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt index d24062a5..388ec0c6 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt @@ -9,12 +9,12 @@ import com.github.damontecres.wholphin.data.NavDrawerItemRepository import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.FavoriteWatchManager +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.SlimItemFields -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.nav.ServerNavDrawerItem import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.util.ExceptionHandler -import com.github.damontecres.wholphin.util.FavoriteWatchManager import com.github.damontecres.wholphin.util.HomeRowLoadingState import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/SearchPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/SearchPage.kt index 244edbcc..2a57fd34 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/SearchPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/SearchPage.kt @@ -33,6 +33,7 @@ import androidx.tv.material3.Text import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.Cards import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.cards.EpisodeCard @@ -42,7 +43,6 @@ import com.github.damontecres.wholphin.ui.components.SearchEditTextBox import com.github.damontecres.wholphin.ui.data.RowColumn import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.isNotNullOrBlank -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.rememberPosition import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ApiRequestPager diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/ApplicationContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/ApplicationContent.kt index 94cc5e33..ddd881b5 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/ApplicationContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/ApplicationContent.kt @@ -10,6 +10,7 @@ import androidx.navigation3.ui.NavDisplay import com.github.damontecres.wholphin.data.model.JellyfinServer import com.github.damontecres.wholphin.data.model.JellyfinUser import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.components.ErrorMessage import org.jellyfin.sdk.model.api.DeviceProfile diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt index ce69e8ce..e41db910 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt @@ -68,7 +68,9 @@ import com.github.damontecres.wholphin.data.NavDrawerItemRepository import com.github.damontecres.wholphin.data.model.JellyfinServer import com.github.damontecres.wholphin.data.model.JellyfinUser import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.FontAwesome +import com.github.damontecres.wholphin.ui.TimeFormatter import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.preferences.PreferenceScreenOption @@ -76,7 +78,6 @@ import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.ui.spacedByWithFooter import com.github.damontecres.wholphin.ui.toServerString import com.github.damontecres.wholphin.ui.tryRequestFocus -import com.github.damontecres.wholphin.util.TimeFormatter import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/DownloadSubtitlesDialog.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/DownloadSubtitlesDialog.kt index 4842f740..557dbcce 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/DownloadSubtitlesDialog.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/DownloadSubtitlesDialog.kt @@ -39,13 +39,13 @@ import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.ui.AppColors +import com.github.damontecres.wholphin.ui.abbreviateNumber import com.github.damontecres.wholphin.ui.components.DialogItem import com.github.damontecres.wholphin.ui.components.DialogItemDivider import com.github.damontecres.wholphin.ui.components.EditTextBox import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.tryRequestFocus -import com.github.damontecres.wholphin.util.abbreviateNumber import org.jellyfin.sdk.model.api.RemoteSubtitleInfo @Composable diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackControls.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackControls.kt index e131457d..bd106995 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackControls.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackControls.kt @@ -65,9 +65,9 @@ import com.github.damontecres.wholphin.ui.AppColors import com.github.damontecres.wholphin.ui.indexOfFirstOrNull import com.github.damontecres.wholphin.ui.seekBack import com.github.damontecres.wholphin.ui.seekForward +import com.github.damontecres.wholphin.ui.stringRes import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ExceptionHandler -import com.github.damontecres.wholphin.util.stringRes import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt index f07d6683..0181f8e1 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt @@ -51,13 +51,13 @@ import com.github.damontecres.wholphin.data.model.Chapter import com.github.damontecres.wholphin.data.model.ItemPlayback import com.github.damontecres.wholphin.data.model.Playlist import com.github.damontecres.wholphin.ui.AppColors +import com.github.damontecres.wholphin.ui.TimeFormatter import com.github.damontecres.wholphin.ui.cards.ChapterCard import com.github.damontecres.wholphin.ui.cards.SeasonCard import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.tryRequestFocus -import com.github.damontecres.wholphin.util.TimeFormatter import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import org.jellyfin.sdk.model.api.MediaSegmentDto diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt index 7b60b2b1..75d0cfa2 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt @@ -72,11 +72,11 @@ import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.LoadingPage import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.toSubtitleStyle +import com.github.damontecres.wholphin.ui.seasonEpisode +import com.github.damontecres.wholphin.ui.stringRes import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingState -import com.github.damontecres.wholphin.util.seasonEpisode -import com.github.damontecres.wholphin.util.stringRes import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.jellyfin.sdk.model.api.DeviceProfile diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt index 82ea4881..f4b4cb60 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt @@ -25,7 +25,6 @@ import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.Chapter import com.github.damontecres.wholphin.data.model.ItemPlayback import com.github.damontecres.wholphin.data.model.Playlist -import com.github.damontecres.wholphin.data.model.PlaylistCreator import com.github.damontecres.wholphin.data.model.TrackIndex import com.github.damontecres.wholphin.data.model.chooseSource import com.github.damontecres.wholphin.data.model.chooseStream @@ -34,9 +33,13 @@ import com.github.damontecres.wholphin.preferences.AppPreferences import com.github.damontecres.wholphin.preferences.ShowNextUpWhen import com.github.damontecres.wholphin.preferences.SkipSegmentBehavior import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.NavigationManager +import com.github.damontecres.wholphin.services.PlayerFactory +import com.github.damontecres.wholphin.services.PlaylistCreator +import com.github.damontecres.wholphin.ui.formatDateTime import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.nav.NavigationManager +import com.github.damontecres.wholphin.ui.seasonEpisodePadded import com.github.damontecres.wholphin.ui.seekBack import com.github.damontecres.wholphin.ui.seekForward import com.github.damontecres.wholphin.ui.setValueOnMain @@ -46,12 +49,9 @@ import com.github.damontecres.wholphin.util.EqualityMutableLiveData import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState -import com.github.damontecres.wholphin.util.PlayerFactory import com.github.damontecres.wholphin.util.TrackActivityPlaybackListener import com.github.damontecres.wholphin.util.TrackSupport import com.github.damontecres.wholphin.util.checkForSupport -import com.github.damontecres.wholphin.util.formatDateTime -import com.github.damontecres.wholphin.util.seasonEpisodePadded import com.github.damontecres.wholphin.util.subtitleMimeTypes import com.github.damontecres.wholphin.util.supportItemKinds import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesViewModel.kt index 57701e6b..33cd8b56 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesViewModel.kt @@ -14,10 +14,10 @@ import com.github.damontecres.wholphin.data.model.NavPinType import com.github.damontecres.wholphin.preferences.AppPreferences import com.github.damontecres.wholphin.preferences.resetSubtitles import com.github.damontecres.wholphin.preferences.updateSubtitlePreferences +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.detail.DebugViewModel.Companion.sendAppLogs import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.NavDrawerItem -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.RememberTabManager diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/InstallUpdatePage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/InstallUpdatePage.kt index 4d02def5..4c608779 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/InstallUpdatePage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/InstallUpdatePage.kt @@ -50,22 +50,22 @@ import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.preferences.UserPreferences +import com.github.damontecres.wholphin.services.DownloadCallback +import com.github.damontecres.wholphin.services.NavigationManager +import com.github.damontecres.wholphin.services.Release +import com.github.damontecres.wholphin.services.UpdateChecker import com.github.damontecres.wholphin.ui.PreviewTvSpec import com.github.damontecres.wholphin.ui.components.BasicDialog import com.github.damontecres.wholphin.ui.components.ErrorMessage import com.github.damontecres.wholphin.ui.components.LoadingPage +import com.github.damontecres.wholphin.ui.formatBytes import com.github.damontecres.wholphin.ui.ifElse -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.ui.theme.WholphinTheme -import com.github.damontecres.wholphin.util.DownloadCallback import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState -import com.github.damontecres.wholphin.util.Release -import com.github.damontecres.wholphin.util.UpdateChecker import com.github.damontecres.wholphin.util.Version -import com.github.damontecres.wholphin.util.formatBytes import com.mikepenz.markdown.m3.Markdown import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerViewModel.kt index 9e962992..11202a31 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerViewModel.kt @@ -9,9 +9,9 @@ import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.JellyfinServerDao import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.JellyfinServer +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.showToast import com.github.damontecres.wholphin.util.LoadingState import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserViewModel.kt index abd7d4c4..cd1d6daf 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserViewModel.kt @@ -7,8 +7,8 @@ import com.github.damontecres.wholphin.data.JellyfinServerDao import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.JellyfinServer import com.github.damontecres.wholphin.data.model.JellyfinUser +import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.launchIO -import com.github.damontecres.wholphin.ui.nav.NavigationManager import com.github.damontecres.wholphin.ui.setValueOnMain import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingState diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/CrashReportSenderFactory.kt b/app/src/main/java/com/github/damontecres/wholphin/util/CrashReportSenderFactory.kt index 0afdca90..43d3e45a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/CrashReportSenderFactory.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/CrashReportSenderFactory.kt @@ -2,7 +2,7 @@ package com.github.damontecres.wholphin.util import android.content.Context import com.github.damontecres.wholphin.data.ServerRepository -import com.github.damontecres.wholphin.hilt.AppModule +import com.github.damontecres.wholphin.services.hilt.AppModule import com.google.auto.service.AutoService import kotlinx.coroutines.runBlocking import okhttp3.OkHttpClient