Basic initial port of About screen to Compose

This commit is contained in:
arkon 2022-04-23 12:05:00 -04:00
parent 070abd79ce
commit d6c87ec10e
9 changed files with 359 additions and 99 deletions

View file

@ -142,6 +142,7 @@ dependencies {
implementation(compose.foundation)
implementation(compose.material3.core)
implementation(compose.material3.adapter)
implementation(compose.material.icons)
implementation(compose.animation)
implementation(compose.ui.tooling)

View file

@ -0,0 +1,39 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.unit.dp
@Composable
fun LinkIcon(
modifier: Modifier = Modifier,
label: String,
painter: Painter,
url: String,
) {
val uriHandler = LocalUriHandler.current
LinkIcon(modifier, label, painter) { uriHandler.openUri(url) }
}
@Composable
fun LinkIcon(
modifier: Modifier = Modifier,
label: String,
painter: Painter,
onClick: () -> Unit,
) {
Icon(
modifier = modifier
.clickable(onClick = onClick)
.padding(16.dp),
painter = painter,
tint = MaterialTheme.colorScheme.primary,
contentDescription = label,
)
}

View file

@ -0,0 +1,89 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
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.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.util.horizontalPadding
@Composable
fun PreferenceRow(
title: String,
icon: ImageVector? = null,
onClick: () -> Unit = {},
onLongClick: () -> Unit = {},
subtitle: String? = null,
action: @Composable (() -> Unit)? = null,
) {
val height = if (subtitle != null) 72.dp else 56.dp
// TODO: adjust text styles, especially subtitles
val textStyle = MaterialTheme.typography.titleMedium.copy(
color = contentColorFor(MaterialTheme.colorScheme.background),
)
Row(
modifier = Modifier
.fillMaxWidth()
.requiredHeight(height)
.combinedClickable(
onLongClick = onLongClick,
onClick = onClick
),
verticalAlignment = Alignment.CenterVertically
) {
if (icon != null) {
Icon(
imageVector = icon,
modifier = Modifier
.padding(horizontal = horizontalPadding)
.size(24.dp),
tint = MaterialTheme.colorScheme.primary,
contentDescription = null
)
}
Column(
Modifier
.padding(horizontal = horizontalPadding)
.weight(1f)
) {
Text(
text = title,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = textStyle,
)
if (subtitle != null) {
Text(
text = subtitle,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = textStyle.copy(
fontWeight = FontWeight.Normal,
),
)
}
}
if (action != null) {
Box(Modifier.widthIn(min = 56.dp)) {
action()
}
}
}
}

View file

@ -0,0 +1,144 @@
package eu.kanade.presentation.more
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Public
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.LinkIcon
import eu.kanade.presentation.components.PreferenceRow
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.updater.RELEASE_URL
import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.system.copyToClipboard
@Composable
fun AboutScreen(
nestedScrollInterop: NestedScrollConnection,
checkVersion: () -> Unit,
getFormattedBuildTime: () -> String,
onClickLicenses: () -> Unit,
) {
val context = LocalContext.current
val uriHandler = LocalUriHandler.current
LazyColumn(
modifier = Modifier.nestedScroll(nestedScrollInterop),
) {
item {
LogoHeader()
}
item {
PreferenceRow(
title = stringResource(R.string.version),
subtitle = when {
BuildConfig.DEBUG -> {
"Debug ${BuildConfig.COMMIT_SHA} (${getFormattedBuildTime()})"
}
BuildConfig.PREVIEW -> {
"Preview r${BuildConfig.COMMIT_COUNT} (${BuildConfig.COMMIT_SHA}, ${getFormattedBuildTime()})"
}
else -> {
"Stable ${BuildConfig.VERSION_NAME} (${getFormattedBuildTime()})"
}
},
onClick = {
val deviceInfo = CrashLogUtil(context).getDebugInfo()
context.copyToClipboard("Debug information", deviceInfo)
},
)
}
if (BuildConfig.INCLUDE_UPDATER) {
item {
PreferenceRow(
title = stringResource(R.string.check_for_updates),
onClick = {
checkVersion()
},
)
}
}
if (!BuildConfig.DEBUG) {
item {
PreferenceRow(
title = stringResource(R.string.whats_new),
onClick = {
uriHandler.openUri(RELEASE_URL)
},
)
}
}
item {
PreferenceRow(
title = stringResource(R.string.help_translate),
onClick = { uriHandler.openUri("https://tachiyomi.org/help/contribution/#translation") },
)
}
item {
PreferenceRow(
title = stringResource(R.string.licenses),
onClick = onClickLicenses,
)
}
item {
PreferenceRow(
title = stringResource(R.string.privacy_policy),
onClick = { uriHandler.openUri("https://tachiyomi.org/privacy") },
)
}
item {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
LinkIcon(
label = stringResource(R.string.website),
painter = rememberVectorPainter(Icons.Outlined.Public),
url = "https://tachiyomi.org",
)
LinkIcon(
label = "Discord",
painter = painterResource(R.drawable.ic_discord_24dp),
url = "https://discord.gg/tachiyomi",
)
LinkIcon(
label = "Twitter",
painter = painterResource(R.drawable.ic_twitter_24dp),
url = "https://twitter.com/tachiyomiorg",
)
LinkIcon(
label = "Facebook",
painter = painterResource(R.drawable.ic_facebook_24dp),
url = "https://facebook.com/tachiyomiorg",
)
LinkIcon(
label = "Reddit",
painter = painterResource(R.drawable.ic_reddit_24dp),
url = "https://www.reddit.com/r/Tachiyomi",
)
LinkIcon(
label = "GitHub",
painter = painterResource(R.drawable.ic_github_24dp),
url = "https://github.com/tachiyomiorg",
)
}
}
}
}

View file

@ -0,0 +1,28 @@
package eu.kanade.presentation.more
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer
import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults
@Composable
fun LicensesScreen(
nestedScrollInterop: NestedScrollConnection,
) {
LibrariesContainer(
modifier = Modifier
.fillMaxSize()
.nestedScroll(nestedScrollInterop),
colors = LibraryDefaults.libraryColors(
backgroundColor = MaterialTheme.colorScheme.background,
contentColor = contentColorFor(MaterialTheme.colorScheme.background),
badgeBackgroundColor = MaterialTheme.colorScheme.primary,
badgeContentColor = contentColorFor(MaterialTheme.colorScheme.primary),
),
)
}

View file

@ -0,0 +1,36 @@
package eu.kanade.presentation.more
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.R
@Composable
fun LogoHeader() {
Column {
Surface(
modifier = Modifier.fillMaxWidth()
) {
Icon(
painter = painterResource(R.drawable.ic_tachi),
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurface,
modifier = Modifier
.padding(32.dp)
.size(56.dp),
)
}
// TODO: proper color
Divider()
}
}

View file

@ -1,106 +1,44 @@
package eu.kanade.tachiyomi.ui.more
import androidx.preference.PreferenceScreen
import androidx.compose.runtime.Composable
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import eu.kanade.presentation.more.AboutScreen
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
import eu.kanade.tachiyomi.data.updater.AppUpdateResult
import eu.kanade.tachiyomi.data.updater.RELEASE_URL
import eu.kanade.tachiyomi.ui.base.controller.BasicComposeController
import eu.kanade.tachiyomi.ui.base.controller.NoAppBarElevationController
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.setting.SettingsController
import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.lang.launchNow
import eu.kanade.tachiyomi.util.lang.toDateTimestampString
import eu.kanade.tachiyomi.util.preference.add
import eu.kanade.tachiyomi.util.preference.onClick
import eu.kanade.tachiyomi.util.preference.preference
import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toast
import logcat.LogPriority
import uy.kohesive.injekt.injectLazy
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone
class AboutController : SettingsController(), NoAppBarElevationController {
class AboutController : BasicComposeController(), NoAppBarElevationController {
private val preferences: PreferencesHelper by injectLazy()
private val updateChecker by lazy { AppUpdateChecker() }
private val dateFormat: DateFormat = preferences.dateFormat()
override fun getTitle() = resources?.getString(R.string.pref_category_about)
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_about
add(MoreHeaderPreference(context))
preference {
key = "pref_about_version"
titleRes = R.string.version
summary = when {
BuildConfig.DEBUG -> {
"Debug ${BuildConfig.COMMIT_SHA} (${getFormattedBuildTime()})"
}
BuildConfig.PREVIEW -> {
"Preview r${BuildConfig.COMMIT_COUNT} (${BuildConfig.COMMIT_SHA}, ${getFormattedBuildTime()})"
}
else -> {
"Stable ${BuildConfig.VERSION_NAME} (${getFormattedBuildTime()})"
}
}
onClick {
activity?.let {
val deviceInfo = CrashLogUtil(it).getDebugInfo()
it.copyToClipboard("Debug information", deviceInfo)
}
}
}
if (BuildConfig.INCLUDE_UPDATER) {
preference {
key = "pref_about_check_for_updates"
titleRes = R.string.check_for_updates
onClick { checkVersion() }
}
}
if (!BuildConfig.DEBUG) {
preference {
key = "pref_about_whats_new"
titleRes = R.string.whats_new
onClick {
openInBrowser(RELEASE_URL)
}
}
}
preference {
key = "pref_about_help_translate"
titleRes = R.string.help_translate
onClick {
openInBrowser("https://tachiyomi.org/help/contribution/#translation")
}
}
preference {
key = "pref_about_licenses"
titleRes = R.string.licenses
onClick {
@Composable
override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) {
AboutScreen(
nestedScrollInterop = nestedScrollInterop,
checkVersion = this::checkVersion,
getFormattedBuildTime = this::getFormattedBuildTime,
onClickLicenses = {
router.pushController(LicensesController().withFadeTransaction())
}
}
preference {
key = "pref_about_privacy_policy"
titleRes = R.string.privacy_policy
onClick {
openInBrowser("https://tachiyomi.org/privacy")
}
}
add(AboutLinksPreference(context))
},
)
}
/**
@ -142,7 +80,7 @@ class AboutController : SettingsController(), NoAppBarElevationController {
)
outputDf.timeZone = TimeZone.getDefault()
buildTime!!.toDateTimestampString(dateFormat)
buildTime!!.toDateTimestampString(preferences.dateFormat())
} catch (e: Exception) {
BuildConfig.BUILD_TIME
}

View file

@ -1,14 +1,8 @@
package eu.kanade.tachiyomi.ui.more
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer
import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults.libraryColors
import eu.kanade.presentation.more.LicensesScreen
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.BasicComposeController
@ -18,16 +12,6 @@ class LicensesController : BasicComposeController() {
@Composable
override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) {
LibrariesContainer(
modifier = Modifier
.fillMaxSize()
.nestedScroll(nestedScrollInterop),
colors = libraryColors(
backgroundColor = MaterialTheme.colorScheme.background,
contentColor = contentColorFor(MaterialTheme.colorScheme.background),
badgeBackgroundColor = MaterialTheme.colorScheme.primary,
badgeContentColor = contentColorFor(MaterialTheme.colorScheme.primary),
),
)
LicensesScreen(nestedScrollInterop = nestedScrollInterop)
}
}

View file

@ -5,5 +5,6 @@ compose = "1.2.0-alpha07"
foundation = { module = "androidx.compose.foundation:foundation", version.ref="compose" }
material3-core = "androidx.compose.material3:material3:1.0.0-alpha09"
material3-adapter = "com.google.android.material:compose-theme-adapter-3:1.0.6"
material-icons = { module = "androidx.compose.material:material-icons-extended", version.ref="compose" }
animation = { module = "androidx.compose.animation:animation", version.ref="compose" }
ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref="compose" }