diff --git a/i18n/src/main/res/values-de/strings.xml b/i18n/src/main/res/values-de/strings.xml index 94cfc2e8..ee9f002a 100644 --- a/i18n/src/main/res/values-de/strings.xml +++ b/i18n/src/main/res/values-de/strings.xml @@ -58,7 +58,7 @@ Durchscheinende Karten Debug Debug-Informationen exportieren - Exportiere nach %1$s + Exportiert nach %1$s https://de.wikipedia.org Aus Wikipedia Symbole diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 8e3860d7..a4507594 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -91,7 +91,7 @@ Translucent cards Debug Export debug information - Exporting to %1$s + Exported to %1$s https://en.wikipedia.org From Wikipedia diff --git a/ui/src/main/java/de/mm20/launcher2/ui/activity/ComposeActivity.kt b/ui/src/main/java/de/mm20/launcher2/ui/activity/ComposeActivity.kt index 9e9c9a33..8b84cb02 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/activity/ComposeActivity.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/activity/ComposeActivity.kt @@ -17,6 +17,7 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument import com.google.accompanist.insets.ProvideWindowInsets import de.mm20.launcher2.ui.LauncherTheme import de.mm20.launcher2.ui.locals.LocalAppWidgetHost @@ -24,6 +25,8 @@ import de.mm20.launcher2.ui.locals.LocalColorScheme import de.mm20.launcher2.ui.locals.LocalNavController import de.mm20.launcher2.ui.locals.LocalWindowSize import de.mm20.launcher2.ui.screens.LauncherMainScreen +import de.mm20.launcher2.ui.screens.settings.SettingsAboutScreen +import de.mm20.launcher2.ui.screens.settings.SettingsLicenseScreen import de.mm20.launcher2.ui.screens.settings.SettingsMainScreen import de.mm20.launcher2.ui.theme.WallpaperColors import de.mm20.launcher2.ui.theme.colors.DefaultColorScheme @@ -100,6 +103,17 @@ class ComposeActivity : AppCompatActivity() { composable("settings") { SettingsMainScreen() } + composable("settings/about") { + SettingsAboutScreen() + } + composable( + "settings/license?library={libraryName}", + arguments = listOf(navArgument("libraryName") { + nullable = true + }) + ) { + SettingsLicenseScreen(it.arguments?.getString("libraryName")) + } } } } diff --git a/ui/src/main/java/de/mm20/launcher2/ui/icons/Icons.kt b/ui/src/main/java/de/mm20/launcher2/ui/icons/Icons.kt index f07e8e35..12b69aba 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/icons/Icons.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/icons/Icons.kt @@ -645,4 +645,122 @@ val Icons.Rounded.NotificationBadge arcTo(4f, 4f, 0f, false, false, 18f, 14f) close() } + } + +val Icons.Rounded.GitHub + get() = materialIcon("Icons.Rounded.GitHub") { + materialPath { + moveTo(12f, 2f) + arcTo(10f, 10f, 0f, false, false, 2f, 12f) + curveTo(2f, 16.42f, 4.87f, 20.17f, 8.84f, 21.5f) + curveTo(9.34f, 21.58f, 9.5f, 21.27f, 9.5f, 21f) + curveTo(9.5f, 20.77f, 9.5f, 20.14f, 9.5f, 19.31f) + curveTo(6.73f, 19.91f, 6.14f, 17.97f, 6.14f, 17.97f) + curveTo(5.68f, 16.81f, 5.03f, 16.5f, 5.03f, 16.5f) + curveTo(4.12f, 15.88f, 5.1f, 15.9f, 5.1f, 15.9f) + curveTo(6.1f, 15.97f, 6.63f, 16.93f, 6.63f, 16.93f) + curveTo(7.5f, 18.45f, 8.97f, 18f, 9.54f, 17.76f) + curveTo(9.63f, 17.11f, 9.89f, 16.67f, 10.17f, 16.42f) + curveTo(7.95f, 16.17f, 5.62f, 15.31f, 5.62f, 11.5f) + curveTo(5.62f, 10.39f, 6f, 9.5f, 6.65f, 8.79f) + curveTo(6.55f, 8.54f, 6.2f, 7.5f, 6.75f, 6.15f) + curveTo(6.75f, 6.15f, 7.59f, 5.88f, 9.5f, 7.17f) + curveTo(10.29f, 6.95f, 11.15f, 6.84f, 12f, 6.84f) + curveTo(12.85f, 6.84f, 13.71f, 6.95f, 14.5f, 7.17f) + curveTo(16.41f, 5.88f, 17.25f, 6.15f, 17.25f, 6.15f) + curveTo(17.8f, 7.5f, 17.45f, 8.54f, 17.35f, 8.79f) + curveTo(18f, 9.5f, 18.38f, 10.39f, 18.38f, 11.5f) + curveTo(18.38f, 15.32f, 16.04f, 16.16f, 13.81f, 16.41f) + curveTo(14.17f, 16.72f, 14.5f, 17.33f, 14.5f, 18.26f) + curveTo(14.5f, 19.6f, 14.5f, 20.68f, 14.5f, 21f) + curveTo(14.5f, 21.27f, 14.66f, 21.59f, 15.17f, 21.5f) + curveTo(19.14f, 20.16f, 22f, 16.42f, 22f, 12f) + arcTo(10f, 10f, 0f, false, false, 12f, 2f) + close() + } + } + +val Icons.Rounded.Telegram + get() = materialIcon("Icons.Rounded.Telegram") { + materialPath { + moveTo(9.78f, 18.65f) + lineTo(10.06f, 14.42f) + lineTo(17.74f, 7.5f) + curveTo(18.08f, 7.19f, 17.67f, 7.04f, 17.22f, 7.31f) + lineTo(7.74f, 13.3f) + lineTo(3.64f, 12f) + curveTo(2.76f, 11.75f, 2.75f, 11.14f, 3.84f, 10.7f) + lineTo(19.81f, 4.54f) + curveTo(20.54f, 4.21f, 21.24f, 4.72f, 20.96f, 5.84f) + lineTo(18.24f, 18.65f) + curveTo(18.05f, 19.56f, 17.5f, 19.78f, 16.74f, 19.36f) + lineTo(12.6f, 16.3f) + lineTo(10.61f, 18.23f) + curveTo(10.38f, 18.46f, 10.19f, 18.65f, 9.78f, 18.65f) + close() + } + } + +val Icons.Rounded.Fdroid + get() = materialIcon("Icons.Rounded.Fdroid") { + materialPath { + moveTo(4.4570312f, 3.6269531f) + arcTo(0.44109249f, 0.44109249f, 0f, false, false, 4.1757812f, 3.71875f) + arcTo(0.44109249f, 0.44109249f, 0f, false, false, 4.0957031f, 4.3378906f) + lineTo(5.3691406f, 5.984375f) + curveTo(5.3240789f, 6.102208f, 5.2949219f, 6.2272726f, 5.2949219f, 6.3613281f) + lineTo(5.2949219f, 6.5507812f) + lineTo(5.2949219f, 7.4199219f) + lineTo(5.2949219f, 7.7714844f) + lineTo(5.2949219f, 8.640625f) + lineTo(5.2949219f, 8.8300781f) + curveTo(5.2949219f, 9.4165531f, 5.7670378f, 9.8886719f, 6.3535156f, 9.8886719f) + lineTo(17.646484f, 9.8886719f) + curveTo(18.232962f, 9.8886719f, 18.705078f, 9.4165531f, 18.705078f, 8.8300781f) + lineTo(18.705078f, 8.640625f) + lineTo(18.705078f, 6.5507812f) + lineTo(18.705078f, 6.3613281f) + curveTo(18.705078f, 6.2272726f, 18.675921f, 6.102208f, 18.630859f, 5.984375f) + lineTo(19.904297f, 4.3378906f) + arcTo(0.44109249f, 0.44109249f, 0f, false, false, 19.824219f, 3.71875f) + arcTo(0.44109249f, 0.44109249f, 0f, false, false, 19.566406f, 3.6269531f) + arcTo(0.44109249f, 0.44109249f, 0f, false, false, 19.207031f, 3.796875f) + lineTo(17.990234f, 5.3710938f) + curveTo(17.881129f, 5.3332803f, 17.768779f, 5.3027344f, 17.646484f, 5.3027344f) + lineTo(6.3535156f, 5.3027344f) + curveTo(6.2312214f, 5.3027344f, 6.1188708f, 5.3332803f, 6.0097656f, 5.3710938f) + lineTo(4.7929688f, 3.796875f) + arcTo(0.44109249f, 0.44109249f, 0f, false, false, 4.4570312f, 3.6269531f) + close() + moveTo(8.5664062f, 6.625f) + arcTo(1.1909523f, 1.1909465f, 0f, false, true, 8.6035156f, 6.625f) + arcTo(1.1909523f, 1.1909465f, 0f, false, true, 9.7949219f, 7.8164062f) + arcTo(1.1909523f, 1.1909465f, 0f, false, true, 8.6035156f, 9.0078125f) + arcTo(1.1909523f, 1.1909465f, 0f, false, true, 7.4121094f, 7.8164062f) + arcTo(1.1909523f, 1.1909465f, 0f, false, true, 8.5664062f, 6.625f) + close() + moveTo(15.447266f, 6.625f) + arcTo(1.1909523f, 1.1909465f, 0f, false, true, 15.484375f, 6.625f) + arcTo(1.1909523f, 1.1909465f, 0f, false, true, 16.675781f, 7.8164062f) + arcTo(1.1909523f, 1.1909465f, 0f, false, true, 15.484375f, 9.0078125f) + arcTo(1.1909523f, 1.1909465f, 0f, false, true, 14.292969f, 7.8164062f) + arcTo(1.1909523f, 1.1909465f, 0f, false, true, 15.447266f, 6.625f) + close() + moveTo(6.3535156f, 10.242188f) + curveTo(5.7670378f, 10.242188f, 5.2949219f, 10.714306f, 5.2949219f, 11.300781f) + lineTo(5.2949219f, 11.572266f) + lineTo(5.2949219f, 13.771484f) + lineTo(5.2949219f, 15.888672f) + lineTo(5.2949219f, 18.087891f) + lineTo(5.2949219f, 18.359375f) + curveTo(5.2949219f, 18.94585f, 5.7670378f, 19.417969f, 6.3535156f, 19.417969f) + lineTo(17.646484f, 19.417969f) + curveTo(18.232962f, 19.417969f, 18.705078f, 18.94585f, 18.705078f, 18.359375f) + lineTo(18.705078f, 18.087891f) + lineTo(18.705078f, 11.572266f) + lineTo(18.705078f, 11.300781f) + curveTo(18.705078f, 10.714306f, 18.232962f, 10.242187f, 17.646484f, 10.242188f) + lineTo(6.3535156f, 10.242188f) + close() + } } \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/screens/settings/SettingsAboutScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/screens/settings/SettingsAboutScreen.kt new file mode 100644 index 00000000..da280718 --- /dev/null +++ b/ui/src/main/java/de/mm20/launcher2/ui/screens/settings/SettingsAboutScreen.kt @@ -0,0 +1,173 @@ +package de.mm20.launcher2.ui.screens.settings + +import android.content.Intent +import android.net.Uri +import androidx.compose.material.SnackbarDuration +import androidx.compose.material.SnackbarResult +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Info +import androidx.compose.material.rememberScaffoldState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.core.content.FileProvider +import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.debug.DebugInformationDumper +import de.mm20.launcher2.ktx.tryStartActivity +import de.mm20.launcher2.licenses.OpenSourceLicenses +import de.mm20.launcher2.ui.R +import de.mm20.launcher2.ui.component.preferences.Preference +import de.mm20.launcher2.ui.component.preferences.PreferenceCategory +import de.mm20.launcher2.ui.component.preferences.PreferenceScreen +import de.mm20.launcher2.ui.icons.Fdroid +import de.mm20.launcher2.ui.icons.GitHub +import de.mm20.launcher2.ui.icons.Telegram +import de.mm20.launcher2.ui.locals.LocalNavController +import kotlinx.coroutines.launch +import java.io.File + +@Composable +fun SettingsAboutScreen() { + val context = LocalContext.current + val navController = LocalNavController.current + val scaffoldState = rememberScaffoldState() + val scope = rememberCoroutineScope() + PreferenceScreen( + title = stringResource(id = R.string.preference_screen_about), + scaffoldState = scaffoldState + ) { + item { + PreferenceCategory { + val version = context.packageManager.getPackageInfo( + context.packageName, + 0 + ).versionName + Preference( + title = stringResource(id = R.string.preference_version), + summary = version + ) + } + } + item { + PreferenceCategory(title = stringResource(id = R.string.preference_category_license)) { + Preference( + icon = Icons.Rounded.Info, + title = stringResource(id = R.string.preference_about_license), + summary = stringResource(id = R.string.preference_about_license_summary), + onClick = { + navController?.navigate("settings/license") + } + ) + } + } + item { + PreferenceCategory(title = stringResource(id = R.string.preference_category_links)) { + Preference( + icon = Icons.Rounded.Telegram, + title = stringResource(id = R.string.preference_about_telegram), + summary = "t.me/Kvaesitso", + onClick = { + context.startActivity(Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse("https://t.me/Kvaesitso") + }) + } + ) + Preference( + icon = Icons.Rounded.Fdroid, + title = stringResource(id = R.string.preference_about_fdroid), + summary = "github.com/MM2-0/fdroid", + onClick = { + context.startActivity(Intent(Intent.ACTION_VIEW).apply { + data = + Uri.parse("https://raw.githubusercontent.com/MM2-0/fdroid/master/fdroid/repo") + }) + } + ) + Preference( + icon = Icons.Rounded.GitHub, + title = "GitHub", + summary = "github.com/MM2-0/Kvaesitso", + onClick = { + context.startActivity(Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse("https://github.com/MM2-0/Kvaesitso") + }) + } + ) + } + } + item { + PreferenceCategory(title = stringResource(id = R.string.preference_category_debug)) { + Preference( + title = stringResource(id = R.string.preference_crash_reporter), + onClick = { + context.startActivity(CrashReporter.getLaunchIntent()) + } + ) + Preference( + title = stringResource(id = R.string.preference_export_debug), + onClick = { + scope.launch { + val path = DebugInformationDumper().dump(context) + val result = scaffoldState.snackbarHostState.showSnackbar( + context.getString(R.string.debug_export_information_file, path), + actionLabel = context.getString(R.string.menu_share), + duration = SnackbarDuration.Long + ) + if (result == SnackbarResult.ActionPerformed) { + context.tryStartActivity(Intent(Intent.ACTION_SEND).apply { + type = "text/plain" + putExtra( + Intent.EXTRA_STREAM, FileProvider.getUriForFile( + context, + context.applicationContext.packageName + ".fileprovider", + File(path) + ) + ) + }) + } + } + } + ) + Preference( + title = stringResource(id = R.string.preference_export_databases), + onClick = { + scope.launch { + val path = DebugInformationDumper().exportDatabases(context) + val result = scaffoldState.snackbarHostState.showSnackbar( + context.getString(R.string.debug_export_information_file, path), + actionLabel = context.getString(R.string.menu_share), + duration = SnackbarDuration.Long + ) + if (result == SnackbarResult.ActionPerformed) { + context.tryStartActivity(Intent(Intent.ACTION_SEND).apply { + type = "application/x-sqlite3" + putExtra( + Intent.EXTRA_STREAM, FileProvider.getUriForFile( + context, + context.applicationContext.packageName + ".fileprovider", + File(path) + ) + ) + }) + } + } + } + ) + } + } + item { + PreferenceCategory(title = stringResource(id = R.string.preference_category_licenses)) { + for (library in OpenSourceLicenses.sortedBy { it.name.lowercase() }) { + Preference( + title = library.name, + summary = library.description, + onClick = { + navController?.navigate("settings/license?library=${library.name}") + } + ) + } + } + } + } +} \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/screens/settings/SettingsLicenseScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/screens/settings/SettingsLicenseScreen.kt new file mode 100644 index 00000000..2202459d --- /dev/null +++ b/ui/src/main/java/de/mm20/launcher2/ui/screens/settings/SettingsLicenseScreen.kt @@ -0,0 +1,93 @@ +package de.mm20.launcher2.ui.screens.settings + +import android.content.Intent +import android.net.Uri +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Icon +import androidx.compose.material.LocalContentColor +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.OpenInBrowser +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import de.mm20.launcher2.licenses.AppLicense +import de.mm20.launcher2.licenses.OpenSourceLicenses +import de.mm20.launcher2.ui.R +import de.mm20.launcher2.ui.component.preferences.PreferenceCategory +import de.mm20.launcher2.ui.component.preferences.PreferenceScreen + +@Composable +fun SettingsLicenseScreen(libraryName: String? = null) { + val context = LocalContext.current + val library = if (libraryName == null) { + AppLicense.get(context) + } else { + OpenSourceLicenses.first { it.name == libraryName } + } + PreferenceScreen(title = stringResource(id = R.string.preference_screen_about)) { + item { + PreferenceCategory { + Column( + modifier = Modifier.padding(16.dp) + ) { + Text(text = library.name, style = MaterialTheme.typography.subtitle1) + library.description?.let { Text(text = it) } + } + CompositionLocalProvider( + LocalContentColor provides MaterialTheme.colors.primary + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + context.startActivity(Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(library.url) + }) + } + .padding(all = 16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon(imageVector = Icons.Rounded.OpenInBrowser, contentDescription = null) + Text( + modifier = Modifier.padding(start = 8.dp), + text = stringResource(id = R.string.open_webpage), + style = MaterialTheme.typography.button + ) + } + } + } + } + item { + PreferenceCategory { + Column( + modifier = Modifier.padding(16.dp) + ) { + Text( + text = stringResource(id = library.licenseName), + style = MaterialTheme.typography.subtitle2 + ) + library.copyrightNote?.let { + Text( + text = it, + modifier = Modifier.padding(vertical = 4.dp) + ) + } + Text( + text = context.resources.openRawResource(library.licenseText).reader() + .readText() + ) + } + } + } + } +} \ No newline at end of file