Compare commits

..

4 Commits

134 changed files with 272 additions and 252 deletions

View File

@@ -52,8 +52,8 @@ kotlin {
dependencies {
implementation(project(":data"))
implementation(project(":domain"))
implementation(project(":ui-common"))
implementation(project(":core"))
implementation(project(":core-ui"))
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.viewmodel.compose)

View File

@@ -37,7 +37,7 @@ import coil3.request.crossfade
import coil3.util.DebugLogger
import dagger.hilt.android.AndroidEntryPoint
import hu.bbara.purefin.data.SessionBootstrapper
import hu.bbara.purefin.data.session.UserSessionRepository
import hu.bbara.purefin.data.UserSessionRepository
import hu.bbara.purefin.navigation.NavigationCommand
import hu.bbara.purefin.navigation.NavigationManager
import hu.bbara.purefin.navigation.Route

View File

@@ -10,7 +10,6 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester

View File

@@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape

View File

@@ -51,8 +51,10 @@ kotlin {
dependencies {
implementation(project(":data"))
implementation(project(":domain"))
implementation(project(":ui-common"))
implementation(project(":core"))
//TODO: temporary hack fix it
implementation(project(":core-model"))
implementation(project(":core-ui"))
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.viewmodel.compose)

View File

@@ -38,7 +38,7 @@ import coil3.util.DebugLogger
import dagger.hilt.android.AndroidEntryPoint
import hu.bbara.purefin.data.SessionBootstrapper
import hu.bbara.purefin.jellyfin.JellyfinAuthInterceptor
import hu.bbara.purefin.data.session.UserSessionRepository
import hu.bbara.purefin.data.UserSessionRepository
import hu.bbara.purefin.navigation.LocalNavigationBackStack
import hu.bbara.purefin.navigation.LocalNavigationManager
import hu.bbara.purefin.navigation.NavigationCommand

View File

@@ -34,17 +34,16 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import hu.bbara.purefin.ui.common.media.MediaCastRow
import hu.bbara.purefin.ui.common.media.MediaSynopsis
import hu.bbara.purefin.ui.common.media.mediaPlaybackProgress
import hu.bbara.purefin.ui.common.media.mediaPlayButtonText
import hu.bbara.purefin.ui.common.button.GhostIconButton
import hu.bbara.purefin.ui.common.button.MediaActionButton
import hu.bbara.purefin.ui.common.media.MediaPlaybackSettings
import hu.bbara.purefin.ui.common.button.MediaResumeButton
import hu.bbara.purefin.download.DownloadState
import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.player.PlayerActivity
import hu.bbara.purefin.ui.common.button.GhostIconButton
import hu.bbara.purefin.ui.common.button.MediaActionButton
import hu.bbara.purefin.ui.common.button.MediaResumeButton
import hu.bbara.purefin.ui.common.media.MediaPlaybackSettings
import hu.bbara.purefin.ui.common.media.MediaSynopsis
import hu.bbara.purefin.ui.common.media.mediaPlayButtonText
import hu.bbara.purefin.ui.common.media.mediaPlaybackProgress
internal sealed interface EpisodeTopBarShortcut {
val label: String
@@ -182,9 +181,10 @@ internal fun EpisodeDetails(
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(12.dp))
MediaCastRow(
cast = episode.cast
)
//TODO use it
// MediaCastRow(
// cast = episode.cast
// )
}
}
}

View File

@@ -29,17 +29,16 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import hu.bbara.purefin.ui.common.media.MediaCastRow
import hu.bbara.purefin.ui.common.media.MediaSynopsis
import hu.bbara.purefin.ui.common.media.mediaPlaybackProgress
import hu.bbara.purefin.ui.common.media.mediaPlayButtonText
import hu.bbara.purefin.ui.common.button.GhostIconButton
import hu.bbara.purefin.ui.common.button.MediaActionButton
import hu.bbara.purefin.ui.common.media.MediaPlaybackSettings
import hu.bbara.purefin.ui.common.button.MediaResumeButton
import hu.bbara.purefin.download.DownloadState
import hu.bbara.purefin.model.Movie
import hu.bbara.purefin.player.PlayerActivity
import hu.bbara.purefin.ui.common.button.GhostIconButton
import hu.bbara.purefin.ui.common.button.MediaActionButton
import hu.bbara.purefin.ui.common.button.MediaResumeButton
import hu.bbara.purefin.ui.common.media.MediaPlaybackSettings
import hu.bbara.purefin.ui.common.media.MediaSynopsis
import hu.bbara.purefin.ui.common.media.mediaPlayButtonText
import hu.bbara.purefin.ui.common.media.mediaPlaybackProgress
@Composable
internal fun MovieTopBar(
@@ -139,9 +138,10 @@ internal fun MovieDetails(
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(12.dp))
MediaCastRow(
cast = movie.cast,
)
//TODO fix
// MediaCastRow(
// cast = movie.cast,
// )
}
}
}

View File

@@ -1,5 +1,7 @@
package hu.bbara.purefin.ui.screen.series
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -15,32 +17,30 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import hu.bbara.purefin.ui.common.media.MediaSynopsis
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
import hu.bbara.purefin.ui.common.media.MediaHero
import hu.bbara.purefin.download.DownloadState
import hu.bbara.purefin.feature.content.series.SeriesViewModel
import hu.bbara.purefin.image.ArtworkKind
import hu.bbara.purefin.image.ImageUrlBuilder
import hu.bbara.purefin.navigation.SeriesDto
import hu.bbara.purefin.model.CastMember
import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.model.Season
import hu.bbara.purefin.model.Series
import hu.bbara.purefin.feature.content.series.SeriesViewModel
import hu.bbara.purefin.navigation.SeriesDto
import hu.bbara.purefin.ui.common.media.MediaHero
import hu.bbara.purefin.ui.common.media.MediaSynopsis
import hu.bbara.purefin.ui.screen.series.components.CastRow
import hu.bbara.purefin.ui.screen.series.components.EpisodeCarousel
import hu.bbara.purefin.ui.screen.series.components.SeasonTabs
@@ -48,8 +48,8 @@ import hu.bbara.purefin.ui.screen.series.components.SeriesActionButtons
import hu.bbara.purefin.ui.screen.series.components.SeriesDownloadOption
import hu.bbara.purefin.ui.screen.series.components.SeriesMetaChips
import hu.bbara.purefin.ui.screen.series.components.SeriesTopBar
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
import hu.bbara.purefin.ui.theme.AppTheme
import hu.bbara.purefin.image.ArtworkKind
import java.util.UUID
@Composable

View File

@@ -55,28 +55,27 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import hu.bbara.purefin.ui.common.media.MediaCastRow
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
import hu.bbara.purefin.ui.common.media.mediaPlaybackProgress
import hu.bbara.purefin.ui.common.media.mediaPlayButtonText
import hu.bbara.purefin.ui.common.button.GhostIconButton
import hu.bbara.purefin.ui.common.button.MediaActionButton
import hu.bbara.purefin.ui.common.bar.MediaProgressBar
import hu.bbara.purefin.ui.common.button.MediaResumeButton
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
import hu.bbara.purefin.download.DownloadState
import hu.bbara.purefin.feature.content.series.SeriesViewModel
import hu.bbara.purefin.image.ArtworkKind
import hu.bbara.purefin.image.ImageUrlBuilder
import hu.bbara.purefin.navigation.EpisodeDto
import hu.bbara.purefin.navigation.LocalNavigationManager
import hu.bbara.purefin.navigation.Route
import hu.bbara.purefin.model.CastMember
import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.model.Season
import hu.bbara.purefin.model.Series
import hu.bbara.purefin.feature.content.series.SeriesViewModel
import hu.bbara.purefin.navigation.EpisodeDto
import hu.bbara.purefin.navigation.LocalNavigationManager
import hu.bbara.purefin.navigation.Route
import hu.bbara.purefin.player.PlayerActivity
import hu.bbara.purefin.image.ArtworkKind
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
import hu.bbara.purefin.ui.common.bar.MediaProgressBar
import hu.bbara.purefin.ui.common.button.GhostIconButton
import hu.bbara.purefin.ui.common.button.MediaActionButton
import hu.bbara.purefin.ui.common.button.MediaResumeButton
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
import hu.bbara.purefin.ui.common.media.mediaPlayButtonText
import hu.bbara.purefin.ui.common.media.mediaPlaybackProgress
@Composable
internal fun SeriesTopBar(
@@ -407,11 +406,12 @@ private fun EpisodeCard(
@Composable
internal fun CastRow(cast: List<CastMember>, modifier: Modifier = Modifier) {
MediaCastRow(
cast = cast,
modifier = modifier,
cardWidth = 84.dp,
nameSize = 11.sp,
roleSize = 10.sp
)
//TODO fix
// MediaCastRow(
// cast = cast,
// modifier = modifier,
// cardWidth = 84.dp,
// nameSize = 11.sp,
// roleSize = 10.sp
// )
}

1
core-model/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,13 @@
plugins {
id("java-library")
alias(libs.plugins.kotlin.jvm)
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11
}
}

View File

@@ -32,7 +32,7 @@ kotlin {
dependencies {
implementation(project(":data"))
implementation(project(":domain"))
implementation(project(":core"))
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)

View File

@@ -0,0 +1,118 @@
package hu.bbara.purefin.ui.common.media
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun MediaMetaChip(
text: String,
background: Color = MaterialTheme.colorScheme.surfaceVariant,
border: Color = Color.Transparent,
textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.height(28.dp)
.wrapContentHeight(Alignment.CenterVertically)
.clip(RoundedCornerShape(6.dp))
.background(background)
.border(width = 1.dp, color = border, shape = RoundedCornerShape(6.dp))
.padding(horizontal = 12.dp),
contentAlignment = Alignment.Center
) {
Text(
text = text,
color = textColor,
fontSize = 12.sp,
fontWeight = FontWeight.Bold
)
}
}
//TODO use CastMemberUiModel
//@Composable
//fun MediaCastRow(
// cast: List<CastMember>,
// modifier: Modifier = Modifier,
// cardWidth: Dp = 96.dp,
// nameSize: TextUnit = 12.sp,
// roleSize: TextUnit = 10.sp
//) {
// val scheme = MaterialTheme.colorScheme
// val mutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
//
// LazyRow(
// modifier = modifier,
// contentPadding = PaddingValues(horizontal = 4.dp),
// horizontalArrangement = Arrangement.spacedBy(16.dp)
// ) {
// items(
// items = cast,
// key = { member -> "${member.name}:${member.role}:${member.imageUrl.orEmpty()}" }
// ) { member ->
// Column(modifier = Modifier.width(cardWidth)) {
// Box(
// modifier = Modifier
// .aspectRatio(4f / 5f)
// .clip(RoundedCornerShape(12.dp))
// .background(scheme.surfaceVariant)
// ) {
// if (member.imageUrl == null) {
// Box(
// modifier = Modifier
// .fillMaxSize()
// .background(scheme.surfaceVariant.copy(alpha = 0.6f)),
// contentAlignment = Alignment.Center
// ) {
// Icon(
// imageVector = Icons.Outlined.Person,
// contentDescription = null,
// tint = mutedStrong
// )
// }
// } else {
// PurefinAsyncImage(
// model = member.imageUrl,
// contentDescription = null,
// modifier = Modifier.fillMaxSize(),
// contentScale = ContentScale.Crop,
// fallbackIcon = null
// )
// }
// }
// Spacer(modifier = Modifier.height(6.dp))
// Text(
// text = member.name,
// color = scheme.onBackground,
// fontSize = nameSize,
// fontWeight = FontWeight.Bold,
// maxLines = 1,
// overflow = TextOverflow.Ellipsis
// )
// Text(
// text = member.role,
// color = mutedStrong,
// fontSize = roleSize,
// maxLines = 1,
// overflow = TextOverflow.Ellipsis
// )
// }
// }
// }
//}

View File

@@ -29,7 +29,7 @@ kotlin {
}
dependencies {
implementation(project(":data"))
implementation(project(":core-model"))
implementation(libs.hilt)
ksp(libs.hilt.compiler)
implementation(libs.androidx.lifecycle.viewmodel.ktx)

View File

@@ -0,0 +1,11 @@
package hu.bbara.purefin
import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class Online
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class Offline

View File

@@ -1,7 +1,7 @@
package hu.bbara.purefin.data
import hu.bbara.purefin.data.catalog.InMemoryMediaRepository
import hu.bbara.purefin.data.catalog.OfflineMediaRepository
import hu.bbara.purefin.Offline
import hu.bbara.purefin.Online
import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.model.Movie
import hu.bbara.purefin.model.Series
@@ -22,8 +22,8 @@ import javax.inject.Singleton
@OptIn(ExperimentalCoroutinesApi::class)
@Singleton
class CompositeMediaRepository @Inject constructor(
private val offlineRepository: OfflineMediaRepository,
private val onlineRepository: InMemoryMediaRepository,
@Offline private val offlineRepository: MediaRepository,
@Online private val onlineRepository: MediaRepository,
) : MediaCatalogReader, MediaProgressWriter {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

View File

@@ -0,0 +1,4 @@
package hu.bbara.purefin.data
interface MediaRepository : MediaCatalogReader, MediaProgressWriter {
}

View File

@@ -3,8 +3,8 @@ package hu.bbara.purefin.data
import hu.bbara.purefin.model.Episode
import hu.bbara.purefin.model.Movie
import hu.bbara.purefin.model.Series
import java.util.UUID
import kotlinx.coroutines.flow.StateFlow
import java.util.UUID
interface OfflineCatalogReader {
val movies: StateFlow<Map<UUID, Movie>>

View File

@@ -1,7 +1,7 @@
package hu.bbara.purefin.data
import java.util.UUID
import kotlinx.coroutines.flow.Flow
import java.util.UUID
interface SmartDownloadStore {
suspend fun enable(seriesId: UUID)

View File

@@ -1,7 +1,7 @@
package hu.bbara.purefin.data.session
package hu.bbara.purefin.data
import java.util.UUID
import kotlinx.coroutines.flow.Flow
import java.util.UUID
interface UserSessionRepository {
val serverUrl: Flow<String>

View File

@@ -5,7 +5,7 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import hu.bbara.purefin.data.HomeRepository
import hu.bbara.purefin.data.MediaCatalogReader
import hu.bbara.purefin.data.session.UserSessionRepository
import hu.bbara.purefin.data.UserSessionRepository
import hu.bbara.purefin.download.MediaDownloadController
import hu.bbara.purefin.model.LibraryKind
import hu.bbara.purefin.model.Media

View File

@@ -3,7 +3,8 @@ package hu.bbara.purefin.feature.downloads
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import hu.bbara.purefin.data.OfflineCatalogReader
import hu.bbara.purefin.Offline
import hu.bbara.purefin.data.MediaRepository
import hu.bbara.purefin.download.MediaDownloadController
import hu.bbara.purefin.image.ArtworkKind
import hu.bbara.purefin.image.ImageUrlBuilder
@@ -22,7 +23,7 @@ import javax.inject.Inject
@HiltViewModel
class DownloadsViewModel @Inject constructor(
private val offlineCatalogReader: OfflineCatalogReader,
@Offline private val offlineCatalogReader: MediaRepository,
private val navigationManager: NavigationManager,
private val downloadManager: MediaDownloadController,
) : ViewModel() {

View File

@@ -4,7 +4,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import hu.bbara.purefin.data.AuthenticationRepository
import hu.bbara.purefin.data.session.UserSessionRepository
import hu.bbara.purefin.data.UserSessionRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

View File

@@ -4,8 +4,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import hu.bbara.purefin.data.MediaCatalogReader
import hu.bbara.purefin.data.UserSessionRepository
import hu.bbara.purefin.image.ImageUrlBuilder
import hu.bbara.purefin.data.session.UserSessionRepository
import java.util.UUID
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableStateFlow

View File

@@ -1,6 +1,6 @@
package hu.bbara.purefin.jellyfin
import hu.bbara.purefin.data.session.UserSessionRepository
import hu.bbara.purefin.data.UserSessionRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob

View File

@@ -5,19 +5,14 @@ import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import hu.bbara.purefin.data.CompositeMediaRepository
import hu.bbara.purefin.data.HomeRepository
import hu.bbara.purefin.data.MediaCatalogReader
import hu.bbara.purefin.data.MediaProgressWriter
import hu.bbara.purefin.data.catalog.InMemoryAppContentRepository
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
@Binds
abstract fun bindHomeRepository(impl: InMemoryAppContentRepository): HomeRepository
@Binds
abstract fun bindMediaCatalogReader(impl: CompositeMediaRepository): MediaCatalogReader

View File

@@ -1,8 +1,7 @@
package hu.bbara.purefin.navigation
import java.util.UUID
import kotlinx.serialization.Serializable
import hu.bbara.purefin.navigation.UuidSerializer
import java.util.UUID
@Serializable
data class EpisodeDto(

Some files were not shown because too many files have changed in this diff Show More