mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-13 07:12:57 -05:00
add watch provider info
This commit is contained in:
@@ -114,8 +114,9 @@ dependencies {
|
|||||||
implementation "com.localebro:okhttpprofiler:$profiler"
|
implementation "com.localebro:okhttpprofiler:$profiler"
|
||||||
|
|
||||||
// koin
|
// koin
|
||||||
def koin = "3.1.4"
|
def koin = "3.3.0"
|
||||||
implementation "io.insert-koin:koin-android:$koin"
|
implementation "io.insert-koin:koin-android:$koin"
|
||||||
|
implementation "io.insert-koin:koin-androidx-compose:$koin"
|
||||||
|
|
||||||
// coil
|
// coil
|
||||||
def coil = "2.2.2"
|
def coil = "2.2.2"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.kieronquinn.monetcompat.core.MonetCompat
|
|||||||
import com.owenlejeune.tvtime.di.modules.appModule
|
import com.owenlejeune.tvtime.di.modules.appModule
|
||||||
import com.owenlejeune.tvtime.di.modules.networkModule
|
import com.owenlejeune.tvtime.di.modules.networkModule
|
||||||
import com.owenlejeune.tvtime.di.modules.preferencesModule
|
import com.owenlejeune.tvtime.di.modules.preferencesModule
|
||||||
|
import com.owenlejeune.tvtime.di.modules.viewModelModule
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import dev.kdrag0n.monet.factory.ColorSchemeFactory
|
import dev.kdrag0n.monet.factory.ColorSchemeFactory
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
@@ -30,7 +31,8 @@ class TvTimeApplication: Application() {
|
|||||||
modules(
|
modules(
|
||||||
networkModule,
|
networkModule,
|
||||||
preferencesModule,
|
preferencesModule,
|
||||||
appModule
|
appModule,
|
||||||
|
viewModelModule
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,4 +23,6 @@ interface DetailService {
|
|||||||
|
|
||||||
suspend fun getKeywords(id: Int): Response<KeywordsResponse>
|
suspend fun getKeywords(id: Int): Response<KeywordsResponse>
|
||||||
|
|
||||||
|
suspend fun getWatchProviders(id: Int): Response<WatchProviderResponse>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -55,4 +55,7 @@ interface MoviesApi {
|
|||||||
@Query("session_id") sessionId: String
|
@Query("session_id") sessionId: String
|
||||||
): Response<StatusResponse>
|
): Response<StatusResponse>
|
||||||
|
|
||||||
|
@GET("movie/{id}/watch/providers")
|
||||||
|
suspend fun getWatchProviders(@Path("id") id: Int): Response<WatchProviderResponse>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RatingBody
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ReviewResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ReviewResponse
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.StatusResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.StatusResponse
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.VideoResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.VideoResponse
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchProviderResponse
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
@@ -77,4 +78,8 @@ class MoviesService: KoinComponent, DetailService, HomePageService {
|
|||||||
return movieService.getKeywords(id)
|
return movieService.getKeywords(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getWatchProviders(id: Int): Response<WatchProviderResponse> {
|
||||||
|
return movieService.getWatchProviders(id)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -58,4 +58,7 @@ interface TvApi {
|
|||||||
@GET("tv/{id}/season/{season}")
|
@GET("tv/{id}/season/{season}")
|
||||||
suspend fun getSeason(@Path("id") seriesId: Int, @Path("season") seasonNumber: Int): Response<Season>
|
suspend fun getSeason(@Path("id") seriesId: Int, @Path("season") seasonNumber: Int): Response<Season>
|
||||||
|
|
||||||
|
@GET("tv/{id}/watch/providers")
|
||||||
|
suspend fun getWatchProviders(@Path("id") seriesId: Int): Response<WatchProviderResponse>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Season
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.StatusResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.StatusResponse
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TvContentRatings
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TvContentRatings
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.VideoResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.VideoResponse
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchProviderResponse
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
@@ -78,6 +79,10 @@ class TvService: KoinComponent, DetailService, HomePageService {
|
|||||||
return service.getKeywords(id)
|
return service.getKeywords(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getWatchProviders(id: Int): Response<WatchProviderResponse> {
|
||||||
|
return service.getWatchProviders(id)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getSeason(seriesId: Int, seasonId: Int): Response<Season> {
|
suspend fun getSeason(seriesId: Int, seasonId: Int): Response<Season> {
|
||||||
return service.getSeason(seriesId, seasonId)
|
return service.getSeason(seriesId, seasonId)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class DetailedMovie(
|
|||||||
@SerializedName("adult") val isAdult: Boolean,
|
@SerializedName("adult") val isAdult: Boolean,
|
||||||
@SerializedName("budget") val budget: Int,
|
@SerializedName("budget") val budget: Int,
|
||||||
@SerializedName("release_date") val releaseDate: String,
|
@SerializedName("release_date") val releaseDate: String,
|
||||||
@SerializedName("revenue") val revenue: Int,
|
@SerializedName("revenue") val revenue: Long,
|
||||||
@SerializedName("runtime") val runtime: Int?,
|
@SerializedName("runtime") val runtime: Int?,
|
||||||
@SerializedName("imdb_id") val imdbId: String?,
|
@SerializedName("imdb_id") val imdbId: String?,
|
||||||
@SerializedName("belongs_to_collection") val collection: Collection?
|
@SerializedName("belongs_to_collection") val collection: Collection?
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
data class WatchProviderResponse(
|
||||||
|
@SerializedName("id")
|
||||||
|
val id: Int,
|
||||||
|
@SerializedName("results")
|
||||||
|
val results: Map<String, WatchProviders>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class WatchProviders(
|
||||||
|
@SerializedName("link")
|
||||||
|
val link: String,
|
||||||
|
@SerializedName("flatrate")
|
||||||
|
val flaterate: List<WatchProviderDetails>?,
|
||||||
|
@SerializedName("rent")
|
||||||
|
val rent: List<WatchProviderDetails>?,
|
||||||
|
@SerializedName("buy")
|
||||||
|
val buy: List<WatchProviderDetails>?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class WatchProviderDetails(
|
||||||
|
@SerializedName("logo_path")
|
||||||
|
val logoPath: String,
|
||||||
|
@SerializedName("provider_id")
|
||||||
|
val providerId: Int,
|
||||||
|
@SerializedName("provider_name")
|
||||||
|
val providerName: String,
|
||||||
|
@SerializedName("display_priority")
|
||||||
|
val displayPriority: Int
|
||||||
|
)
|
||||||
@@ -18,7 +18,7 @@ class MediaList(
|
|||||||
@SerializedName("average_rating") val averageRating: Float,
|
@SerializedName("average_rating") val averageRating: Float,
|
||||||
@SerializedName("runtime") val runtime: Int,
|
@SerializedName("runtime") val runtime: Int,
|
||||||
@SerializedName("name") val name: String,
|
@SerializedName("name") val name: String,
|
||||||
@SerializedName("revenue") val revenue: Int,
|
@SerializedName("revenue") val revenue: Long,
|
||||||
@SerializedName("sort_by") val sortBy: SortOrder
|
@SerializedName("sort_by") val sortBy: SortOrder
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ import com.owenlejeune.tvtime.api.tmdb.api.v4.AccountV4Service
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.deserializer.ListItemDeserializer
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.deserializer.ListItemDeserializer
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListItem
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListItem
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
|
import com.owenlejeune.tvtime.ui.viewmodel.ConfigurationViewModel
|
||||||
|
import com.owenlejeune.tvtime.ui.viewmodel.SettingsViewModel
|
||||||
import com.owenlejeune.tvtime.utils.ResourceUtils
|
import com.owenlejeune.tvtime.utils.ResourceUtils
|
||||||
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
val networkModule = module {
|
val networkModule = module {
|
||||||
@@ -61,4 +64,9 @@ val preferencesModule = module {
|
|||||||
|
|
||||||
val appModule = module {
|
val appModule = module {
|
||||||
factory { ResourceUtils(get()) }
|
factory { ResourceUtils(get()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val viewModelModule = module {
|
||||||
|
viewModel { ConfigurationViewModel() }
|
||||||
|
viewModel { SettingsViewModel() }
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.owenlejeune.tvtime.extensions
|
||||||
|
|
||||||
|
fun <T> List<T>.lastOr(provider: () -> T): T {
|
||||||
|
return if (isNotEmpty()) {
|
||||||
|
last()
|
||||||
|
} else {
|
||||||
|
provider()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,7 +62,7 @@ fun ContentCard(
|
|||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
modifier = Modifier.padding(start = 16.dp, top = 8.dp),
|
modifier = Modifier.padding(start = 16.dp, top = 12.dp),
|
||||||
color = textColor
|
color = textColor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -119,41 +119,6 @@ fun ExpandableContentCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun LazyListContentCard(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
header: @Composable (() -> Unit)? = null,
|
|
||||||
footer: @Composable (() -> Unit)? = null,
|
|
||||||
backgroundColor: Color = MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
content: LazyListScope.() -> Unit
|
|
||||||
) {
|
|
||||||
Card(
|
|
||||||
modifier = modifier,
|
|
||||||
shape = RoundedCornerShape(10.dp),
|
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp),
|
|
||||||
colors = CardDefaults.cardColors(containerColor = backgroundColor)
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(12.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
header?.invoke()
|
|
||||||
val listState = rememberLazyListState()
|
|
||||||
LazyColumn(
|
|
||||||
content = content,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.weight(1f),
|
|
||||||
state = listState
|
|
||||||
)
|
|
||||||
footer?.invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ListContentCard(
|
fun ListContentCard(
|
||||||
@@ -226,258 +191,4 @@ fun TwoLineImageTextCard(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
|
||||||
@Composable
|
|
||||||
fun SwipeableActionCard(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
leftSwipeCard: (@Composable () -> Unit)? = null,
|
|
||||||
rightSwipeCard: (@Composable () -> Unit)? = null,
|
|
||||||
leftSwiped: () -> Unit = {},
|
|
||||||
rightSwiped: () -> Unit = {},
|
|
||||||
animationSpec: AnimationSpec<Float> = tween(250),
|
|
||||||
velocityThreshold: Dp = 125.dp,
|
|
||||||
// shape: Shape = RectangleShape,
|
|
||||||
mainContent: @Composable () -> Unit
|
|
||||||
) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
val thresholds = { _: SwipeCardState, _: SwipeCardState ->
|
|
||||||
FractionalThreshold(0.6f)
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = modifier//.clip(shape)
|
|
||||||
) {
|
|
||||||
val swipeableState = rememberSwipeableState(
|
|
||||||
initialValue = SwipeCardState.DEFAULT,
|
|
||||||
animationSpec = animationSpec
|
|
||||||
)
|
|
||||||
|
|
||||||
val swipeLeftCardVisible = remember { mutableStateOf(false) }
|
|
||||||
val swipeEnabled = remember { mutableStateOf(true) }
|
|
||||||
val maxWidthInPx = with(LocalDensity.current) {
|
|
||||||
LocalConfiguration.current.screenWidthDp.dp.toPx()
|
|
||||||
}
|
|
||||||
|
|
||||||
val anchors = hashMapOf(0f to SwipeCardState.DEFAULT)
|
|
||||||
leftSwipeCard?.let { anchors[-maxWidthInPx] = SwipeCardState.LEFT }
|
|
||||||
rightSwipeCard?.let { anchors[maxWidthInPx] = SwipeCardState.RIGHT }
|
|
||||||
|
|
||||||
Surface(
|
|
||||||
color = Color.Transparent,
|
|
||||||
content = if (swipeLeftCardVisible.value) {
|
|
||||||
leftSwipeCard
|
|
||||||
} else {
|
|
||||||
rightSwipeCard
|
|
||||||
} ?: {}
|
|
||||||
)
|
|
||||||
|
|
||||||
Surface(
|
|
||||||
color = Color.Transparent,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.offset {
|
|
||||||
var offset = swipeableState.offset.value.roundToInt()
|
|
||||||
if (offset < 0 && leftSwipeCard == null) offset = 0
|
|
||||||
if (offset > 0 && rightSwipeCard == null) offset = 0
|
|
||||||
IntOffset(offset, 0)
|
|
||||||
}
|
|
||||||
.swipeable(
|
|
||||||
state = swipeableState,
|
|
||||||
anchors = anchors,
|
|
||||||
orientation = Orientation.Horizontal,
|
|
||||||
enabled = swipeEnabled.value,
|
|
||||||
thresholds = thresholds,
|
|
||||||
velocityThreshold = velocityThreshold
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
val resetStateToDefault = {
|
|
||||||
coroutineScope.launch {
|
|
||||||
swipeEnabled.value = false
|
|
||||||
swipeableState.animateTo(SwipeCardState.DEFAULT)
|
|
||||||
swipeEnabled.value = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
when {
|
|
||||||
swipeableState.currentValue == SwipeCardState.LEFT && !swipeableState.isAnimationRunning -> {
|
|
||||||
leftSwiped()
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
resetStateToDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
swipeableState.currentValue == SwipeCardState.RIGHT && !swipeableState.isAnimationRunning -> {
|
|
||||||
rightSwiped()
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
resetStateToDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
swipeLeftCardVisible.value = swipeableState.offset.value <= 0
|
|
||||||
|
|
||||||
mainContent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun DraggableCard(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
cardOffset: Float,
|
|
||||||
isRevealed: Boolean,
|
|
||||||
cardElevation: CardElevation = CardDefaults.cardElevation(defaultElevation = 10.dp),
|
|
||||||
backgroundColor: Color = MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
shape: Shape = RectangleShape,
|
|
||||||
onExpand: () -> Unit = {},
|
|
||||||
onCollapse: () -> Unit = {},
|
|
||||||
backgroundContent: @Composable () -> Unit,
|
|
||||||
content: @Composable ColumnScope.() -> Unit
|
|
||||||
) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
val animationDuration = 500
|
|
||||||
val velicityThreshold = 125.dp
|
|
||||||
|
|
||||||
val thresholds: (from: SwipeCardState, to: SwipeCardState) -> ThresholdConfig = { _, _ ->
|
|
||||||
FractionalThreshold(0.6f)
|
|
||||||
}
|
|
||||||
|
|
||||||
val swipeableState = rememberSwipeableState(
|
|
||||||
initialValue = SwipeCardState.DEFAULT,
|
|
||||||
animationSpec = tween(animationDuration)
|
|
||||||
)
|
|
||||||
|
|
||||||
val maxWidthPx = with(LocalDensity.current) {
|
|
||||||
LocalConfiguration.current.screenWidthDp.dp.toPx()
|
|
||||||
}
|
|
||||||
|
|
||||||
val anchors = hashMapOf(0f to SwipeCardState.DEFAULT)
|
|
||||||
anchors[-maxWidthPx] = SwipeCardState.LEFT
|
|
||||||
|
|
||||||
val swipeEnabled = remember { mutableStateOf(true) }
|
|
||||||
|
|
||||||
Box (
|
|
||||||
modifier = Modifier.clip(shape = shape)
|
|
||||||
) {
|
|
||||||
backgroundContent()
|
|
||||||
|
|
||||||
Card(
|
|
||||||
shape = shape,
|
|
||||||
elevation = cardElevation,
|
|
||||||
modifier = modifier
|
|
||||||
.background(color = backgroundColor)
|
|
||||||
.offset {
|
|
||||||
var offset = swipeableState.offset.value.roundToInt()
|
|
||||||
if (offset < 0) offset = 0
|
|
||||||
IntOffset(offset, 0)
|
|
||||||
}
|
|
||||||
.swipeable(
|
|
||||||
state = swipeableState,
|
|
||||||
anchors = anchors,
|
|
||||||
orientation = Orientation.Horizontal,
|
|
||||||
enabled = swipeEnabled.value,
|
|
||||||
thresholds = thresholds,
|
|
||||||
velocityThreshold = velicityThreshold
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if (swipeableState.currentValue == SwipeCardState.LEFT && !swipeableState.isAnimationRunning) {
|
|
||||||
onExpand()
|
|
||||||
LaunchedEffect(key1 = Unit) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
swipeEnabled.value = false
|
|
||||||
swipeableState.animateTo(SwipeCardState.DEFAULT)
|
|
||||||
swipeEnabled.value = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
content()
|
|
||||||
}
|
|
||||||
// DraggableCardInternal(
|
|
||||||
// modifier = modifier,
|
|
||||||
// cardOffset = cardOffset,
|
|
||||||
// isRevealed = isRevealed,
|
|
||||||
// cardElevation = cardElevation,
|
|
||||||
// backgroundColor = backgroundColor,
|
|
||||||
// shape = shape,
|
|
||||||
// onExpand = onExpand,
|
|
||||||
// onCollapse = onCollapse,
|
|
||||||
// content = content
|
|
||||||
// )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum class SwipeCardState {
|
|
||||||
DEFAULT,
|
|
||||||
LEFT,
|
|
||||||
RIGHT
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
|
||||||
@SuppressLint("UnusedTransitionTargetStateParameter")
|
|
||||||
@Composable
|
|
||||||
fun DraggableCardInternal(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
cardOffset: Float,
|
|
||||||
isRevealed: Boolean,
|
|
||||||
cardElevation: CardElevation = CardDefaults.cardElevation(defaultElevation = 10.dp),
|
|
||||||
backgroundColor: Color = MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
shape: Shape = RectangleShape,
|
|
||||||
onExpand: () -> Unit = {},
|
|
||||||
onCollapse: () -> Unit = {},
|
|
||||||
content: @Composable ColumnScope.() -> Unit
|
|
||||||
) {
|
|
||||||
val animationDuration = 500
|
|
||||||
|
|
||||||
val swipeableState = rememberSwipeableState(
|
|
||||||
initialValue = SwipeCardState.DEFAULT,
|
|
||||||
animationSpec = tween(animationDuration)
|
|
||||||
)
|
|
||||||
|
|
||||||
val maxWidthPx = with(LocalDensity.current) {
|
|
||||||
LocalConfiguration.current.screenWidthDp.dp.toPx()
|
|
||||||
}
|
|
||||||
|
|
||||||
val anchors = hashMapOf(0f to SwipeCardState.DEFAULT)
|
|
||||||
anchors[-maxWidthPx] = SwipeCardState.LEFT
|
|
||||||
|
|
||||||
Card(
|
|
||||||
modifier = modifier
|
|
||||||
.background(color = backgroundColor),
|
|
||||||
shape = shape,
|
|
||||||
elevation = cardElevation
|
|
||||||
){}
|
|
||||||
// val animationDuration = 500
|
|
||||||
// val minDragAmount = 5
|
|
||||||
//
|
|
||||||
// val transitionState = remember {
|
|
||||||
// MutableTransitionState(isRevealed).apply {
|
|
||||||
// targetState = !isRevealed
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// val transition = updateTransition(targetState = transitionState, "cardTransition")
|
|
||||||
// val offsetTransition by transition.animateFloat(
|
|
||||||
// label = "cardOffsetTransition",
|
|
||||||
// transitionSpec = { tween(durationMillis = animationDuration) },
|
|
||||||
// targetValueByState = { if (isRevealed) cardOffset else 0f },
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// Card(
|
|
||||||
// modifier = modifier
|
|
||||||
// .background(color = backgroundColor)
|
|
||||||
// .offset { IntOffset(offsetTransition.roundToInt(), 0) }
|
|
||||||
// .pointerInput(Unit) {
|
|
||||||
// detectHorizontalDragGestures { _, dragAmount ->
|
|
||||||
// when {
|
|
||||||
// dragAmount >= minDragAmount -> onExpand()
|
|
||||||
// dragAmount < -minDragAmount -> onCollapse()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// shape = shape,
|
|
||||||
// content = content
|
|
||||||
// )
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -25,9 +25,11 @@ import com.google.accompanist.pager.PagerState
|
|||||||
import com.google.accompanist.pager.rememberPagerState
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection
|
||||||
|
import com.owenlejeune.tvtime.ui.viewmodel.ConfigurationViewModel
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -7,13 +7,16 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import com.google.accompanist.pager.rememberPagerState
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection
|
||||||
|
import com.owenlejeune.tvtime.ui.viewmodel.ConfigurationViewModel
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -2,14 +2,12 @@ package com.owenlejeune.tvtime.ui.components
|
|||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.focusable
|
import androidx.compose.foundation.focusable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.foundation.magnifier
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.BrokenImage
|
import androidx.compose.material.icons.filled.BrokenImage
|
||||||
@@ -28,10 +26,8 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.IntSize
|
import androidx.compose.ui.unit.IntSize
|
||||||
@@ -39,8 +35,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.paging.LoadState
|
import androidx.paging.LoadState
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.compose.rememberAsyncImagePainter
|
|
||||||
import com.owenlejeune.tvtime.R
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePagePerson
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePagePerson
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Person
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Person
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
|
||||||
@@ -48,35 +42,14 @@ import com.owenlejeune.tvtime.extensions.header
|
|||||||
import com.owenlejeune.tvtime.extensions.lazyPagingItems
|
import com.owenlejeune.tvtime.extensions.lazyPagingItems
|
||||||
import com.owenlejeune.tvtime.extensions.listItems
|
import com.owenlejeune.tvtime.extensions.listItems
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
|
import com.owenlejeune.tvtime.ui.viewmodel.ConfigurationViewModel
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import org.koin.java.KoinJavaComponent.get
|
import org.koin.java.KoinJavaComponent.get
|
||||||
|
|
||||||
private val POSTER_WIDTH = 120.dp
|
private val POSTER_WIDTH = 120.dp
|
||||||
private val POSTER_HEIGHT = 180.dp
|
private val POSTER_HEIGHT = 180.dp
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun PosterGrid(
|
|
||||||
fetchMedia: (MutableState<List<TmdbItem>>) -> Unit = {},
|
|
||||||
onClick: (Int) -> Unit = {}
|
|
||||||
) {
|
|
||||||
val mediaList = remember { mutableStateOf(emptyList<TmdbItem>()) }
|
|
||||||
fetchMedia(mediaList)
|
|
||||||
|
|
||||||
LazyVerticalGrid(
|
|
||||||
columns = GridCells.Adaptive(minSize = POSTER_WIDTH),
|
|
||||||
contentPadding = PaddingValues(8.dp),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
|
||||||
listItems(mediaList.value) { item ->
|
|
||||||
PosterItem(
|
|
||||||
modifier = Modifier.padding(5.dp),
|
|
||||||
mediaItem = item,
|
|
||||||
onClick = onClick
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PagingPosterGrid(
|
fun PagingPosterGrid(
|
||||||
lazyPagingItems: LazyPagingItems<TmdbItem>?,
|
lazyPagingItems: LazyPagingItems<TmdbItem>?,
|
||||||
@@ -148,34 +121,6 @@ fun PagingPeoplePosterGrid(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
|
||||||
@Composable
|
|
||||||
fun PeoplePosterGrid(
|
|
||||||
fetchPeople: (MutableState<List<HomePagePerson>>) -> Unit = {},
|
|
||||||
onClick: (Int) -> Unit = {}
|
|
||||||
) {
|
|
||||||
val peopleList = remember { mutableStateOf(emptyList<HomePagePerson>()) }
|
|
||||||
fetchPeople(peopleList)
|
|
||||||
|
|
||||||
LazyVerticalGrid(
|
|
||||||
columns = GridCells.Adaptive(minSize = POSTER_WIDTH),
|
|
||||||
contentPadding = PaddingValues(8.dp),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
|
||||||
listItems(peopleList.value) { person ->
|
|
||||||
PosterItem(
|
|
||||||
url = TmdbUtils.getFullPersonImagePath(person.profilePath),
|
|
||||||
placeholder = Icons.Filled.Person,
|
|
||||||
modifier = Modifier.padding(5.dp),
|
|
||||||
onClick = {
|
|
||||||
onClick(person.id)
|
|
||||||
},
|
|
||||||
title = person.name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PosterItem(
|
fun PosterItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@@ -198,27 +143,6 @@ fun PosterItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun PosterItem(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
width: Dp = POSTER_WIDTH,
|
|
||||||
onClick: (id: Int) -> Unit = {},
|
|
||||||
person: Person?
|
|
||||||
) {
|
|
||||||
PosterItem(
|
|
||||||
modifier = modifier,
|
|
||||||
width = width,
|
|
||||||
onClick = {
|
|
||||||
person?.let {
|
|
||||||
onClick(person.id)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
url = person?.let { TmdbUtils.getFullPersonImagePath(person) },
|
|
||||||
elevation = 8.dp,
|
|
||||||
title = person?.name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PosterItem(
|
fun PosterItem(
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ fun ScrollableTabs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SmallTabIndicator(
|
fun SmallTabIndicator(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
color: Color = MaterialTheme.colorScheme.primary
|
color: Color = MaterialTheme.colorScheme.primary
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import androidx.compose.ui.graphics.painter.Painter
|
|||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
@@ -66,6 +67,7 @@ import coil.compose.rememberAsyncImagePainter
|
|||||||
import com.google.accompanist.flowlayout.FlowRow
|
import com.google.accompanist.flowlayout.FlowRow
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.AuthorDetails
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.AuthorDetails
|
||||||
|
import com.owenlejeune.tvtime.extensions.toDp
|
||||||
import com.owenlejeune.tvtime.extensions.unlessEmpty
|
import com.owenlejeune.tvtime.extensions.unlessEmpty
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
@@ -1099,4 +1101,69 @@ fun SearchBar(
|
|||||||
@Composable
|
@Composable
|
||||||
fun MyDivider(modifier: Modifier = Modifier) {
|
fun MyDivider(modifier: Modifier = Modifier) {
|
||||||
Divider(thickness = 0.5.dp, modifier = modifier, color = MaterialTheme.colorScheme.secondaryContainer)
|
Divider(thickness = 0.5.dp, modifier = modifier, color = MaterialTheme.colorScheme.secondaryContainer)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SelectableTextItem(
|
||||||
|
selected: Boolean,
|
||||||
|
onSelected: () -> Unit,
|
||||||
|
text: String,
|
||||||
|
selectedColor: Color = MaterialTheme.colorScheme.secondary,
|
||||||
|
unselectedColor: Color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(10.dp))
|
||||||
|
.clickable(onClick = onSelected)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.padding(8.dp)
|
||||||
|
) {
|
||||||
|
val size = remember { mutableStateOf(IntSize.Zero) }
|
||||||
|
val color = if (selected) selectedColor else unselectedColor
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
fontWeight = if (selected) FontWeight.Bold else FontWeight.Normal,
|
||||||
|
color = color,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 4.dp)
|
||||||
|
.onGloballyPositioned { size.value = it.size }
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(height = if (selected) 3.dp else 1.dp)
|
||||||
|
.width(width = size.value.width.toDp().plus(8.dp))
|
||||||
|
.clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp))
|
||||||
|
.background(color = color)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SelectableTextChip(
|
||||||
|
selected: Boolean,
|
||||||
|
onSelected: () -> Unit,
|
||||||
|
text: String,
|
||||||
|
selectedColor: Color = MaterialTheme.colorScheme.secondary,
|
||||||
|
unselectedColor: Color = MaterialTheme.colorScheme.surfaceVariant
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(percent = 25))
|
||||||
|
.border(width = 1.dp, color = selectedColor, shape = RoundedCornerShape(percent = 25))
|
||||||
|
.background(color = if(selected) selectedColor else unselectedColor)
|
||||||
|
.clickable(onClick = onSelected)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
color = if (selected) unselectedColor else selectedColor,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.Center)
|
||||||
|
.padding(12.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -30,18 +30,18 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
|||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AccountList
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AccountList
|
||||||
import com.owenlejeune.tvtime.ui.viewmodel.RecommendedMediaViewModel
|
|
||||||
import com.owenlejeune.tvtime.extensions.unlessEmpty
|
import com.owenlejeune.tvtime.extensions.unlessEmpty
|
||||||
import com.owenlejeune.tvtime.ui.components.AccountIcon
|
import com.owenlejeune.tvtime.ui.components.AccountIcon
|
||||||
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem
|
|
||||||
import com.owenlejeune.tvtime.ui.navigation.ListFetchFun
|
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
|
||||||
import com.owenlejeune.tvtime.ui.components.MediaResultCard
|
import com.owenlejeune.tvtime.ui.components.MediaResultCard
|
||||||
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
||||||
import com.owenlejeune.tvtime.ui.components.ScrollableTabs
|
import com.owenlejeune.tvtime.ui.components.ScrollableTabs
|
||||||
|
import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem
|
||||||
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
|
import com.owenlejeune.tvtime.ui.navigation.ListFetchFun
|
||||||
|
import com.owenlejeune.tvtime.ui.viewmodel.RecommendedMediaViewModel
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
|||||||
@@ -43,14 +43,14 @@ import com.owenlejeune.tvtime.api.tmdb.api.v4.model.*
|
|||||||
import com.owenlejeune.tvtime.extensions.WindowSizeClass
|
import com.owenlejeune.tvtime.extensions.WindowSizeClass
|
||||||
import com.owenlejeune.tvtime.extensions.unlessEmpty
|
import com.owenlejeune.tvtime.extensions.unlessEmpty
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
|
import com.owenlejeune.tvtime.ui.components.RatingView
|
||||||
import com.owenlejeune.tvtime.ui.components.Spinner
|
import com.owenlejeune.tvtime.ui.components.Spinner
|
||||||
import com.owenlejeune.tvtime.ui.components.SwitchPreference
|
import com.owenlejeune.tvtime.ui.components.SwitchPreference
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
|
||||||
import com.owenlejeune.tvtime.ui.components.RatingView
|
|
||||||
import com.owenlejeune.tvtime.ui.theme.*
|
import com.owenlejeune.tvtime.ui.theme.*
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import de.charlex.compose.RevealDirection
|
import de.charlex.compose.RevealDirection
|
||||||
import de.charlex.compose.RevealSwipe
|
import de.charlex.compose.RevealSwipe
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.animation.core.LinearEasing
|
import androidx.compose.animation.core.LinearEasing
|
||||||
@@ -8,6 +10,7 @@ import androidx.compose.animation.core.tween
|
|||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@@ -16,6 +19,7 @@ import androidx.compose.material.icons.filled.Movie
|
|||||||
import androidx.compose.material.icons.filled.Send
|
import androidx.compose.material.icons.filled.Send
|
||||||
import androidx.compose.material.icons.outlined.ExpandMore
|
import androidx.compose.material.icons.outlined.ExpandMore
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.material.TabRow
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -30,15 +34,19 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.intl.Locale
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.compose.ui.util.fastForEachIndexed
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
|
import com.google.accompanist.flowlayout.FlowRow
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import com.google.accompanist.pager.HorizontalPager
|
import com.google.accompanist.pager.HorizontalPager
|
||||||
import com.google.accompanist.pager.PagerState
|
import com.google.accompanist.pager.PagerState
|
||||||
|
import com.google.accompanist.pager.pagerTabIndicatorOffset
|
||||||
import com.google.accompanist.pager.rememberPagerState
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
@@ -60,10 +68,13 @@ import com.owenlejeune.tvtime.ui.theme.FavoriteSelected
|
|||||||
import com.owenlejeune.tvtime.ui.theme.RatingSelected
|
import com.owenlejeune.tvtime.ui.theme.RatingSelected
|
||||||
import com.owenlejeune.tvtime.ui.theme.WatchlistSelected
|
import com.owenlejeune.tvtime.ui.theme.WatchlistSelected
|
||||||
import com.owenlejeune.tvtime.ui.theme.actionButtonColor
|
import com.owenlejeune.tvtime.ui.theme.actionButtonColor
|
||||||
|
import com.owenlejeune.tvtime.ui.viewmodel.ConfigurationViewModel
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import okhttp3.internal.notify
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import org.koin.java.KoinJavaComponent.get
|
import org.koin.java.KoinJavaComponent.get
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
|
|
||||||
@@ -200,7 +211,6 @@ private fun MediaViewContent(
|
|||||||
)
|
)
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
// modifier = Modifier.padding(horizontal = 16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
if (type == MediaViewType.MOVIE) {
|
if (type == MediaViewType.MOVIE) {
|
||||||
@@ -442,6 +452,8 @@ private fun MainContent(
|
|||||||
VideosCard(itemId = itemId, service = service, modifier = Modifier.fillMaxWidth())
|
VideosCard(itemId = itemId, service = service, modifier = Modifier.fillMaxWidth())
|
||||||
|
|
||||||
AdditionalDetailsCard(itemId = itemId, mediaItem = mediaItem, service = service, type = type)
|
AdditionalDetailsCard(itemId = itemId, mediaItem = mediaItem, service = service, type = type)
|
||||||
|
|
||||||
|
WatchProvidersCard(itemId = itemId, service = service)
|
||||||
|
|
||||||
if (windowSize != WindowSizeClass.Expanded) {
|
if (windowSize != WindowSizeClass.Expanded) {
|
||||||
ReviewsCard(itemId = itemId, service = service)
|
ReviewsCard(itemId = itemId, service = service)
|
||||||
@@ -966,7 +978,7 @@ private fun AdditionalDetailsCard(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.wrapContentHeight()
|
.wrapContentHeight()
|
||||||
.padding(vertical = 12.dp, horizontal = 16.dp),
|
.padding(vertical = 12.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
AdditionalDetailItem(
|
AdditionalDetailItem(
|
||||||
@@ -1072,12 +1084,14 @@ private fun AdditionalDetailItem(
|
|||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = subtext,
|
text = subtext,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp)
|
||||||
)
|
)
|
||||||
if (includeDivider) {
|
if (includeDivider) {
|
||||||
Divider()
|
Divider()
|
||||||
@@ -1218,7 +1232,7 @@ fun VideosCard(
|
|||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.videos_label),
|
text = stringResource(id = R.string.videos_label),
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
modifier = Modifier.padding(start = 8.dp, top = 8.dp),
|
modifier = Modifier.padding(start = 12.dp, top = 8.dp),
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -1250,7 +1264,7 @@ private fun VideoGroup(results: List<Video>, type: Video.Type, title: String) {
|
|||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = MaterialTheme.colorScheme.primary,
|
||||||
modifier = Modifier.padding(start = 8.dp, top = 8.dp)
|
modifier = Modifier.padding(start = 12.dp, top = 8.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
val posterWidth = 120.dp
|
val posterWidth = 120.dp
|
||||||
@@ -1277,6 +1291,136 @@ private fun VideoGroup(results: List<Video>, type: Video.Type, title: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun WatchProvidersCard(
|
||||||
|
itemId: Int?,
|
||||||
|
service: DetailService,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val watchProviders = remember { mutableStateOf<WatchProviders?>(null) }
|
||||||
|
itemId?.let {
|
||||||
|
if (watchProviders.value == null) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val response = service.getWatchProviders(it)
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val results = response.body()?.results
|
||||||
|
results?.get(Locale.current.region)?.let { watchProviders.value = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watchProviders.value?.let { providers ->
|
||||||
|
if (providers.buy?.isNotEmpty() == true || providers.rent?.isNotEmpty() == true || providers.flaterate?.isNotEmpty() == true) {
|
||||||
|
Card(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight(),
|
||||||
|
shape = RoundedCornerShape(10.dp),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp),
|
||||||
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.watch_providers_title),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
modifier = Modifier.padding(start = 16.dp, top = 12.dp),
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
|
||||||
|
var selectedIndex by remember { mutableStateOf(
|
||||||
|
when {
|
||||||
|
providers.flaterate != null -> 0
|
||||||
|
providers.rent != null -> 1
|
||||||
|
providers.buy != null -> 2
|
||||||
|
else -> -1
|
||||||
|
}
|
||||||
|
) }
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.padding(start = 16.dp, top = 12.dp, bottom = 12.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
providers.flaterate?.let {
|
||||||
|
SelectableTextChip(
|
||||||
|
selected = selectedIndex == 0,
|
||||||
|
onSelected = { selectedIndex = 0 },
|
||||||
|
text = stringResource(R.string.streaming_label)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
providers.rent?.let {
|
||||||
|
SelectableTextChip(
|
||||||
|
selected = selectedIndex == 1,
|
||||||
|
onSelected = { selectedIndex = 1 },
|
||||||
|
text = stringResource(R.string.rent_label)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
providers.buy?.let {
|
||||||
|
SelectableTextChip(
|
||||||
|
selected = selectedIndex == 2,
|
||||||
|
onSelected = { selectedIndex = 2 },
|
||||||
|
text = stringResource(R.string.buy_label)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Crossfade(
|
||||||
|
modifier = modifier.padding(top = 4.dp, bottom = 12.dp),
|
||||||
|
targetState = selectedIndex
|
||||||
|
) { index ->
|
||||||
|
when (index) {
|
||||||
|
0 -> WatchProviderContainer(watchProviders = providers.flaterate!!, link = providers.link)
|
||||||
|
1 -> WatchProviderContainer(watchProviders = providers.rent!!, link = providers.link)
|
||||||
|
2 -> WatchProviderContainer(watchProviders = providers.buy!!, link = providers.link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WatchProviderContainer(
|
||||||
|
watchProviders: List<WatchProviderDetails>,
|
||||||
|
link: String
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
FlowRow(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
mainAxisSpacing = 8.dp,
|
||||||
|
crossAxisSpacing = 4.dp
|
||||||
|
) {
|
||||||
|
watchProviders
|
||||||
|
.sortedBy { it.displayPriority }
|
||||||
|
.forEach { item ->
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(10.dp))
|
||||||
|
.clickable {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
AsyncImage(
|
||||||
|
model = TmdbUtils.fullImagePath(item.logoPath),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.clip(RoundedCornerShape(10.dp))
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = item.providerName,
|
||||||
|
fontSize = 10.sp,
|
||||||
|
modifier = Modifier.width(48.dp),
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ReviewsCard(
|
private fun ReviewsCard(
|
||||||
itemId: Int?,
|
itemId: Int?,
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.PeopleService
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailPerson
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailPerson
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonCreditsResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonCreditsResponse
|
||||||
import com.owenlejeune.tvtime.ui.components.ContentCard
|
import com.owenlejeune.tvtime.ui.components.ContentCard
|
||||||
|
import com.owenlejeune.tvtime.ui.components.DetailHeader
|
||||||
import com.owenlejeune.tvtime.ui.components.ExpandableContentCard
|
import com.owenlejeune.tvtime.ui.components.ExpandableContentCard
|
||||||
import com.owenlejeune.tvtime.ui.components.TwoLineImageTextCard
|
import com.owenlejeune.tvtime.ui.components.TwoLineImageTextCard
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
import com.owenlejeune.tvtime.ui.components.DetailHeader
|
|
||||||
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -105,7 +105,9 @@ fun PersonDetailScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentCard(title = stringResource(R.string.known_for_label)) {
|
ContentCard(
|
||||||
|
title = stringResource(R.string.known_for_label)
|
||||||
|
) {
|
||||||
LazyRow(
|
LazyRow(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.TvService
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
||||||
import com.owenlejeune.tvtime.extensions.listItems
|
import com.owenlejeune.tvtime.extensions.listItems
|
||||||
import com.owenlejeune.tvtime.ui.components.MediaResultCard
|
import com.owenlejeune.tvtime.ui.components.MediaResultCard
|
||||||
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import com.owenlejeune.tvtime.ui.views.ItemMoveCallback
|
|||||||
import com.owenlejeune.tvtime.utils.ResourceUtils
|
import com.owenlejeune.tvtime.utils.ResourceUtils
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import org.koin.java.KoinJavaComponent.get
|
import org.koin.java.KoinJavaComponent.get
|
||||||
@@ -224,7 +225,7 @@ private fun TopLevelSettingsCard(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SearchPreferences() {
|
private fun SearchPreferences() {
|
||||||
val settingsViewModel = viewModel<SettingsViewModel>()
|
val settingsViewModel = koinViewModel<SettingsViewModel>()
|
||||||
|
|
||||||
SwitchPreference(
|
SwitchPreference(
|
||||||
titleText = stringResource(R.string.preferences_persistent_search_title),
|
titleText = stringResource(R.string.preferences_persistent_search_title),
|
||||||
|
|||||||
@@ -1,20 +1,38 @@
|
|||||||
package com.owenlejeune.tvtime.utils
|
package com.owenlejeune.tvtime.utils
|
||||||
|
|
||||||
import androidx.compose.ui.text.intl.Locale
|
import androidx.compose.ui.text.intl.Locale
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.AccountDetails
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.AuthorDetails
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailedItem
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailedMovie
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.DetailedTv
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Episode
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Image
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.MovieReleaseResults
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Person
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Status
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TvContentRatings
|
||||||
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Video
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
object TmdbUtils {
|
object TmdbUtils {
|
||||||
|
|
||||||
private const val POSTER_BASE = "https://image.tmdb.org/t/p/original"
|
private const val POSTER_BASE = "https://image.tmdb.org/t/p/original"
|
||||||
private const val BACKDROP_BASE = "https://www.themoviedb.org/t/p/original"
|
// private const val BACKDROP_BASE = "https://www.themoviedb.org/t/p/original"
|
||||||
private const val PERSON_BASE = "https://www.themoviedb.org/t/p/w600_and_h900_bestv2"
|
// private const val PERSON_BASE = "https://www.themoviedb.org/t/p/w600_and_h900_bestv2"
|
||||||
private const val GRAVATAR_BASE = "https://www.gravatar.com/avatar/"
|
private const val GRAVATAR_BASE = "https://www.gravatar.com/avatar/"
|
||||||
private const val AVATAR_BASE = "https://www.themoviedb.org/t/p/w150_and_h150_face"
|
// private const val AVATAR_BASE = "https://www.themoviedb.org/t/p/w150_and_h150_face"
|
||||||
private const val STILL_BASE = "https://www.themoviedb.org/t/p/w454_and_h254_bestv2/"
|
// private const val STILL_BASE = "https://www.themoviedb.org/t/p/w454_and_h254_bestv2/"
|
||||||
|
private const val BACKDROP_BASE = POSTER_BASE
|
||||||
|
private const val PERSON_BASE = POSTER_BASE
|
||||||
|
private const val AVATAR_BASE = POSTER_BASE
|
||||||
|
private const val STILL_BASE = POSTER_BASE
|
||||||
|
|
||||||
private const val DEF_REGION = "US"
|
private const val DEF_REGION = "US"
|
||||||
|
|
||||||
|
fun fullImagePath(sourcePath: String) = POSTER_BASE.plus(sourcePath)
|
||||||
|
|
||||||
fun getFullPosterPath(posterPath: String?): String? {
|
fun getFullPosterPath(posterPath: String?): String? {
|
||||||
return posterPath?.let {
|
return posterPath?.let {
|
||||||
if (posterPath.isEmpty()) null else "${POSTER_BASE}${posterPath}"
|
if (posterPath.isEmpty()) null else "${POSTER_BASE}${posterPath}"
|
||||||
@@ -203,7 +221,7 @@ object TmdbUtils {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatRevenue(revenue: Int): String {
|
fun formatRevenue(revenue: Long): String {
|
||||||
val decFormat = "%.1f"
|
val decFormat = "%.1f"
|
||||||
val thousands = revenue.toFloat() / 1000f
|
val thousands = revenue.toFloat() / 1000f
|
||||||
if (thousands > 1000) {
|
if (thousands > 1000) {
|
||||||
|
|||||||
@@ -221,4 +221,8 @@
|
|||||||
<string name="app_info_label">App Info</string>
|
<string name="app_info_label">App Info</string>
|
||||||
<string name="changelog_label">Changelog</string>
|
<string name="changelog_label">Changelog</string>
|
||||||
<string name="powered_by_tmdb">Powered by TMDB</string>
|
<string name="powered_by_tmdb">Powered by TMDB</string>
|
||||||
|
<string name="watch_providers_title">Watch Providers</string>
|
||||||
|
<string name="streaming_label">Streaming</string>
|
||||||
|
<string name="rent_label">Rent</string>
|
||||||
|
<string name="buy_label">Buy</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user