--- 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")
--- 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
--- 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) {
--- /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
--- /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
--- /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 <http://www.gnu.org/licenses/>.
+ */
+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
--- /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 <http://www.gnu.org/licenses/>.
+ */
+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<String>,
+    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))
+                }
+            }
+        }
+    }
+}
--- /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 <http://www.gnu.org/licenses/>.
+ */
+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<String>) -> 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<TextLayoutResult?>(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))
+        }
+    }
+}
--- /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 <http://www.gnu.org/licenses/>.
+ */
+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()
+                },
+            )
+        }
+    }
+}
+
--- /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 <http://www.gnu.org/licenses/>.
+ */
+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
--- 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
--- 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 <http://www.gnu.org/licenses/>.
- */
-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
--- 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 <http://www.gnu.org/licenses/>.
- */
-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<String>,
-    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))
-                }
-            }
-        }
-    }
-}
--- 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 <http://www.gnu.org/licenses/>.
- */
-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<String>) -> 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<TextLayoutResult?>(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))
-        }
-    }
-}
--- 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 <http://www.gnu.org/licenses/>.
- */
-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()
-                },
-            )
-        }
-    }
-}
-
--- 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 <http://www.gnu.org/licenses/>.
- */
-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