--- a/settings.gradle.kts Sun Apr 16 15:04:57 2023 -0400
+++ b/settings.gradle.kts Tue May 02 15:06:53 2023 -0400
@@ -21,3 +21,4 @@
include(":core")
include(":ui:common")
include(":ui:material2")
+include(":ui:material3")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/material3/.gitignore Tue May 02 15:06:53 2023 -0400
@@ -0,0 +1,1 @@
+/build
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/material3/build.gradle.kts Tue May 02 15:06:53 2023 -0400
@@ -0,0 +1,115 @@
+/*
+ * AboutOss is an 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/>.
+ */
+plugins {
+ id("com.android.library")
+ kotlin("android")
+ id("com.geekorum.build.source-license-checker")
+ `maven-publish`
+}
+
+group = "com.geekorum.aboutoss"
+version = "0.0.1"
+
+android {
+ namespace = "com.geekorum.aboutoss.ui.material3"
+ compileSdk = 33
+
+ defaultConfig {
+ minSdk = 24
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+
+ aarMetadata {
+ minCompileSdk = 24
+ }
+ }
+
+ 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()
+ }
+
+ publishing {
+ singleVariant("release") {
+ withJavadocJar()
+ withSourcesJar()
+ }
+ }
+}
+
+dependencies {
+ implementation(project(":ui:common"))
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.compose.material3)
+ implementation(libs.androidx.activity.compose)
+ implementation(libs.androidx.navigation.compose)
+
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.test.ext.junit)
+ androidTestImplementation(libs.espresso.core)
+}
+
+publishing {
+ publications {
+ val pomConfiguration: (MavenPom).() -> Unit = {
+ name.set("ui-material3")
+ description.set("A library to retrieve and display opensource licenses in Android applications")
+ licenses {
+ license {
+ name.set("GPL-3.0-or-later")
+ url.set("https://www.gnu.org/licenses/gpl-3.0.html")
+ distribution.set("repo")
+ }
+ }
+ inceptionYear.set("2023")
+ }
+
+ register<MavenPublication>("release") {
+ afterEvaluate {
+ from(components["release"])
+ }
+ artifactId = "ui-material3"
+ pom(pomConfiguration)
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/material3/proguard-rules.pro Tue May 02 15:06:53 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/material3/src/androidTest/java/com/geekorum/aboutoss/ui/material3/ExampleInstrumentedTest.kt Tue May 02 15:06:53 2023 -0400
@@ -0,0 +1,45 @@
+/*
+ * AboutOss is an 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.material3
+
+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.material3.test", appContext.packageName)
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/material3/src/main/AndroidManifest.xml Tue May 02 15:06:53 2023 -0400
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ AboutOss is an 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/>.
+
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <application>
+ <activity android:name=".OpenSourceLicensesActivity"
+ android:theme="@style/Theme.AppCompat.NoActionBar"
+ android:label="@string/title_oss_licenses"
+ android:exported="false"
+ />
+ </application>
+</manifest>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/material3/src/main/java/com/geekorum/aboutoss/ui/material3/OpenSourceDependenciesListScreen.kt Tue May 02 15:06:53 2023 -0400
@@ -0,0 +1,116 @@
+/*
+ * AboutOss is an 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.material3
+
+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.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+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
+
+/**
+ * Display the list of dependencies used in the application
+ *
+ * @param viewModel the [OpenSourceLicensesViewModel] to use
+ * @param onDependencyClick lambda to execute on click on one dependency item
+ * @param onUpClick lambda to execute on click on the up arrow
+ */
+@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
+ )
+}
+
+/**
+ * Display the list of dependencies used in the application
+ *
+ * @param dependencies the list of dependencies
+ * @param onDependencyClick lambda to execute on click on one dependency item
+ * @param onUpClick lambda to execute on click on the up arrow
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun OpenSourceDependenciesListScreen(
+ dependencies: List<String>,
+ onDependencyClick: (String) -> Unit,
+ onUpClick: () -> Unit
+) {
+ val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
+ Scaffold(
+ modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
+ topBar = {
+ TopAppBar(
+ title = { Text(stringResource(commonR.string.title_oss_licenses)) },
+ navigationIcon = {
+ IconButton(onClick = onUpClick) {
+ Icon(
+ Icons.Default.ArrowBack,
+ contentDescription = null
+ )
+ }
+ },
+ scrollBehavior = scrollBehavior
+ )
+ }) {
+ LazyColumn(Modifier.fillMaxSize(), contentPadding = it) {
+ items(dependencies) {
+ Column {
+ ListItem(
+ modifier = Modifier
+ .height(64.dp)
+ .clickable(onClick = { onDependencyClick(it) }),
+ headlineContent = {
+ 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/material3/src/main/java/com/geekorum/aboutoss/ui/material3/OpenSourceLicenseScreen.kt Tue May 02 15:06:53 2023 -0400
@@ -0,0 +1,173 @@
+/*
+ * AboutOss is an 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.material3
+
+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.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.*
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.core.net.toUri
+import com.geekorum.aboutoss.ui.common.OpenSourceLicensesViewModel
+
+/**
+ * Display the opensource license of a dependency
+ *
+ * @param viewModel the [OpenSourceLicensesViewModel] to use
+ * @param dependency the dependency
+ * @param onUpClick lambda to execute on click on the up arrow
+ */
+@Composable
+fun OpenSourceLicenseScreen(
+ viewModel: OpenSourceLicensesViewModel,
+ dependency: String,
+ onUpClick: () -> Unit,
+) {
+ val context = LocalContext.current
+ val license by viewModel.getLicenseDependency(dependency).collectAsState("")
+ OpenSourceLicenseScreen(
+ dependency = dependency,
+ license = license,
+ onUpClick = onUpClick,
+ onUrlClick = {
+ viewModel.openLinkInBrowser(context, it)
+ },
+ onUrlsFound = {
+ val uris = it.map { uri -> uri.toUri() }
+ viewModel.mayLaunchUrl(*uris.toTypedArray())
+ }
+ )
+}
+
+/**
+ * Display the opensource license of a dependency
+ *
+ * @param dependency the dependency
+ * @param license the opensource license text
+ * @param onUpClick lambda to execute on click on the up arrow
+ * @param onUrlClick lambda to execute on click on a url
+ * @param onUrlsFound lambda to execute when all urls in the license have been found
+ */
+@OptIn(ExperimentalLayoutApi::class, ExperimentalTextApi::class, ExperimentalMaterial3Api::class)
+@Composable
+fun OpenSourceLicenseScreen(
+ dependency: String,
+ license: String,
+ onUpClick: () -> 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 scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
+ Scaffold(
+ modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
+ topBar = {
+ TopAppBar(title = { Text(dependency, overflow = TextOverflow.Ellipsis, maxLines = 1) },
+ navigationIcon = {
+ IconButton(onClick = onUpClick) {
+ Icon(
+ Icons.Default.ArrowBack,
+ contentDescription = null
+ )
+ }
+ },
+ )
+ }) { 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(paddingValues)
+ .consumeWindowInsets(paddingValues)
+ .padding(horizontal = 16.dp)
+ .fillMaxSize()
+ .then(pressIndicator)
+ .verticalScroll(scrollState),
+ 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.colorScheme.primary,
+ 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/material3/src/main/java/com/geekorum/aboutoss/ui/material3/OpenSourceLicensesActivity.kt Tue May 02 15:06:53 2023 -0400
@@ -0,0 +1,115 @@
+/*
+ * AboutOss is an 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.material3
+
+import android.app.Activity
+import android.net.Uri
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalView
+import androidx.core.view.WindowCompat
+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
+
+/**
+ * Activity to display opensource license information
+ *
+ * This activity use Material compose to create the UI.
+ * You can specify the Material theme to use by setting [themeProvider]
+ * before launching the activity
+ */
+class OpenSourceLicensesActivity : BaseOpensourceLicenseActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ WindowCompat.setDecorFitsSystemWindows(window, false)
+ setContent {
+ themeProvider {
+ DependencyNavHost(
+ openSourceLicensesViewModel = viewModel,
+ navigateUp = {
+ if (!onNavigateUp()) {
+ finish()
+ }
+ }
+ )
+ }
+ }
+ }
+
+ companion object {
+ /**
+ * The composable Theme function to set the theme of the UI in [OpenSourceLicensesActivity]
+ * Default to base material theme [MaterialTheme]
+ */
+ var themeProvider: @Composable (@Composable () -> Unit) -> Unit = { content ->
+ val darkTheme: Boolean = isSystemInDarkTheme()
+ val colorScheme = MaterialTheme.colorScheme
+ val view = LocalView.current
+ if (!view.isInEditMode) {
+ SideEffect {
+ val window = (view.context as Activity).window
+ window.statusBarColor = colorScheme.primary.toArgb()
+ WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
+ }
+ }
+ MaterialTheme(content = content)
+ }
+ }
+}
+
+@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,
+ onUpClick = {
+ navController.popBackStack()
+ },
+ )
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/material3/src/test/java/com/geekorum/aboutoss/ui/material3/ExampleUnitTest.kt Tue May 02 15:06:53 2023 -0400
@@ -0,0 +1,37 @@
+/*
+ * AboutOss is an 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.material3
+
+import org.junit.Assert
+import org.junit.Test
+
+/**
+ * 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() {
+ Assert.assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file