diff --git a/build.gradle b/build.gradle index 296acb6cf9f..6ab2af2cde4 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ plugins { id 'org.jetbrains.kotlin.plugin.serialization' version "1.8.21" id "com.google.devtools.ksp" version "1.8.21-1.0.11" id 'com.google.protobuf' version "0.9.3" - id 'com.google.android.gms.oss-licenses-plugin' version "0.10.6" + id 'app.cash.licensee' version "1.7.0" id 'dev.rikka.tools.refine' version "4.3.0" } @@ -106,6 +106,15 @@ android { } applicationVariants.configureEach { variant -> + def copyArtifactList = tasks.register("copy${variant.name.capitalize()}ArtifactList", Copy) { + dependsOn tasks.named("licensee${variant.name.capitalize()}") + from project.extensions.getByType(ReportingExtension).file("licensee/${variant.name}/artifacts.json") + into layout.buildDirectory.dir("generated/dependencyAssets/") + } + tasks.named("merge${variant.name.capitalize()}Assets").configure { + dependsOn copyArtifactList + } + variant.outputs.configureEach { outputFileName = "Lawnchair ${variant.versionName}.apk" } @@ -279,6 +288,7 @@ android { lawnWithQuickstep { manifest.srcFile "quickstep/AndroidManifest-launcher.xml" + assets.srcDir layout.buildDirectory.dir("generated/dependencyAssets/") } withoutQuickstep { @@ -358,7 +368,6 @@ dependencies { implementation "com.github.topjohnwu.libsu:service:$libsu_version" implementation "com.google.protobuf:protobuf-javalite:$protocVersion" - implementation 'com.github.LawnchairLauncher:oss-notices:1.0.2' // Persian Date implementation 'com.github.samanzamani:PersianDate:1.6.1' @@ -389,3 +398,14 @@ protobuf { } } } + +licensee { + allow("Apache-2.0") + allow("BSD-3-Clause") + allowUrl("https://api.github.com/licenses/apache-2.0") + allowUrl("https://api.github.com/licenses/bsd-3-clause") + allowUrl("https://github.com/patrykmichalik/opto/blob/master/LICENSE") + allowUrl("https://github.com/RikkaApps/HiddenApiRefinePlugin/blob/main/LICENSE") + allowUrl("https://github.com/stleary/JSON-java/blob/master/LICENSE") + allowUrl("https://www.gnu.org/licenses/old-licenses/gpl-2.0.html") +} diff --git a/lawnchair/src/app/lawnchair/ui/preferences/PreferenceInteractor.kt b/lawnchair/src/app/lawnchair/ui/preferences/PreferenceInteractor.kt index 2c3eb6e9e19..455c18faa02 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/PreferenceInteractor.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/PreferenceInteractor.kt @@ -16,7 +16,7 @@ package app.lawnchair.ui.preferences -import app.lawnchair.ossnotices.OssLibrary +import app.lawnchair.ui.preferences.about.acknowledgements.OssLibrary import kotlinx.coroutines.flow.StateFlow sealed interface PreferenceInteractor { diff --git a/lawnchair/src/app/lawnchair/ui/preferences/PreferenceViewModel.kt b/lawnchair/src/app/lawnchair/ui/preferences/PreferenceViewModel.kt index 824e646d440..831c2a8c425 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/PreferenceViewModel.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/PreferenceViewModel.kt @@ -22,17 +22,17 @@ import androidx.core.content.ContextCompat import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import app.lawnchair.icons.CustomAdaptiveIconDrawable -import app.lawnchair.ossnotices.OssLibrary -import app.lawnchair.ossnotices.getOssLibraries +import app.lawnchair.ui.preferences.about.acknowledgements.OssLibrary +import app.lawnchair.util.Constants.LAWNICONS_PACKAGE_NAME +import app.lawnchair.util.getPackageVersionCode import com.android.launcher3.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.stateIn -import app.lawnchair.util.getPackageVersionCode -import app.lawnchair.util.Constants.LAWNICONS_PACKAGE_NAME -import kotlinx.coroutines.flow.StateFlow +import kotlinx.serialization.json.Json private val iconPackIntents = listOf( Intent("com.novalauncher.THEME"), @@ -109,10 +109,15 @@ class PreferenceViewModel(private val app: Application) : AndroidViewModel(app), .stateIn(viewModelScope, SharingStarted.Lazily, listOf()) override val ossLibraries: StateFlow> = flow { - val ossLibraries = app.getOssLibraries(R.raw.third_party_license_metadata) - .distinctBy { it.name } + val jsonString = app.resources.assets.open("artifacts.json") + .bufferedReader().use { it.readText() } + val ossLibraries = Json.decodeFromString>(jsonString) + .asSequence() + .distinctBy { "${it.groupId}:${it.artifactId}" } + .sortedBy { it.name } + .toList() emit(ossLibraries) } - .flowOn(Dispatchers.Default) + .flowOn(Dispatchers.IO) .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/about/acknowledgements/Acknowledgements.kt b/lawnchair/src/app/lawnchair/ui/preferences/about/acknowledgements/Acknowledgements.kt index d496e791ee0..73e3485ad3b 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/about/acknowledgements/Acknowledgements.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/about/acknowledgements/Acknowledgements.kt @@ -36,7 +36,6 @@ import androidx.compose.ui.unit.sp import androidx.navigation.NavGraphBuilder import androidx.navigation.NavType import androidx.navigation.navArgument -import app.lawnchair.ossnotices.OssLibrary import app.lawnchair.ui.preferences.LocalNavController import app.lawnchair.ui.preferences.LocalPreferenceInteractor import app.lawnchair.ui.preferences.components.* @@ -44,9 +43,6 @@ import app.lawnchair.ui.preferences.preferenceGraph import app.lawnchair.ui.preferences.subRoute import com.android.launcher3.R import com.google.accompanist.navigation.animation.composable -import com.google.accompanist.placeholder.PlaceholderHighlight -import com.google.accompanist.placeholder.material.fade -import com.google.accompanist.placeholder.material.placeholder @OptIn(ExperimentalAnimationApi::class) fun NavGraphBuilder.licensesGraph(route: String) { @@ -103,48 +99,35 @@ fun NoticePage(index: Int) { PreferenceLayout( label = ossLibrary?.name ?: stringResource(id = R.string.loading) ) { - Crossfade(targetState = data) { it -> - if (it != null) { - val uriHandler = LocalUriHandler.current - val layoutResult = remember { mutableStateOf(null) } - val pressIndicator = Modifier.pointerInput(Unit) { - detectTapGestures { pos -> - layoutResult.value?.let { layoutResult -> - val position = layoutResult.getOffsetForPosition(pos) - val annotation = - it.notice.getStringAnnotations(position, position).firstOrNull() - if (annotation?.tag == "URL") { - uriHandler.openUri(annotation.item) - } + Crossfade(targetState = data, label = "") { it -> + it ?: return@Crossfade + val uriHandler = LocalUriHandler.current + val layoutResult = remember { mutableStateOf(null) } + val pressIndicator = Modifier.pointerInput(Unit) { + detectTapGestures { pos -> + layoutResult.value?.let { layoutResult -> + val position = layoutResult.getOffsetForPosition(pos) + val annotation = + it.notice.getStringAnnotations(position, position).firstOrNull() + if (annotation?.tag == "URL") { + uriHandler.openUri(annotation.item) } } } - - Text( - modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 16.dp) - .then(pressIndicator), - text = it.notice, - fontFamily = FontFamily.Monospace, - fontSize = 14.sp, - onTextLayout = { - layoutResult.value = it - } - ) - } else { - Text( - modifier = Modifier - .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 16.dp) - .placeholder( - visible = true, - highlight = PlaceholderHighlight.fade(), - ), - text = "a".repeat(ossLibrary?.noticeLength ?: 20), - fontFamily = FontFamily.Monospace, - fontSize = 14.sp - ) } + + Text( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 16.dp) + .then(pressIndicator), + text = it.notice, + fontFamily = FontFamily.Monospace, + fontSize = 14.sp, + onTextLayout = { + layoutResult.value = it + } + ) } } } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/about/acknowledgements/loadNotice.kt b/lawnchair/src/app/lawnchair/ui/preferences/about/acknowledgements/loadNotice.kt index 4b80d9e02af..575b593a1db 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/about/acknowledgements/loadNotice.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/about/acknowledgements/loadNotice.kt @@ -20,22 +20,24 @@ import android.text.SpannableString import android.text.style.URLSpan import android.text.util.Linkify import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.* -import androidx.compose.ui.platform.LocalContext +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextDecoration -import app.lawnchair.ossnotices.OssLibrary -import com.android.launcher3.R +import kotlinx.serialization.Serializable @Composable fun loadNotice(ossLibrary: OssLibrary): State { - val context = LocalContext.current val noticeStringState = remember { mutableStateOf(null) } val accentColor = MaterialTheme.colorScheme.primary DisposableEffect(Unit) { - val string = ossLibrary.getNotice(context = context, thirdPartyLicensesId = R.raw.third_party_licenses) + val string = (ossLibrary.spdxLicenses ?: ossLibrary.unknownLicenses) + ?.firstOrNull()?.url.orEmpty() val spannable = SpannableString(string) Linkify.addLinks(spannable, Linkify.WEB_URLS) val spans = spannable.getSpans(0, string.length, URLSpan::class.java) @@ -66,6 +68,27 @@ fun loadNotice(ossLibrary: OssLibrary): State { return noticeStringState } +@Serializable +data class OssLibrary( + val groupId: String, + val artifactId: String, + val version: String, + val name: String, + val scm: Scm? = null, + val spdxLicenses: List? = null, + val unknownLicenses: List? = null, +) { + @Serializable + data class License( + val identifier: String? = null, + val name: String, + val url: String, + ) + + @Serializable + data class Scm(val url: String) +} + data class OssLibraryWithNotice( val ossLibrary: OssLibrary, val notice: AnnotatedString,