1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-06-08 01:24:05 +00:00

Use AboutLibraries to display library information

This commit is contained in:
Isira Seneviratne 2024-08-23 14:05:50 +05:30
parent 6d05af484e
commit 4758244cf5
7 changed files with 89 additions and 269 deletions

View File

@ -9,6 +9,8 @@ plugins {
id "kotlin-parcelize" id "kotlin-parcelize"
id "checkstyle" id "checkstyle"
id "org.sonarqube" version "4.0.0.2929" id "org.sonarqube" version "4.0.0.2929"
id "org.jetbrains.kotlin.plugin.compose" version "${kotlin_version}"
id 'com.mikepenz.aboutlibraries.plugin'
} }
android { android {
@ -104,10 +106,6 @@ android {
'META-INF/COPYRIGHT'] 'META-INF/COPYRIGHT']
} }
} }
composeOptions {
kotlinCompilerExtensionVersion = "1.5.14"
}
} }
ext { ext {
@ -293,6 +291,9 @@ dependencies {
implementation 'androidx.compose.ui:ui-text:1.7.0-beta05' // Needed for parsing HTML to AnnotatedString implementation 'androidx.compose.ui:ui-text:1.7.0-beta05' // Needed for parsing HTML to AnnotatedString
implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.2.0' implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.2.0'
// Library loading for About screen
implementation "com.mikepenz:aboutlibraries-compose-m3:$about_libs"
/** Debugging **/ /** Debugging **/
// Memory leak detection // Memory leak detection
debugImplementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}" debugImplementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"

View File

@ -1,28 +0,0 @@
package org.schabi.newpipe.ui.components.about
import android.content.Context
import androidx.annotation.StringRes
class AboutData(
@StringRes val title: Int,
@StringRes val description: Int,
@StringRes val buttonText: Int,
@StringRes val url: Int
)
/**
* Class for storing information about a software license.
*/
class License(val name: String, val abbreviation: String, val filename: String) {
fun getFormattedLicense(context: Context): String {
return context.assets.open(filename).bufferedReader().use { it.readText() }
}
}
class SoftwareComponent(
val name: String,
val years: String,
val copyrightOwner: String,
val link: String,
val license: License
)

View File

@ -1,10 +1,15 @@
package org.schabi.newpipe.ui.components.about package org.schabi.newpipe.ui.components.about
import androidx.annotation.StringRes
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
@ -15,6 +20,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import my.nanihadesuka.compose.ColumnScrollbar
import org.schabi.newpipe.BuildConfig import org.schabi.newpipe.BuildConfig
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.util.external_communication.ShareUtils import org.schabi.newpipe.util.external_communication.ShareUtils
@ -39,9 +46,26 @@ private val ABOUT_ITEMS = listOf(
) )
) )
private class AboutData(
@StringRes val title: Int,
@StringRes val description: Int,
@StringRes val buttonText: Int,
@StringRes val url: Int
)
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun AboutTab() { fun AboutTab() {
val scrollState = rememberScrollState()
ColumnScrollbar(state = scrollState) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp, vertical = 10.dp)
.verticalScroll(scrollState),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -64,6 +88,8 @@ fun AboutTab() {
for (item in ABOUT_ITEMS) { for (item in ABOUT_ITEMS) {
AboutItem(item) AboutItem(item)
} }
}
}
} }
@Composable @Composable

View File

@ -1,126 +1,35 @@
package org.schabi.newpipe.ui.components.about package org.schabi.newpipe.ui.components.about
import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.NonRestartableComposable import androidx.compose.runtime.NonRestartableComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp
import androidx.compose.ui.text.SpanStyle import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
import androidx.compose.ui.text.TextLinkStyles import my.nanihadesuka.compose.LazyColumnScrollbar
import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.text.style.TextDecoration
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.util.external_communication.ShareUtils
private val SOFTWARE_COMPONENTS = listOf(
SoftwareComponent(
"ACRA", "2013", "Kevin Gaudin",
"https://github.com/ACRA/acra", StandardLicenses.APACHE2
),
SoftwareComponent(
"AndroidX", "2005 - 2011", "The Android Open Source Project",
"https://developer.android.com/jetpack", StandardLicenses.APACHE2
),
SoftwareComponent(
"Coil", "2023", "Coil Contributors",
"https://coil-kt.github.io/coil/", StandardLicenses.APACHE2
),
SoftwareComponent(
"ExoPlayer", "2014 - 2020", "Google, Inc.",
"https://github.com/google/ExoPlayer", StandardLicenses.APACHE2
),
SoftwareComponent(
"GigaGet", "2014 - 2015", "Peter Cai",
"https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL3
),
SoftwareComponent(
"Groupie", "2016", "Lisa Wray",
"https://github.com/lisawray/groupie", StandardLicenses.MIT
),
SoftwareComponent(
"Icepick", "2015", "Frankie Sardo",
"https://github.com/frankiesardo/icepick", StandardLicenses.EPL1
),
SoftwareComponent(
"Jsoup", "2009 - 2020", "Jonathan Hedley",
"https://github.com/jhy/jsoup", StandardLicenses.MIT
),
SoftwareComponent(
"LazyColumnScrollbar", "2024", "nani",
"https://github.com/nanihadesuka/LazyColumnScrollbar", StandardLicenses.MIT
),
SoftwareComponent(
"Markwon", "2019", "Dimitry Ivanov",
"https://github.com/noties/Markwon", StandardLicenses.APACHE2
),
SoftwareComponent(
"Material Components for Android", "2016 - 2020", "Google, Inc.",
"https://github.com/material-components/material-components-android",
StandardLicenses.APACHE2
),
SoftwareComponent(
"NewPipe Extractor", "2017 - 2020", "Christian Schabesberger",
"https://github.com/TeamNewPipe/NewPipeExtractor", StandardLicenses.GPL3
),
SoftwareComponent(
"NoNonsense-FilePicker", "2016", "Jonas Kalderstam",
"https://github.com/spacecowboy/NoNonsense-FilePicker", StandardLicenses.MPL2
),
SoftwareComponent(
"OkHttp", "2019", "Square, Inc.",
"https://square.github.io/okhttp/", StandardLicenses.APACHE2
),
SoftwareComponent(
"PrettyTime", "2012 - 2020", "Lincoln Baxter, III",
"https://github.com/ocpsoft/prettytime", StandardLicenses.APACHE2
),
SoftwareComponent(
"ProcessPhoenix", "2015", "Jake Wharton",
"https://github.com/JakeWharton/ProcessPhoenix", StandardLicenses.APACHE2
),
SoftwareComponent(
"RxAndroid", "2015", "The RxAndroid authors",
"https://github.com/ReactiveX/RxAndroid", StandardLicenses.APACHE2
),
SoftwareComponent(
"RxBinding", "2015", "Jake Wharton",
"https://github.com/JakeWharton/RxBinding", StandardLicenses.APACHE2
),
SoftwareComponent(
"RxJava", "2016 - 2020", "RxJava Contributors",
"https://github.com/ReactiveX/RxJava", StandardLicenses.APACHE2
),
SoftwareComponent(
"SearchPreference", "2018", "ByteHamster",
"https://github.com/ByteHamster/SearchPreference", StandardLicenses.MIT
)
)
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun LicenseTab() { fun LicenseTab() {
var selectedLicense by remember { mutableStateOf<SoftwareComponent?>(null) } val lazyListState = rememberLazyListState()
val onClick = remember {
{ it: SoftwareComponent -> selectedLicense = it }
}
LazyColumnScrollbar(state = lazyListState) {
LibrariesContainer(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp, vertical = 10.dp),
lazyListState = lazyListState,
header = {
item {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text( Text(
text = stringResource(R.string.app_license_title), text = stringResource(R.string.app_license_title),
style = MaterialTheme.typography.titleLarge style = MaterialTheme.typography.titleLarge
@ -129,69 +38,13 @@ fun LicenseTab() {
text = stringResource(R.string.app_license), text = stringResource(R.string.app_license),
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium
) )
Text( Text(
text = stringResource(R.string.title_licenses), text = stringResource(R.string.title_licenses),
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
) )
for (component in SOFTWARE_COMPONENTS) {
LicenseItem(component, onClick)
}
selectedLicense?.let {
var formattedLicense by remember { mutableStateOf("") }
val context = LocalContext.current
LaunchedEffect(key1 = it) {
formattedLicense = withContext(Dispatchers.IO) {
it.license.getFormattedLicense(context)
} }
} }
AlertDialog(
onDismissRequest = { selectedLicense = null },
confirmButton = {
TextButton(onClick = { ShareUtils.openUrlInApp(context, it.link) }) {
Text(text = stringResource(R.string.open_website_license))
}
},
dismissButton = {
TextButton(onClick = { selectedLicense = null }) {
Text(text = stringResource(R.string.done))
}
},
title = {
Text(text = it.name, color = MaterialTheme.colorScheme.onBackground)
},
text = {
val styles = TextLinkStyles(SpanStyle(textDecoration = TextDecoration.Underline))
Text(
modifier = Modifier.verticalScroll(rememberScrollState()),
text = AnnotatedString.fromHtml(formattedLicense, styles)
)
} }
) )
} }
} }
@Composable
@NonRestartableComposable
private fun LicenseItem(
softwareComponent: SoftwareComponent,
onClick: (SoftwareComponent) -> Unit
) {
Column(
modifier = Modifier
.fillMaxWidth()
.clickable { onClick(softwareComponent) }
) {
Text(text = softwareComponent.name)
Text(
style = MaterialTheme.typography.bodyMedium,
text = stringResource(
R.string.copyright, softwareComponent.years,
softwareComponent.copyrightOwner, softwareComponent.license.abbreviation
)
)
}
}

View File

@ -1,21 +0,0 @@
package org.schabi.newpipe.ui.components.about
/**
* Class containing information about standard software licenses.
*/
object StandardLicenses {
@JvmField
val GPL3 = License("GNU General Public License, Version 3.0", "GPLv3", "gpl_3.html")
@JvmField
val APACHE2 = License("Apache License, Version 2.0", "ALv2", "apache2.html")
@JvmField
val MPL2 = License("Mozilla Public License, Version 2.0", "MPL 2.0", "mpl2.html")
@JvmField
val MIT = License("MIT License", "MIT", "mit.html")
@JvmField
val EPL1 = License("Eclipse Public License, Version 1.0", "EPL 1.0", "epl1.html")
}

View File

@ -1,15 +1,12 @@
package org.schabi.newpipe.ui.screens package org.schabi.newpipe.ui.screens
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
@ -27,7 +24,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import my.nanihadesuka.compose.ColumnScrollbar
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.ui.components.about.AboutTab import org.schabi.newpipe.ui.components.about.AboutTab
import org.schabi.newpipe.ui.components.about.LicenseTab import org.schabi.newpipe.ui.components.about.LicenseTab
@ -65,16 +61,6 @@ fun AboutScreen(padding: PaddingValues) {
.fillMaxWidth() .fillMaxWidth()
.weight(1f) .weight(1f)
) { page -> ) { page ->
val scrollState = rememberScrollState()
ColumnScrollbar(state = scrollState) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp, vertical = 10.dp)
.verticalScroll(scrollState),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
if (page == 0) { if (page == 0) {
AboutTab() AboutTab()
} else { } else {
@ -82,8 +68,6 @@ fun AboutScreen(padding: PaddingValues) {
} }
} }
} }
}
}
} }
@Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO) @Preview(name = "Light mode", uiMode = Configuration.UI_MODE_NIGHT_NO)

View File

@ -1,14 +1,19 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.9.24' ext {
kotlin_version = '2.0.0'
about_libs = '11.2.2'
}
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:8.2.0' classpath 'com.android.tools.build:gradle:8.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libs"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files