feat: adding attributions page

This commit is contained in:
tapframe 2026-05-10 12:36:15 +05:30
parent c477e7e217
commit 38c882980c
11 changed files with 461 additions and 0 deletions

View file

@ -5,6 +5,7 @@ import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import com.nuvio.app.R import com.nuvio.app.R
import nuvio.composeapp.generated.resources.Res import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.introdb_favicon
import nuvio.composeapp.generated.resources.rating_tmdb import nuvio.composeapp.generated.resources.rating_tmdb
import org.jetbrains.compose.resources.painterResource as composePainterResource import org.jetbrains.compose.resources.painterResource as composePainterResource
@ -14,4 +15,5 @@ internal actual fun integrationLogoPainter(logo: IntegrationLogo): Painter =
IntegrationLogo.Tmdb -> composePainterResource(Res.drawable.rating_tmdb) IntegrationLogo.Tmdb -> composePainterResource(Res.drawable.rating_tmdb)
IntegrationLogo.Trakt -> painterResource(id = R.drawable.trakt_tv_favicon) IntegrationLogo.Trakt -> painterResource(id = R.drawable.trakt_tv_favicon)
IntegrationLogo.MdbList -> painterResource(id = R.drawable.mdblist_logo) IntegrationLogo.MdbList -> painterResource(id = R.drawable.mdblist_logo)
IntegrationLogo.IntroDb -> composePainterResource(Res.drawable.introdb_favicon)
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -1,4 +1,5 @@
<resources> <resources>
<string name="about_licenses_attributions_subtitle">Data sources, acknowledgements, and platform licenses</string>
<string name="about_supporters_contributors_subtitle">Open recognition and project credits</string> <string name="about_supporters_contributors_subtitle">Open recognition and project credits</string>
<string name="action_back">Back</string> <string name="action_back">Back</string>
<string name="action_cancel">Cancel</string> <string name="action_cancel">Cancel</string>
@ -366,6 +367,7 @@
<string name="compose_settings_page_continue_watching">Continue Watching</string> <string name="compose_settings_page_continue_watching">Continue Watching</string>
<string name="compose_settings_page_homescreen">Home Layout</string> <string name="compose_settings_page_homescreen">Home Layout</string>
<string name="compose_settings_page_integrations">Integrations</string> <string name="compose_settings_page_integrations">Integrations</string>
<string name="compose_settings_page_licenses_attributions">Licenses &amp; Attribution</string>
<string name="compose_settings_page_mdblist_ratings">MDBList Ratings</string> <string name="compose_settings_page_mdblist_ratings">MDBList Ratings</string>
<string name="compose_settings_page_meta_screen">Detail Page</string> <string name="compose_settings_page_meta_screen">Detail Page</string>
<string name="compose_settings_page_notifications">Notifications</string> <string name="compose_settings_page_notifications">Notifications</string>
@ -394,6 +396,28 @@
<string name="settings_search_empty">No settings found.</string> <string name="settings_search_empty">No settings found.</string>
<string name="settings_search_placeholder">Search settings...</string> <string name="settings_search_placeholder">Search settings...</string>
<string name="settings_search_results_section">RESULTS</string> <string name="settings_search_results_section">RESULTS</string>
<string name="settings_licenses_attributions_section_app">APP LICENSE</string>
<string name="settings_licenses_attributions_section_data">DATA &amp; SERVICES</string>
<string name="settings_licenses_attributions_section_playback">PLAYBACK LICENSE</string>
<string name="settings_licenses_attributions_nuvio_title">Nuvio Mobile</string>
<string name="settings_licenses_attributions_nuvio_body">Source code and license terms are available in the project repository.</string>
<string name="settings_licenses_attributions_nuvio_license">Licensed under the GNU General Public License v3.0.</string>
<string name="settings_licenses_attributions_tmdb_title">The Movie Database (TMDB)</string>
<string name="settings_licenses_attributions_tmdb_body">Nuvio uses the TMDB API for movie and TV metadata, artwork, trailers, cast, production details, collections, and recommendations. This product uses the TMDB API but is not endorsed or certified by TMDB.</string>
<string name="settings_licenses_attributions_imdb_title">IMDb Non-Commercial Datasets</string>
<string name="settings_licenses_attributions_imdb_body">Nuvio uses IMDb Non-Commercial Datasets, including title.ratings.tsv.gz, for IMDb ratings and vote counts. Information courtesy of IMDb (https://www.imdb.com). Used with permission. IMDb data is for personal and non-commercial use under IMDb&apos;s terms.</string>
<string name="settings_licenses_attributions_trakt_title">Trakt</string>
<string name="settings_licenses_attributions_trakt_body">Nuvio connects to Trakt for account authentication, watched history, progress sync, library data, ratings, lists, and comments. Nuvio is not affiliated with or endorsed by Trakt.</string>
<string name="settings_licenses_attributions_mdblist_title">MDBList</string>
<string name="settings_licenses_attributions_mdblist_body">Nuvio uses MDBList for ratings and external score provider data. Nuvio is not affiliated with or endorsed by MDBList.</string>
<string name="settings_licenses_attributions_introdb_title">IntroDB</string>
<string name="settings_licenses_attributions_introdb_body">Nuvio uses the IntroDB API for community-provided intro, recap, credits, and preview timestamps used by skip controls. Nuvio is not affiliated with or endorsed by IntroDB.</string>
<string name="settings_licenses_attributions_mpvkit_title">MPVKit</string>
<string name="settings_licenses_attributions_mpvkit_body">Used for playback on iOS builds.</string>
<string name="settings_licenses_attributions_mpvkit_license">MPVKit source alone is licensed under LGPL v3.0. MPVKit bundles, including libmpv and FFmpeg libraries, are also licensed under LGPL v3.0.</string>
<string name="settings_licenses_attributions_exoplayer_title">AndroidX Media3 ExoPlayer 1.8.0</string>
<string name="settings_licenses_attributions_exoplayer_body">Used for playback on Android builds.</string>
<string name="settings_licenses_attributions_exoplayer_license">Licensed under the Apache License, Version 2.0.</string>
<string name="compose_trakt_list_picker_loading">Loading your Trakt lists…</string> <string name="compose_trakt_list_picker_loading">Loading your Trakt lists…</string>
<string name="compose_trakt_list_picker_subtitle">Choose where to save this title on Trakt</string> <string name="compose_trakt_list_picker_subtitle">Choose where to save this title on Trakt</string>
<string name="action_donate">Donate</string> <string name="action_donate">Donate</string>

View file

@ -145,6 +145,7 @@ import com.nuvio.app.features.settings.AddonsSettingsScreen
import com.nuvio.app.features.settings.PluginsSettingsScreen import com.nuvio.app.features.settings.PluginsSettingsScreen
import com.nuvio.app.features.settings.AccountSettingsScreen import com.nuvio.app.features.settings.AccountSettingsScreen
import com.nuvio.app.features.settings.SupportersContributorsSettingsScreen import com.nuvio.app.features.settings.SupportersContributorsSettingsScreen
import com.nuvio.app.features.settings.LicensesAttributionsSettingsScreen
import com.nuvio.app.features.settings.ThemeSettingsRepository import com.nuvio.app.features.settings.ThemeSettingsRepository
import com.nuvio.app.features.collection.CollectionManagementScreen import com.nuvio.app.features.collection.CollectionManagementScreen
import com.nuvio.app.features.collection.CollectionEditorScreen import com.nuvio.app.features.collection.CollectionEditorScreen
@ -237,6 +238,9 @@ object AccountSettingsRoute
@Serializable @Serializable
object SupportersContributorsSettingsRoute object SupportersContributorsSettingsRoute
@Serializable
object LicensesAttributionsSettingsRoute
@Serializable @Serializable
object CollectionsRoute object CollectionsRoute
@ -1065,6 +1069,9 @@ private fun MainAppContent(
onSupportersContributorsSettingsClick = { onSupportersContributorsSettingsClick = {
navController.navigate(SupportersContributorsSettingsRoute) navController.navigate(SupportersContributorsSettingsRoute)
}, },
onLicensesAttributionsSettingsClick = {
navController.navigate(LicensesAttributionsSettingsRoute)
},
onCheckForUpdatesClick = if (AppFeaturePolicy.inAppUpdaterEnabled) { onCheckForUpdatesClick = if (AppFeaturePolicy.inAppUpdaterEnabled) {
{ {
appUpdaterController.checkForUpdates( appUpdaterController.checkForUpdates(
@ -1698,6 +1705,15 @@ private fun MainAppContent(
onBack = onBack, onBack = onBack,
) )
} }
composable<LicensesAttributionsSettingsRoute> { backStackEntry ->
val onBack = rememberGuardedPopBackStack(
navController = navController,
backStackEntry = backStackEntry,
)
LicensesAttributionsSettingsScreen(
onBack = onBack,
)
}
composable<CollectionsRoute> { backStackEntry -> composable<CollectionsRoute> { backStackEntry ->
val onBack = rememberGuardedPopBackStack( val onBack = rememberGuardedPopBackStack(
navController = navController, navController = navController,
@ -1972,6 +1988,7 @@ private fun AppTabHost(
onPluginsSettingsClick: () -> Unit = {}, onPluginsSettingsClick: () -> Unit = {},
onAccountSettingsClick: () -> Unit = {}, onAccountSettingsClick: () -> Unit = {},
onSupportersContributorsSettingsClick: () -> Unit = {}, onSupportersContributorsSettingsClick: () -> Unit = {},
onLicensesAttributionsSettingsClick: () -> Unit = {},
onCheckForUpdatesClick: (() -> Unit)? = null, onCheckForUpdatesClick: (() -> Unit)? = null,
onCollectionsSettingsClick: () -> Unit = {}, onCollectionsSettingsClick: () -> Unit = {},
onFolderClick: ((collectionId: String, folderId: String) -> Unit)? = null, onFolderClick: ((collectionId: String, folderId: String) -> Unit)? = null,
@ -2024,6 +2041,7 @@ private fun AppTabHost(
onPluginsClick = onPluginsSettingsClick, onPluginsClick = onPluginsSettingsClick,
onAccountClick = onAccountSettingsClick, onAccountClick = onAccountSettingsClick,
onSupportersContributorsClick = onSupportersContributorsSettingsClick, onSupportersContributorsClick = onSupportersContributorsSettingsClick,
onLicensesAttributionsClick = onLicensesAttributionsSettingsClick,
onCheckForUpdatesClick = onCheckForUpdatesClick, onCheckForUpdatesClick = onCheckForUpdatesClick,
onCollectionsClick = onCollectionsSettingsClick, onCollectionsClick = onCollectionsSettingsClick,
) )

View file

@ -7,6 +7,7 @@ internal enum class IntegrationLogo {
Tmdb, Tmdb,
Trakt, Trakt,
MdbList, MdbList,
IntroDb,
} }
@Composable @Composable

View file

@ -0,0 +1,341 @@
package com.nuvio.app.features.settings
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.OpenInNew
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
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.alpha
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.nuvio.app.core.ui.NuvioScreen
import com.nuvio.app.core.ui.NuvioScreenHeader
import com.nuvio.app.isIos
import nuvio.composeapp.generated.resources.*
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
private const val TmdbUrl = "https://www.themoviedb.org"
private const val ImdbDatasetsUrl = "https://developer.imdb.com/non-commercial-datasets/"
private const val TraktUrl = "https://trakt.tv"
private const val MdbListUrl = "https://mdblist.com"
private const val IntroDbUrl = "https://introdb.app/"
private const val NuvioRepositoryUrl = "https://github.com/NuvioMedia/NuvioMobile"
private const val MpvKitUrl = "https://github.com/mpvkit/MPVKit"
private const val ApacheLicenseUrl = "https://www.apache.org/licenses/LICENSE-2.0"
private data class AttributionItem(
val titleRes: StringResource,
val bodyRes: StringResource,
val logo: IntegrationLogo?,
val link: String,
)
private data class LicenseItem(
val titleRes: StringResource,
val bodyRes: StringResource,
val licenseRes: StringResource,
val link: String,
)
@Composable
fun LicensesAttributionsSettingsScreen(
onBack: () -> Unit,
) {
NuvioScreen(
modifier = Modifier.fillMaxSize(),
) {
stickyHeader {
NuvioScreenHeader(
title = stringResource(Res.string.compose_settings_page_licenses_attributions),
onBack = onBack,
)
}
licensesAttributionsContent(isTablet = false)
}
}
internal fun LazyListScope.licensesAttributionsContent(
isTablet: Boolean,
) {
item {
LicensesAttributionsBody(isTablet = isTablet)
}
}
@Composable
private fun LicensesAttributionsBody(
isTablet: Boolean,
) {
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(if (isTablet) 28.dp else 24.dp),
) {
PlainSettingsStack(
title = stringResource(Res.string.settings_licenses_attributions_section_app),
isTablet = isTablet,
) {
LicenseRow(
item = appLicenseItem(),
isTablet = isTablet,
)
}
PlainSettingsStack(
title = stringResource(Res.string.settings_licenses_attributions_section_data),
isTablet = isTablet,
) {
val items = attributionItems()
items.forEachIndexed { index, item ->
AttributionRow(
item = item,
isTablet = isTablet,
)
if (index != items.lastIndex) {
PlainStackDivider()
}
}
}
PlainSettingsStack(
title = stringResource(Res.string.settings_licenses_attributions_section_playback),
isTablet = isTablet,
) {
LicenseRow(
item = platformLicenseItem(),
isTablet = isTablet,
)
}
}
}
@Composable
private fun PlainSettingsStack(
title: String,
isTablet: Boolean,
content: @Composable () -> Unit,
) {
Column(
modifier = Modifier.fillMaxWidth(),
) {
Text(
text = title,
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
fontWeight = FontWeight.Bold,
)
Spacer(modifier = Modifier.height(if (isTablet) 12.dp else 10.dp))
Column(
modifier = Modifier.fillMaxWidth(),
) {
content()
}
}
}
@Composable
private fun AttributionRow(
item: AttributionItem,
isTablet: Boolean,
) {
val uriHandler = LocalUriHandler.current
val title = stringResource(item.titleRes)
LinkedPlainRow(
title = title,
body = stringResource(item.bodyRes),
link = item.link,
isTablet = isTablet,
leading = item.logo?.let { logo ->
{
IntegrationLogoImage(
painter = integrationLogoPainter(logo),
contentDescription = title,
isTablet = isTablet,
)
}
},
onOpen = { uriHandler.openUri(item.link) },
)
}
@Composable
private fun LicenseRow(
item: LicenseItem,
isTablet: Boolean,
) {
val uriHandler = LocalUriHandler.current
val itemBody = stringResource(item.bodyRes)
val itemLicense = stringResource(item.licenseRes)
val body = buildString {
append(itemBody)
append("\n")
append(itemLicense)
}
LinkedPlainRow(
title = stringResource(item.titleRes),
body = body,
link = item.link,
isTablet = isTablet,
onOpen = { uriHandler.openUri(item.link) },
)
}
@Composable
private fun LinkedPlainRow(
title: String,
body: String,
link: String,
isTablet: Boolean,
leading: (@Composable () -> Unit)? = null,
onOpen: () -> Unit,
) {
val verticalPadding = if (isTablet) 18.dp else 16.dp
val horizontalPadding = if (isTablet) 4.dp else 0.dp
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onOpen)
.padding(horizontal = horizontalPadding, vertical = verticalPadding),
verticalAlignment = Alignment.Top,
horizontalArrangement = Arrangement.spacedBy(if (isTablet) 18.dp else 14.dp),
) {
leading?.invoke()
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(6.dp),
) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface,
fontWeight = FontWeight.SemiBold,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
Text(
text = body,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Text(
text = link,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
Icon(
imageVector = Icons.AutoMirrored.Rounded.OpenInNew,
contentDescription = null,
modifier = Modifier
.padding(top = 2.dp)
.size(if (isTablet) 22.dp else 20.dp)
.alpha(0.72f),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
@Composable
private fun IntegrationLogoImage(
painter: Painter,
contentDescription: String,
isTablet: Boolean,
) {
Image(
painter = painter,
contentDescription = contentDescription,
modifier = Modifier
.padding(top = 2.dp)
.size(if (isTablet) 46.dp else 40.dp),
contentScale = ContentScale.Fit,
)
}
@Composable
private fun PlainStackDivider() {
HorizontalDivider(
thickness = 0.5.dp,
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.18f),
)
}
private fun attributionItems(): List<AttributionItem> = listOf(
AttributionItem(
titleRes = Res.string.settings_licenses_attributions_tmdb_title,
bodyRes = Res.string.settings_licenses_attributions_tmdb_body,
logo = IntegrationLogo.Tmdb,
link = TmdbUrl,
),
AttributionItem(
titleRes = Res.string.settings_licenses_attributions_trakt_title,
bodyRes = Res.string.settings_licenses_attributions_trakt_body,
logo = IntegrationLogo.Trakt,
link = TraktUrl,
),
AttributionItem(
titleRes = Res.string.settings_licenses_attributions_mdblist_title,
bodyRes = Res.string.settings_licenses_attributions_mdblist_body,
logo = IntegrationLogo.MdbList,
link = MdbListUrl,
),
AttributionItem(
titleRes = Res.string.settings_licenses_attributions_introdb_title,
bodyRes = Res.string.settings_licenses_attributions_introdb_body,
logo = IntegrationLogo.IntroDb,
link = IntroDbUrl,
),
AttributionItem(
titleRes = Res.string.settings_licenses_attributions_imdb_title,
bodyRes = Res.string.settings_licenses_attributions_imdb_body,
logo = null,
link = ImdbDatasetsUrl,
),
)
private fun appLicenseItem(): LicenseItem =
LicenseItem(
titleRes = Res.string.settings_licenses_attributions_nuvio_title,
bodyRes = Res.string.settings_licenses_attributions_nuvio_body,
licenseRes = Res.string.settings_licenses_attributions_nuvio_license,
link = NuvioRepositoryUrl,
)
private fun platformLicenseItem(): LicenseItem =
if (isIos) {
LicenseItem(
titleRes = Res.string.settings_licenses_attributions_mpvkit_title,
bodyRes = Res.string.settings_licenses_attributions_mpvkit_body,
licenseRes = Res.string.settings_licenses_attributions_mpvkit_license,
link = MpvKitUrl,
)
} else {
LicenseItem(
titleRes = Res.string.settings_licenses_attributions_exoplayer_title,
bodyRes = Res.string.settings_licenses_attributions_exoplayer_body,
licenseRes = Res.string.settings_licenses_attributions_exoplayer_license,
link = ApacheLicenseUrl,
)
}

View file

@ -16,6 +16,7 @@ import nuvio.composeapp.generated.resources.compose_settings_page_content_discov
import nuvio.composeapp.generated.resources.compose_settings_page_continue_watching import nuvio.composeapp.generated.resources.compose_settings_page_continue_watching
import nuvio.composeapp.generated.resources.compose_settings_page_homescreen import nuvio.composeapp.generated.resources.compose_settings_page_homescreen
import nuvio.composeapp.generated.resources.compose_settings_page_integrations import nuvio.composeapp.generated.resources.compose_settings_page_integrations
import nuvio.composeapp.generated.resources.compose_settings_page_licenses_attributions
import nuvio.composeapp.generated.resources.compose_settings_page_mdblist_ratings import nuvio.composeapp.generated.resources.compose_settings_page_mdblist_ratings
import nuvio.composeapp.generated.resources.compose_settings_page_meta_screen import nuvio.composeapp.generated.resources.compose_settings_page_meta_screen
import nuvio.composeapp.generated.resources.compose_settings_page_notifications import nuvio.composeapp.generated.resources.compose_settings_page_notifications
@ -58,6 +59,11 @@ internal enum class SettingsPage(
category = SettingsCategory.About, category = SettingsCategory.About,
parentPage = Root, parentPage = Root,
), ),
LicensesAttributions(
titleRes = Res.string.compose_settings_page_licenses_attributions,
category = SettingsCategory.About,
parentPage = Root,
),
Playback( Playback(
titleRes = Res.string.compose_settings_page_playback, titleRes = Res.string.compose_settings_page_playback,
category = SettingsCategory.General, category = SettingsCategory.General,

View file

@ -26,6 +26,7 @@ import nuvio.composeapp.generated.resources.compose_about_version_format
import nuvio.composeapp.generated.resources.compose_settings_page_account import nuvio.composeapp.generated.resources.compose_settings_page_account
import nuvio.composeapp.generated.resources.compose_settings_page_appearance import nuvio.composeapp.generated.resources.compose_settings_page_appearance
import nuvio.composeapp.generated.resources.compose_settings_page_integrations import nuvio.composeapp.generated.resources.compose_settings_page_integrations
import nuvio.composeapp.generated.resources.compose_settings_page_licenses_attributions
import nuvio.composeapp.generated.resources.compose_settings_page_notifications import nuvio.composeapp.generated.resources.compose_settings_page_notifications
import nuvio.composeapp.generated.resources.compose_settings_page_playback import nuvio.composeapp.generated.resources.compose_settings_page_playback
import nuvio.composeapp.generated.resources.compose_settings_page_supporters_contributors import nuvio.composeapp.generated.resources.compose_settings_page_supporters_contributors
@ -48,6 +49,7 @@ import nuvio.composeapp.generated.resources.compose_settings_page_content_discov
import nuvio.composeapp.generated.resources.compose_settings_page_trakt import nuvio.composeapp.generated.resources.compose_settings_page_trakt
import nuvio.composeapp.generated.resources.settings_playback_subtitle import nuvio.composeapp.generated.resources.settings_playback_subtitle
import nuvio.composeapp.generated.resources.about_supporters_contributors_subtitle import nuvio.composeapp.generated.resources.about_supporters_contributors_subtitle
import nuvio.composeapp.generated.resources.about_licenses_attributions_subtitle
import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.resources.stringResource
internal fun LazyListScope.settingsRootContent( internal fun LazyListScope.settingsRootContent(
@ -59,6 +61,7 @@ internal fun LazyListScope.settingsRootContent(
onIntegrationsClick: () -> Unit, onIntegrationsClick: () -> Unit,
onTraktClick: () -> Unit, onTraktClick: () -> Unit,
onSupportersContributorsClick: () -> Unit, onSupportersContributorsClick: () -> Unit,
onLicensesAttributionsClick: () -> Unit,
onCheckForUpdatesClick: (() -> Unit)? = null, onCheckForUpdatesClick: (() -> Unit)? = null,
onDownloadsClick: () -> Unit, onDownloadsClick: () -> Unit,
onAccountClick: () -> Unit, onAccountClick: () -> Unit,
@ -175,6 +178,14 @@ internal fun LazyListScope.settingsRootContent(
isTablet = isTablet, isTablet = isTablet,
onClick = onSupportersContributorsClick, onClick = onSupportersContributorsClick,
) )
SettingsGroupDivider(isTablet = isTablet)
SettingsNavigationRow(
title = stringResource(Res.string.compose_settings_page_licenses_attributions),
description = stringResource(Res.string.about_licenses_attributions_subtitle),
icon = Icons.Rounded.Info,
isTablet = isTablet,
onClick = onLicensesAttributionsClick,
)
if (onCheckForUpdatesClick != null) { if (onCheckForUpdatesClick != null) {
SettingsGroupDivider(isTablet = isTablet) SettingsGroupDivider(isTablet = isTablet)
SettingsNavigationRow( SettingsNavigationRow(

View file

@ -97,6 +97,7 @@ fun SettingsScreen(
onDownloadsClick: () -> Unit = {}, onDownloadsClick: () -> Unit = {},
onAccountClick: () -> Unit = {}, onAccountClick: () -> Unit = {},
onSupportersContributorsClick: () -> Unit = {}, onSupportersContributorsClick: () -> Unit = {},
onLicensesAttributionsClick: () -> Unit = {},
onCheckForUpdatesClick: (() -> Unit)? = null, onCheckForUpdatesClick: (() -> Unit)? = null,
onCollectionsClick: () -> Unit = {}, onCollectionsClick: () -> Unit = {},
) { ) {
@ -243,6 +244,7 @@ fun SettingsScreen(
onSwitchProfile = onSwitchProfile, onSwitchProfile = onSwitchProfile,
onDownloadsClick = onDownloadsClick, onDownloadsClick = onDownloadsClick,
onSupportersContributorsClick = onSupportersContributorsClick, onSupportersContributorsClick = onSupportersContributorsClick,
onLicensesAttributionsClick = onLicensesAttributionsClick,
onCheckForUpdatesClick = onCheckForUpdatesClick, onCheckForUpdatesClick = onCheckForUpdatesClick,
onCollectionsClick = onCollectionsClick, onCollectionsClick = onCollectionsClick,
) )
@ -294,6 +296,7 @@ fun SettingsScreen(
onDownloadsClick = onDownloadsClick, onDownloadsClick = onDownloadsClick,
onAccountClick = onAccountClick, onAccountClick = onAccountClick,
onSupportersContributorsClick = onSupportersContributorsClick, onSupportersContributorsClick = onSupportersContributorsClick,
onLicensesAttributionsClick = onLicensesAttributionsClick,
onCheckForUpdatesClick = onCheckForUpdatesClick, onCheckForUpdatesClick = onCheckForUpdatesClick,
onCollectionsClick = onCollectionsClick, onCollectionsClick = onCollectionsClick,
) )
@ -349,6 +352,7 @@ private fun MobileSettingsScreen(
onDownloadsClick: () -> Unit = {}, onDownloadsClick: () -> Unit = {},
onAccountClick: () -> Unit = {}, onAccountClick: () -> Unit = {},
onSupportersContributorsClick: () -> Unit = {}, onSupportersContributorsClick: () -> Unit = {},
onLicensesAttributionsClick: () -> Unit = {},
onCheckForUpdatesClick: (() -> Unit)? = null, onCheckForUpdatesClick: (() -> Unit)? = null,
onCollectionsClick: () -> Unit = {}, onCollectionsClick: () -> Unit = {},
) { ) {
@ -385,6 +389,7 @@ private fun MobileSettingsScreen(
is SettingsSearchTarget.Page -> when (target.page) { is SettingsSearchTarget.Page -> when (target.page) {
SettingsPage.Account -> onAccountClick() SettingsPage.Account -> onAccountClick()
SettingsPage.SupportersContributors -> onSupportersContributorsClick() SettingsPage.SupportersContributors -> onSupportersContributorsClick()
SettingsPage.LicensesAttributions -> onLicensesAttributionsClick()
SettingsPage.ContinueWatching -> onContinueWatchingClick() SettingsPage.ContinueWatching -> onContinueWatchingClick()
SettingsPage.Addons -> onAddonsClick() SettingsPage.Addons -> onAddonsClick()
SettingsPage.Plugins -> { SettingsPage.Plugins -> {
@ -443,6 +448,7 @@ private fun MobileSettingsScreen(
onIntegrationsClick = { onPageChange(SettingsPage.Integrations) }, onIntegrationsClick = { onPageChange(SettingsPage.Integrations) },
onTraktClick = { onPageChange(SettingsPage.TraktAuthentication) }, onTraktClick = { onPageChange(SettingsPage.TraktAuthentication) },
onSupportersContributorsClick = onSupportersContributorsClick, onSupportersContributorsClick = onSupportersContributorsClick,
onLicensesAttributionsClick = onLicensesAttributionsClick,
onCheckForUpdatesClick = onCheckForUpdatesClick, onCheckForUpdatesClick = onCheckForUpdatesClick,
onDownloadsClick = onDownloadsClick, onDownloadsClick = onDownloadsClick,
onAccountClick = onAccountClick, onAccountClick = onAccountClick,
@ -456,6 +462,9 @@ private fun MobileSettingsScreen(
SettingsPage.SupportersContributors -> supportersContributorsContent( SettingsPage.SupportersContributors -> supportersContributorsContent(
isTablet = false, isTablet = false,
) )
SettingsPage.LicensesAttributions -> licensesAttributionsContent(
isTablet = false,
)
SettingsPage.Playback -> playbackSettingsContent( SettingsPage.Playback -> playbackSettingsContent(
isTablet = false, isTablet = false,
showLoadingOverlay = showLoadingOverlay, showLoadingOverlay = showLoadingOverlay,
@ -635,6 +644,7 @@ private fun TabletSettingsScreen(
onSwitchProfile: (() -> Unit)? = null, onSwitchProfile: (() -> Unit)? = null,
onDownloadsClick: () -> Unit = {}, onDownloadsClick: () -> Unit = {},
onSupportersContributorsClick: () -> Unit = {}, onSupportersContributorsClick: () -> Unit = {},
onLicensesAttributionsClick: () -> Unit = {},
onCheckForUpdatesClick: (() -> Unit)? = null, onCheckForUpdatesClick: (() -> Unit)? = null,
onCollectionsClick: () -> Unit = {}, onCollectionsClick: () -> Unit = {},
) { ) {
@ -792,6 +802,7 @@ private fun TabletSettingsScreen(
onIntegrationsClick = { openInlinePage(SettingsPage.Integrations) }, onIntegrationsClick = { openInlinePage(SettingsPage.Integrations) },
onTraktClick = { openInlinePage(SettingsPage.TraktAuthentication) }, onTraktClick = { openInlinePage(SettingsPage.TraktAuthentication) },
onSupportersContributorsClick = { openInlinePage(SettingsPage.SupportersContributors) }, onSupportersContributorsClick = { openInlinePage(SettingsPage.SupportersContributors) },
onLicensesAttributionsClick = { openInlinePage(SettingsPage.LicensesAttributions) },
onCheckForUpdatesClick = onCheckForUpdatesClick, onCheckForUpdatesClick = onCheckForUpdatesClick,
onDownloadsClick = onDownloadsClick, onDownloadsClick = onDownloadsClick,
onAccountClick = { openInlinePage(SettingsPage.Account) }, onAccountClick = { openInlinePage(SettingsPage.Account) },
@ -808,6 +819,9 @@ private fun TabletSettingsScreen(
SettingsPage.SupportersContributors -> supportersContributorsContent( SettingsPage.SupportersContributors -> supportersContributorsContent(
isTablet = true, isTablet = true,
) )
SettingsPage.LicensesAttributions -> licensesAttributionsContent(
isTablet = true,
)
SettingsPage.Playback -> playbackSettingsContent( SettingsPage.Playback -> playbackSettingsContent(
isTablet = true, isTablet = true,
showLoadingOverlay = showLoadingOverlay, showLoadingOverlay = showLoadingOverlay,

View file

@ -21,6 +21,7 @@ import androidx.compose.material.icons.rounded.Favorite
import androidx.compose.material.icons.rounded.Hub import androidx.compose.material.icons.rounded.Hub
import androidx.compose.material.icons.rounded.Home import androidx.compose.material.icons.rounded.Home
import androidx.compose.material.icons.rounded.Language import androidx.compose.material.icons.rounded.Language
import androidx.compose.material.icons.rounded.Info
import androidx.compose.material.icons.rounded.Link import androidx.compose.material.icons.rounded.Link
import androidx.compose.material.icons.rounded.Notifications import androidx.compose.material.icons.rounded.Notifications
import androidx.compose.material.icons.rounded.Palette import androidx.compose.material.icons.rounded.Palette
@ -94,6 +95,7 @@ internal fun settingsSearchEntries(
val integrationsPage = stringResource(Res.string.compose_settings_page_integrations) val integrationsPage = stringResource(Res.string.compose_settings_page_integrations)
val notificationsPage = stringResource(Res.string.compose_settings_page_notifications) val notificationsPage = stringResource(Res.string.compose_settings_page_notifications)
val supportersPage = stringResource(Res.string.compose_settings_page_supporters_contributors) val supportersPage = stringResource(Res.string.compose_settings_page_supporters_contributors)
val licensesPage = stringResource(Res.string.compose_settings_page_licenses_attributions)
val homeLayoutPage = stringResource(Res.string.compose_settings_page_homescreen) val homeLayoutPage = stringResource(Res.string.compose_settings_page_homescreen)
val detailPage = stringResource(Res.string.compose_settings_page_meta_screen) val detailPage = stringResource(Res.string.compose_settings_page_meta_screen)
val continueWatchingPage = stringResource(Res.string.compose_settings_page_continue_watching) val continueWatchingPage = stringResource(Res.string.compose_settings_page_continue_watching)
@ -248,6 +250,46 @@ internal fun settingsSearchEntries(
category = aboutCategory, category = aboutCategory,
icon = Icons.Rounded.Favorite, icon = Icons.Rounded.Favorite,
) )
addPage(
page = SettingsPage.LicensesAttributions,
key = "licenses-attributions",
title = licensesPage,
description = stringResource(Res.string.about_licenses_attributions_subtitle),
category = aboutCategory,
icon = Icons.Rounded.Info,
)
listOf(
PlaybackSearchRow("nuvio-license", stringResource(Res.string.settings_licenses_attributions_nuvio_title), stringResource(Res.string.settings_licenses_attributions_nuvio_license)),
PlaybackSearchRow("tmdb-attribution", stringResource(Res.string.settings_licenses_attributions_tmdb_title), stringResource(Res.string.settings_licenses_attributions_tmdb_body)),
PlaybackSearchRow("trakt-attribution", stringResource(Res.string.settings_licenses_attributions_trakt_title), stringResource(Res.string.settings_licenses_attributions_trakt_body)),
PlaybackSearchRow("mdblist-attribution", stringResource(Res.string.settings_licenses_attributions_mdblist_title), stringResource(Res.string.settings_licenses_attributions_mdblist_body)),
PlaybackSearchRow("introdb-attribution", stringResource(Res.string.settings_licenses_attributions_introdb_title), stringResource(Res.string.settings_licenses_attributions_introdb_body)),
PlaybackSearchRow("imdb-datasets", stringResource(Res.string.settings_licenses_attributions_imdb_title), stringResource(Res.string.settings_licenses_attributions_imdb_body)),
PlaybackSearchRow(
if (isIos) "mpvkit-license" else "exoplayer-license",
if (isIos) {
stringResource(Res.string.settings_licenses_attributions_mpvkit_title)
} else {
stringResource(Res.string.settings_licenses_attributions_exoplayer_title)
},
if (isIos) {
stringResource(Res.string.settings_licenses_attributions_mpvkit_license)
} else {
stringResource(Res.string.settings_licenses_attributions_exoplayer_license)
},
),
).forEach { row ->
addRow(
page = SettingsPage.LicensesAttributions,
key = row.key,
title = row.title,
description = row.description,
pageLabel = licensesPage,
section = stringResource(Res.string.compose_settings_root_about_section),
category = aboutCategory,
icon = Icons.Rounded.Info,
)
}
if (checkForUpdatesAvailable) { if (checkForUpdatesAvailable) {
add( add(
key = "check-updates", key = "check-updates",

View file

@ -3,6 +3,7 @@ package com.nuvio.app.features.settings
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import nuvio.composeapp.generated.resources.Res import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.introdb_favicon
import nuvio.composeapp.generated.resources.mdblist_logo import nuvio.composeapp.generated.resources.mdblist_logo
import nuvio.composeapp.generated.resources.rating_tmdb import nuvio.composeapp.generated.resources.rating_tmdb
import nuvio.composeapp.generated.resources.trakt_tv_favicon import nuvio.composeapp.generated.resources.trakt_tv_favicon
@ -14,4 +15,5 @@ internal actual fun integrationLogoPainter(logo: IntegrationLogo): Painter =
IntegrationLogo.Tmdb -> painterResource(Res.drawable.rating_tmdb) IntegrationLogo.Tmdb -> painterResource(Res.drawable.rating_tmdb)
IntegrationLogo.Trakt -> painterResource(Res.drawable.trakt_tv_favicon) IntegrationLogo.Trakt -> painterResource(Res.drawable.trakt_tv_favicon)
IntegrationLogo.MdbList -> painterResource(Res.drawable.mdblist_logo) IntegrationLogo.MdbList -> painterResource(Res.drawable.mdblist_logo)
IntegrationLogo.IntroDb -> painterResource(Res.drawable.introdb_favicon)
} }