Update extension details screen design (#7158)

* Update extension details screen design

* Review Changes

Co-Authored-By: Andreas <6576096+ghostbear@users.noreply.github.com>

* Review Changes 2

Co-authored-by: Andreas <6576096+ghostbear@users.noreply.github.com>
This commit is contained in:
FourTOne5 2022-05-20 03:31:07 +06:00 committed by GitHub
parent fd9510e18f
commit 64da16f58f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 157 additions and 39 deletions

View file

@ -1,7 +1,11 @@
package eu.kanade.presentation.browse
import android.util.DisplayMetrics
import androidx.annotation.StringRes
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -12,27 +16,35 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.browse.components.ExtensionIcon
import eu.kanade.presentation.components.Divider
@ -64,6 +76,8 @@ fun ExtensionDetailsScreen(
val sources by presenter.sourcesState.collectAsState()
val (showNsfwWarning, setShowNsfwWarning) = remember { mutableStateOf(false) }
LazyColumn(
modifier = Modifier.nestedScroll(nestedScrollInterop),
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
@ -80,7 +94,14 @@ fun ExtensionDetailsScreen(
}
item {
DetailsHeader(extension, onClickUninstall, onClickAppInfo)
DetailsHeader(
extension = extension,
onClickUninstall = onClickUninstall,
onClickAppInfo = onClickAppInfo,
onClickAgeRating = {
setShowNsfwWarning(true)
},
)
}
items(
@ -95,6 +116,13 @@ fun ExtensionDetailsScreen(
)
}
}
if (showNsfwWarning) {
NsfwWarningDialog(
onClickConfirm = {
setShowNsfwWarning(false)
},
)
}
}
@Composable
@ -116,52 +144,77 @@ private fun WarningBanner(@StringRes textRes: Int) {
@Composable
private fun DetailsHeader(
extension: Extension,
onClickAgeRating: () -> Unit,
onClickUninstall: () -> Unit,
onClickAppInfo: () -> Unit,
) {
val context = LocalContext.current
Column {
Row(
modifier = Modifier.padding(
start = horizontalPadding,
end = horizontalPadding,
top = 16.dp,
bottom = 8.dp,
),
Column(
modifier = Modifier
.fillMaxWidth()
.padding(
start = horizontalPadding,
end = horizontalPadding,
top = 16.dp,
bottom = 8.dp,
),
horizontalAlignment = Alignment.CenterHorizontally,
) {
ExtensionIcon(
modifier = Modifier
.height(56.dp)
.width(56.dp),
.size(112.dp),
extension = extension,
density = DisplayMetrics.DENSITY_XXXHIGH,
)
Column(
modifier = Modifier.padding(start = 16.dp),
) {
Text(
text = extension.name,
style = MaterialTheme.typography.titleMedium,
)
Text(
text = stringResource(R.string.ext_version_info, extension.versionName),
style = MaterialTheme.typography.bodySmall,
)
Text(
text = stringResource(R.string.ext_language_info, LocaleHelper.getSourceDisplayName(extension.lang, context)),
style = MaterialTheme.typography.bodySmall,
)
if (extension.isNsfw) {
Text(
text = stringResource(R.string.ext_nsfw_warning),
Text(
text = extension.name,
style = MaterialTheme.typography.headlineSmall,
)
val strippedPkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
Text(
text = strippedPkgName,
style = MaterialTheme.typography.bodySmall,
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = horizontalPadding * 2,
vertical = 8.dp,
),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
) {
InfoText(
primaryText = extension.versionName,
secondaryText = stringResource(R.string.ext_info_version),
)
InfoDivider()
InfoText(
primaryText = LocaleHelper.getSourceDisplayName(extension.lang, context),
secondaryText = stringResource(R.string.ext_info_language),
)
if (extension.isNsfw) {
InfoDivider()
InfoText(
primaryText = stringResource(R.string.ext_nsfw_short),
primaryTextStyle = MaterialTheme.typography.bodyLarge.copy(
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall,
)
}
Text(
text = extension.pkgName,
style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.Medium,
),
secondaryText = stringResource(R.string.ext_info_age_rating),
onCLick = onClickAgeRating,
)
}
}
@ -198,6 +251,47 @@ private fun DetailsHeader(
}
}
@Composable
private fun InfoText(
primaryText: String,
primaryTextStyle: TextStyle = MaterialTheme.typography.bodyLarge,
secondaryText: String,
onCLick: (() -> Unit)? = null,
) {
val interactionSource = remember { MutableInteractionSource() }
val modifier = if (onCLick != null) {
Modifier.clickable(interactionSource, indication = null) { onCLick() }
} else Modifier
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = primaryText,
style = primaryTextStyle,
)
Text(
text = secondaryText + if (onCLick != null) "" else "",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5F),
)
}
}
@Composable
private fun InfoDivider() {
Divider(
modifier = Modifier
.height(20.dp)
.width(1.dp),
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5F),
)
}
@Composable
private fun SourceSwitchPreference(
modifier: Modifier = Modifier,
@ -234,3 +328,20 @@ private fun SourceSwitchPreference(
},
)
}
@Composable
fun NsfwWarningDialog(
onClickConfirm: () -> Unit,
) {
AlertDialog(
text = {
Text(text = stringResource(id = R.string.ext_nsfw_warning))
},
confirmButton = {
TextButton(onClick = onClickConfirm) {
Text(text = stringResource(id = R.string.ext_nsfw_warning_dismiss))
}
},
onDismissRequest = onClickConfirm,
)
}

View file

@ -1,5 +1,7 @@
package eu.kanade.presentation.browse.components
import android.content.pm.PackageManager
import android.util.DisplayMetrics
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
@ -57,6 +59,7 @@ fun SourceIcon(
fun ExtensionIcon(
extension: Extension,
modifier: Modifier = Modifier,
density: Int = DisplayMetrics.DENSITY_DEFAULT,
) {
when (extension) {
is Extension.Available -> {
@ -71,7 +74,7 @@ fun ExtensionIcon(
)
}
is Extension.Installed -> {
val icon by extension.getIcon()
val icon by extension.getIcon(density)
when (icon) {
Result.Error -> Image(
bitmap = ImageBitmap.imageResource(id = R.mipmap.ic_local_source),
@ -95,13 +98,15 @@ fun ExtensionIcon(
}
@Composable
private fun Extension.getIcon(): State<Result<ImageBitmap>> {
private fun Extension.getIcon(density: Int = DisplayMetrics.DENSITY_DEFAULT): State<Result<ImageBitmap>> {
val context = LocalContext.current
return produceState<Result<ImageBitmap>>(initialValue = Result.Loading, this) {
withIOContext {
value = try {
val appInfo = context.packageManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA)
val appResources = context.packageManager.getResourcesForApplication(appInfo)
Result.Success(
context.packageManager.getApplicationIcon(pkgName)
appResources.getDrawableForDensity(appInfo.icon, density, null)!!
.toBitmap()
.asImageBitmap(),
)

View file

@ -269,10 +269,12 @@
<string name="obsolete_extension_message">This extension is no longer available.</string>
<string name="unofficial_extension_message">This extension is not from the official Tachiyomi extensions list.</string>
<string name="extension_api_error">Failed to get extensions list</string>
<string name="ext_version_info">Version: %1$s</string>
<string name="ext_language_info">Language: %1$s</string>
<string name="ext_info_version">Version</string>
<string name="ext_info_language">Language</string>
<string name="ext_info_age_rating">Age rating</string>
<string name="ext_nsfw_short">18+</string>
<string name="ext_nsfw_warning">May contain NSFW (18+) content</string>
<string name="ext_nsfw_warning_dismiss">Got it</string>
<string name="ext_install_service_notif">Installing extension…</string>
<string name="ext_installer_pref">Installer</string>
<string name="ext_installer_legacy">Legacy</string>