# HG changeset patch # User Da Risk # Date 1681421833 14400 # Node ID 3d88feb1156b64a0bfa3496edb43bcd4bcb1ead3 # Parent e2e4636c1e70a16feaf8303b0ff0eab1fa4a0c8f move ui activities to :ui:material2 diff -r e2e4636c1e70 -r 3d88feb1156b settings.gradle.kts --- a/settings.gradle.kts Thu Apr 13 17:28:56 2023 -0400 +++ b/settings.gradle.kts Thu Apr 13 17:37:13 2023 -0400 @@ -19,5 +19,5 @@ rootProject.name = "AboutOss" include(":core") -include(":ui") include(":ui:common") +include(":ui:material2") diff -r e2e4636c1e70 -r 3d88feb1156b ui/build.gradle.kts --- a/ui/build.gradle.kts Thu Apr 13 17:28:56 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -plugins { - id("com.android.library") - kotlin("android") - id("com.geekorum.build.source-license-checker") -} - -android { - namespace = "com.geekorum.aboutoss.ui" - compileSdk = 33 - - defaultConfig { - minSdk = 28 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = "1.8" - } - - buildFeatures { - compose = true - } - - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() - } -} - -dependencies { - implementation(project(":ui:common")) - implementation(platform(libs.androidx.compose.bom)) - implementation(libs.androidx.compose.material) - implementation(libs.androidx.lifecycle.viewmodel) - implementation(libs.androidx.activity) - implementation(libs.androidx.activity.compose) - implementation(libs.androidx.navigation.compose) - implementation(libs.geekdroid) { - //TODO get rid of dagger platform in geekdroid - exclude("com.google.dagger", "dagger-platform") - } - - implementation(libs.appcompat) - testImplementation(libs.junit) - androidTestImplementation(libs.androidx.test.ext.junit) - androidTestImplementation(libs.espresso.core) -} \ No newline at end of file diff -r e2e4636c1e70 -r 3d88feb1156b ui/common/build.gradle.kts --- a/ui/common/build.gradle.kts Thu Apr 13 17:28:56 2023 -0400 +++ b/ui/common/build.gradle.kts Thu Apr 13 17:37:13 2023 -0400 @@ -35,7 +35,7 @@ dependencies { implementation(project(":core")) - implementation(libs.appcompat) + api(libs.appcompat) implementation(libs.androidx.lifecycle.viewmodel) implementation(libs.androidx.activity) implementation(libs.geekdroid) { diff -r e2e4636c1e70 -r 3d88feb1156b ui/consumer-rules.pro diff -r e2e4636c1e70 -r 3d88feb1156b ui/material2/build.gradle.kts --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/material2/build.gradle.kts Thu Apr 13 17:37:13 2023 -0400 @@ -0,0 +1,54 @@ +plugins { + id("com.android.library") + kotlin("android") + id("com.geekorum.build.source-license-checker") +} + +android { + namespace = "com.geekorum.aboutoss.ui.material" + compileSdk = 33 + + defaultConfig { + minSdk = 28 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + + buildFeatures { + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get() + } +} + +dependencies { + implementation(project(":ui:common")) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.compose.material) + implementation(libs.androidx.activity.compose) + implementation(libs.androidx.navigation.compose) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff -r e2e4636c1e70 -r 3d88feb1156b ui/material2/consumer-rules.pro diff -r e2e4636c1e70 -r 3d88feb1156b ui/material2/proguard-rules.pro --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/material2/proguard-rules.pro Thu Apr 13 17:37:13 2023 -0400 @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff -r e2e4636c1e70 -r 3d88feb1156b ui/material2/src/androidTest/java/com/geekorum/aboutoss/ui/material/ExampleInstrumentedTest.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/material2/src/androidTest/java/com/geekorum/aboutoss/ui/material/ExampleInstrumentedTest.kt Thu Apr 13 17:37:13 2023 -0400 @@ -0,0 +1,45 @@ +/* + * AboutOss is a utility library to retrieve and display + * opensource licenses in Android applications. + * + * Copyright (C) 2023 by Frederic-Charles Barthelery. + * + * This file is part of AboutOss. + * + * AboutOss is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AboutOss is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AboutOss. If not, see . + */ +package com.geekorum.aboutoss.ui.material + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.geekorum.aboutoss.ui.material.test", appContext.packageName) + } +} \ No newline at end of file diff -r e2e4636c1e70 -r 3d88feb1156b ui/material2/src/main/java/com/geekorum/aboutoss/ui/material/OpenSourceDependenciesListScreen.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/material2/src/main/java/com/geekorum/aboutoss/ui/material/OpenSourceDependenciesListScreen.kt Thu Apr 13 17:37:13 2023 -0400 @@ -0,0 +1,116 @@ +/* + * AboutOss is a utility library to retrieve and display + * opensource licenses in Android applications. + * + * Copyright (C) 2023 by Frederic-Charles Barthelery. + * + * This file is part of AboutOss. + * + * AboutOss is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AboutOss is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AboutOss. If not, see . + */ +package com.geekorum.aboutoss.ui.material + +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.Divider +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.ListItem +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.geekorum.aboutoss.ui.common.OpenSourceLicensesViewModel +import com.geekorum.aboutoss.ui.common.R as commonR + +@Composable +fun OpenSourceDependenciesListScreen( + viewModel: OpenSourceLicensesViewModel, + onDependencyClick: (String) -> Unit, + onUpClick: () -> Unit +) { + val dependencies by viewModel.dependenciesList.collectAsState(initial = emptyList()) + OpenSourceDependenciesListScreen( + dependencies = dependencies, + onDependencyClick = onDependencyClick, + onUpClick = onUpClick + ) +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun OpenSourceDependenciesListScreen( + dependencies: List, + onDependencyClick: (String) -> Unit, + onUpClick: () -> Unit +) { + val lazyListState = rememberLazyListState() + val hasScrolled by remember { + derivedStateOf { + lazyListState.firstVisibleItemIndex != 0 || lazyListState.firstVisibleItemScrollOffset > 0 + } + } + val topBarElevation by animateDpAsState( + if (hasScrolled) 4.dp else 0.dp + ) + Scaffold(topBar = { + TopAppBar(title = { Text(stringResource(commonR.string.title_oss_licenses)) }, + navigationIcon = { + IconButton(onClick = onUpClick) { + Icon( + Icons.Default.ArrowBack, + contentDescription = null + ) + } + }, + elevation = topBarElevation + ) + }) { + LazyColumn(Modifier.fillMaxSize(), state = lazyListState, contentPadding = it) { + items(dependencies) { + Column { + ListItem( + Modifier + .height(64.dp) + .clickable(onClick = { onDependencyClick(it) }) + ) { + Text( + it, modifier = Modifier.padding(horizontal = 16.dp), + overflow = TextOverflow.Ellipsis, maxLines = 1 + ) + } + Divider(Modifier.padding(horizontal = 16.dp)) + } + } + } + } +} diff -r e2e4636c1e70 -r 3d88feb1156b ui/material2/src/main/java/com/geekorum/aboutoss/ui/material/OpenSourceLicenseScreen.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/material2/src/main/java/com/geekorum/aboutoss/ui/material/OpenSourceLicenseScreen.kt Thu Apr 13 17:37:13 2023 -0400 @@ -0,0 +1,178 @@ +/* + * AboutOss is a utility library to retrieve and display + * opensource licenses in Android applications. + * + * Copyright (C) 2023 by Frederic-Charles Barthelery. + * + * This file is part of AboutOss. + * + * AboutOss is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AboutOss is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AboutOss. If not, see . + */ +package com.geekorum.aboutoss.ui.material + +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.ExperimentalTextApi +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.text.UrlAnnotation +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withAnnotation +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.dp +import androidx.core.net.toUri +import com.geekorum.aboutoss.ui.common.OpenSourceLicensesViewModel + +@Composable +fun OpenSourceLicenseScreen( + viewModel: OpenSourceLicensesViewModel, + dependency: String, + onBackClick: () -> Unit, +) { + val context = LocalContext.current + val license by viewModel.getLicenseDependency(dependency).collectAsState("") + OpenSourceLicenseScreen( + dependency = dependency, + license = license, + onBackClick = onBackClick, + onUrlClick = { + viewModel.openLinkInBrowser(context, it) + }, + onUrlsFound = { + val uris = it.map { uri -> uri.toUri() } + viewModel.mayLaunchUrl(*uris.toTypedArray()) + } + ) +} + +@OptIn(ExperimentalLayoutApi::class, ExperimentalTextApi::class) +@Composable +fun OpenSourceLicenseScreen( + dependency: String, + license: String, + onBackClick: () -> Unit, + onUrlClick: (String) -> Unit, + onUrlsFound: (List) -> Unit, +) { + val linkifiedLicense = linkifyText(text = license) + LaunchedEffect(linkifiedLicense) { + val uris = + linkifiedLicense.getUrlAnnotations(0, linkifiedLicense.length).map { it.item.url } + onUrlsFound(uris) + } + + val scrollState = rememberScrollState() + val hasScrolled by remember { + derivedStateOf { scrollState.value > 0 } + } + val topBarElevation by animateDpAsState( + if (hasScrolled) 4.dp else 0.dp + ) + Scaffold(topBar = { + TopAppBar(title = { Text(dependency, overflow = TextOverflow.Ellipsis, maxLines = 1) }, + navigationIcon = { + IconButton(onClick = onBackClick) { + Icon( + Icons.Default.ArrowBack, + contentDescription = null + ) + } + }, + elevation = topBarElevation + ) + }) { paddingValues -> + val layoutResult = remember { mutableStateOf(null) } + val pressIndicator = Modifier.pointerInput(layoutResult, linkifiedLicense) { + detectTapGestures { pos -> + layoutResult.value?.let { layoutResult -> + val posWithScroll = pos.copy(y = pos.y + scrollState.value) + val offset = layoutResult.getOffsetForPosition(posWithScroll) + linkifiedLicense.getUrlAnnotations(start = offset, end = offset) + .firstOrNull()?.let { annotation -> + onUrlClick(annotation.item.url) + } + } + } + } + + Text(linkifiedLicense, + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxSize() + .then(pressIndicator) + .verticalScroll(scrollState) + .padding(paddingValues) + .consumeWindowInsets(paddingValues), + onTextLayout = { + layoutResult.value = it + } + ) + } +} + +/** + * https://regexr.com/37i6s + */ +private val UrlRegexp = """https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)""".toRegex() + +@OptIn(ExperimentalTextApi::class) +@Composable +private fun linkifyText(text: String): AnnotatedString { + val style = SpanStyle( + color = MaterialTheme.colors.secondary, + textDecoration = TextDecoration.Underline + ) + return remember(text, style) { + buildAnnotatedString { + var currentIdx = 0 + for (match in UrlRegexp.findAll(text)) { + if (currentIdx < match.range.first) { + append(text.substring(currentIdx, match.range.first)) + } + val url = text.substring(match.range) + withAnnotation(UrlAnnotation(url)) { + withStyle(style) { + append(url) + } + } + currentIdx = match.range.last + 1 + } + append(text.substring(currentIdx)) + } + } +} diff -r e2e4636c1e70 -r 3d88feb1156b ui/material2/src/main/java/com/geekorum/aboutoss/ui/material/OpenSourceLicensesActivity.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/material2/src/main/java/com/geekorum/aboutoss/ui/material/OpenSourceLicensesActivity.kt Thu Apr 13 17:37:13 2023 -0400 @@ -0,0 +1,81 @@ +/* + * AboutOss is a utility library to retrieve and display + * opensource licenses in Android applications. + * + * Copyright (C) 2023 by Frederic-Charles Barthelery. + * + * This file is part of AboutOss. + * + * AboutOss is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AboutOss is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AboutOss. If not, see . + */ +package com.geekorum.aboutoss.ui.material + +import android.net.Uri +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import com.geekorum.aboutoss.ui.common.BaseOpensourceLicenseActivity +import com.geekorum.aboutoss.ui.common.OpenSourceLicensesViewModel + +class OpenSourceLicensesActivity : BaseOpensourceLicenseActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + MaterialTheme { + DependencyNavHost( + openSourceLicensesViewModel = viewModel, + navigateUp = { + onNavigateUp() + } + ) + } + } + } +} + + +@Composable +fun DependencyNavHost( + openSourceLicensesViewModel: OpenSourceLicensesViewModel, + navigateUp: () -> Unit +) { + val navController = rememberNavController() + NavHost(navController, startDestination = "dependencies") { + composable("dependencies") { + OpenSourceDependenciesListScreen( + viewModel = openSourceLicensesViewModel, + onDependencyClick = { + navController.navigate("dependency_license/${Uri.encode(it)}") + }, + onUpClick = navigateUp + ) + } + composable("dependency_license/{dependency}") { + val dependency = requireNotNull(it.arguments?.getString("dependency")) + OpenSourceLicenseScreen( + viewModel = openSourceLicensesViewModel, + dependency = dependency, + onBackClick = { + navController.popBackStack() + }, + ) + } + } +} + diff -r e2e4636c1e70 -r 3d88feb1156b ui/material2/src/test/java/com/geekorum/aboutoss/ui/material/ExampleUnitTest.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/material2/src/test/java/com/geekorum/aboutoss/ui/material/ExampleUnitTest.kt Thu Apr 13 17:37:13 2023 -0400 @@ -0,0 +1,38 @@ +/* + * AboutOss is a utility library to retrieve and display + * opensource licenses in Android applications. + * + * Copyright (C) 2023 by Frederic-Charles Barthelery. + * + * This file is part of AboutOss. + * + * AboutOss is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AboutOss is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AboutOss. If not, see . + */ +package com.geekorum.aboutoss.ui.material + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff -r e2e4636c1e70 -r 3d88feb1156b ui/proguard-rules.pro --- a/ui/proguard-rules.pro Thu Apr 13 17:28:56 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff -r e2e4636c1e70 -r 3d88feb1156b ui/src/androidTest/java/com/geekorum/aboutoss/ui/ExampleInstrumentedTest.kt --- a/ui/src/androidTest/java/com/geekorum/aboutoss/ui/ExampleInstrumentedTest.kt Thu Apr 13 17:28:56 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * AboutOss is a utility library to retrieve and display - * opensource licenses in Android applications. - * - * Copyright (C) 2023 by Frederic-Charles Barthelery. - * - * This file is part of AboutOss. - * - * AboutOss is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AboutOss is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AboutOss. If not, see . - */ -package com.geekorum.aboutoss.ui - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.geekorum.aboutoss.ui.test", appContext.packageName) - } -} \ No newline at end of file diff -r e2e4636c1e70 -r 3d88feb1156b ui/src/main/java/com/geekorum/aboutoss/ui/OpenSourceDependenciesListScreen.kt --- a/ui/src/main/java/com/geekorum/aboutoss/ui/OpenSourceDependenciesListScreen.kt Thu Apr 13 17:28:56 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -/* - * AboutOss is a utility library to retrieve and display - * opensource licenses in Android applications. - * - * Copyright (C) 2023 by Frederic-Charles Barthelery. - * - * This file is part of AboutOss. - * - * AboutOss is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AboutOss is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AboutOss. If not, see . - */ -package com.geekorum.aboutoss.ui - -import androidx.compose.animation.core.animateDpAsState -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material.Divider -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.ListItem -import androidx.compose.material.Scaffold -import androidx.compose.material.Text -import androidx.compose.material.TopAppBar -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import com.geekorum.aboutoss.ui.common.OpenSourceLicensesViewModel -import com.geekorum.aboutoss.ui.common.R as commonR - -@Composable -fun OpenSourceDependenciesListScreen( - viewModel: OpenSourceLicensesViewModel, - onDependencyClick: (String) -> Unit, - onUpClick: () -> Unit -) { - val dependencies by viewModel.dependenciesList.collectAsState(initial = emptyList()) - OpenSourceDependenciesListScreen( - dependencies = dependencies, - onDependencyClick = onDependencyClick, - onUpClick = onUpClick - ) -} - -@OptIn(ExperimentalMaterialApi::class) -@Composable -fun OpenSourceDependenciesListScreen( - dependencies: List, - onDependencyClick: (String) -> Unit, - onUpClick: () -> Unit -) { - val lazyListState = rememberLazyListState() - val hasScrolled by remember { - derivedStateOf { - lazyListState.firstVisibleItemIndex != 0 || lazyListState.firstVisibleItemScrollOffset > 0 - } - } - val topBarElevation by animateDpAsState( - if (hasScrolled) 4.dp else 0.dp - ) - Scaffold(topBar = { - TopAppBar(title = { Text(stringResource(commonR.string.title_oss_licenses)) }, - navigationIcon = { - IconButton(onClick = onUpClick) { - Icon( - Icons.Default.ArrowBack, - contentDescription = null - ) - } - }, - elevation = topBarElevation - ) - }) { - LazyColumn(Modifier.fillMaxSize(), state = lazyListState, contentPadding = it) { - items(dependencies) { - Column { - ListItem( - Modifier - .height(64.dp) - .clickable(onClick = { onDependencyClick(it) }) - ) { - Text( - it, modifier = Modifier.padding(horizontal = 16.dp), - overflow = TextOverflow.Ellipsis, maxLines = 1 - ) - } - Divider(Modifier.padding(horizontal = 16.dp)) - } - } - } - } -} diff -r e2e4636c1e70 -r 3d88feb1156b ui/src/main/java/com/geekorum/aboutoss/ui/OpenSourceLicenseScreen.kt --- a/ui/src/main/java/com/geekorum/aboutoss/ui/OpenSourceLicenseScreen.kt Thu Apr 13 17:28:56 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,178 +0,0 @@ -/* - * AboutOss is a utility library to retrieve and display - * opensource licenses in Android applications. - * - * Copyright (C) 2023 by Frederic-Charles Barthelery. - * - * This file is part of AboutOss. - * - * AboutOss is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AboutOss is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AboutOss. If not, see . - */ -package com.geekorum.aboutoss.ui - -import androidx.compose.animation.core.animateDpAsState -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold -import androidx.compose.material.Text -import androidx.compose.material.TopAppBar -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.ExperimentalTextApi -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.TextLayoutResult -import androidx.compose.ui.text.UrlAnnotation -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.text.withAnnotation -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.unit.dp -import androidx.core.net.toUri -import com.geekorum.aboutoss.ui.common.OpenSourceLicensesViewModel - -@Composable -fun OpenSourceLicenseScreen( - viewModel: OpenSourceLicensesViewModel, - dependency: String, - onBackClick: () -> Unit, -) { - val context = LocalContext.current - val license by viewModel.getLicenseDependency(dependency).collectAsState("") - OpenSourceLicenseScreen( - dependency = dependency, - license = license, - onBackClick = onBackClick, - onUrlClick = { - viewModel.openLinkInBrowser(context, it) - }, - onUrlsFound = { - val uris = it.map { uri -> uri.toUri() } - viewModel.mayLaunchUrl(*uris.toTypedArray()) - } - ) -} - -@OptIn(ExperimentalLayoutApi::class, ExperimentalTextApi::class) -@Composable -fun OpenSourceLicenseScreen( - dependency: String, - license: String, - onBackClick: () -> Unit, - onUrlClick: (String) -> Unit, - onUrlsFound: (List) -> Unit, -) { - val linkifiedLicense = linkifyText(text = license) - LaunchedEffect(linkifiedLicense) { - val uris = - linkifiedLicense.getUrlAnnotations(0, linkifiedLicense.length).map { it.item.url } - onUrlsFound(uris) - } - - val scrollState = rememberScrollState() - val hasScrolled by remember { - derivedStateOf { scrollState.value > 0 } - } - val topBarElevation by animateDpAsState( - if (hasScrolled) 4.dp else 0.dp - ) - Scaffold(topBar = { - TopAppBar(title = { Text(dependency, overflow = TextOverflow.Ellipsis, maxLines = 1) }, - navigationIcon = { - IconButton(onClick = onBackClick) { - Icon( - Icons.Default.ArrowBack, - contentDescription = null - ) - } - }, - elevation = topBarElevation - ) - }) { paddingValues -> - val layoutResult = remember { mutableStateOf(null) } - val pressIndicator = Modifier.pointerInput(layoutResult, linkifiedLicense) { - detectTapGestures { pos -> - layoutResult.value?.let { layoutResult -> - val posWithScroll = pos.copy(y = pos.y + scrollState.value) - val offset = layoutResult.getOffsetForPosition(posWithScroll) - linkifiedLicense.getUrlAnnotations(start = offset, end = offset) - .firstOrNull()?.let { annotation -> - onUrlClick(annotation.item.url) - } - } - } - } - - Text(linkifiedLicense, - modifier = Modifier - .padding(horizontal = 16.dp) - .fillMaxSize() - .then(pressIndicator) - .verticalScroll(scrollState) - .padding(paddingValues) - .consumeWindowInsets(paddingValues), - onTextLayout = { - layoutResult.value = it - } - ) - } -} - -/** - * https://regexr.com/37i6s - */ -private val UrlRegexp = """https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)""".toRegex() - -@OptIn(ExperimentalTextApi::class) -@Composable -private fun linkifyText(text: String): AnnotatedString { - val style = SpanStyle( - color = MaterialTheme.colors.secondary, - textDecoration = TextDecoration.Underline - ) - return remember(text, style) { - buildAnnotatedString { - var currentIdx = 0 - for (match in UrlRegexp.findAll(text)) { - if (currentIdx < match.range.first) { - append(text.substring(currentIdx, match.range.first)) - } - val url = text.substring(match.range) - withAnnotation(UrlAnnotation(url)) { - withStyle(style) { - append(url) - } - } - currentIdx = match.range.last + 1 - } - append(text.substring(currentIdx)) - } - } -} diff -r e2e4636c1e70 -r 3d88feb1156b ui/src/main/java/com/geekorum/aboutoss/ui/OpenSourceLicensesActivity.kt --- a/ui/src/main/java/com/geekorum/aboutoss/ui/OpenSourceLicensesActivity.kt Thu Apr 13 17:28:56 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/* - * AboutOss is a utility library to retrieve and display - * opensource licenses in Android applications. - * - * Copyright (C) 2023 by Frederic-Charles Barthelery. - * - * This file is part of AboutOss. - * - * AboutOss is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AboutOss is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AboutOss. If not, see . - */ -package com.geekorum.aboutoss.ui - -import android.net.Uri -import android.os.Bundle -import androidx.activity.compose.setContent -import androidx.compose.material.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.rememberNavController -import com.geekorum.aboutoss.ui.common.BaseOpensourceLicenseActivity -import com.geekorum.aboutoss.ui.common.OpenSourceLicensesViewModel - -class OpenSourceLicensesActivity : BaseOpensourceLicenseActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContent { - MaterialTheme { - DependencyNavHost( - openSourceLicensesViewModel = viewModel, - navigateUp = { - onNavigateUp() - } - ) - } - } - } -} - - -@Composable -fun DependencyNavHost( - openSourceLicensesViewModel: OpenSourceLicensesViewModel, - navigateUp: () -> Unit -) { - val navController = rememberNavController() - NavHost(navController, startDestination = "dependencies") { - composable("dependencies") { - OpenSourceDependenciesListScreen( - viewModel = openSourceLicensesViewModel, - onDependencyClick = { - navController.navigate("dependency_license/${Uri.encode(it)}") - }, - onUpClick = navigateUp - ) - } - composable("dependency_license/{dependency}") { - val dependency = requireNotNull(it.arguments?.getString("dependency")) - OpenSourceLicenseScreen( - viewModel = openSourceLicensesViewModel, - dependency = dependency, - onBackClick = { - navController.popBackStack() - }, - ) - } - } -} - diff -r e2e4636c1e70 -r 3d88feb1156b ui/src/test/java/com/geekorum/aboutoss/ui/ExampleUnitTest.kt --- a/ui/src/test/java/com/geekorum/aboutoss/ui/ExampleUnitTest.kt Thu Apr 13 17:28:56 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/* - * AboutOss is a utility library to retrieve and display - * opensource licenses in Android applications. - * - * Copyright (C) 2023 by Frederic-Charles Barthelery. - * - * This file is part of AboutOss. - * - * AboutOss is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AboutOss is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AboutOss. If not, see . - */ -package com.geekorum.aboutoss.ui - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file