Feat(ToolScreen): Finish tool store list
This commit is contained in:
@@ -168,4 +168,5 @@ dependencies {
|
|||||||
implementation(libs.okhttp.logging)
|
implementation(libs.okhttp.logging)
|
||||||
implementation(libs.paging.runtime)
|
implementation(libs.paging.runtime)
|
||||||
implementation(libs.paging.compose)
|
implementation(libs.paging.compose)
|
||||||
|
implementation(libs.androidsvg.aar)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package top.fatweb.oxygen.toolbox.icon
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.PathFillType
|
||||||
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.graphics.StrokeJoin
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.graphics.vector.path
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun VectorPreview() {
|
||||||
|
Image(OxygenIcons.Loading, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var loading: ImageVector? = null
|
||||||
|
|
||||||
|
val OxygenIcons.Loading: ImageVector
|
||||||
|
get() {
|
||||||
|
if (loading != null) {
|
||||||
|
return loading!!
|
||||||
|
}
|
||||||
|
loading = ImageVector.Builder(
|
||||||
|
name = "Loading",
|
||||||
|
defaultWidth = 1024.dp,
|
||||||
|
defaultHeight = 1024.dp,
|
||||||
|
viewportWidth = 1024f,
|
||||||
|
viewportHeight = 1024f
|
||||||
|
).apply {
|
||||||
|
path(
|
||||||
|
fill = SolidColor(Color.Black),
|
||||||
|
fillAlpha = 1.0f,
|
||||||
|
stroke = null,
|
||||||
|
strokeAlpha = 1.0f,
|
||||||
|
strokeLineWidth = 1.0f,
|
||||||
|
strokeLineCap = StrokeCap.Butt,
|
||||||
|
strokeLineJoin = StrokeJoin.Miter,
|
||||||
|
strokeLineMiter = 1.0f,
|
||||||
|
pathFillType = PathFillType.NonZero
|
||||||
|
) {
|
||||||
|
moveTo(988f, 548f)
|
||||||
|
curveToRelative(-19.9f, 0f, -36f, -16.1f, -36f, -36f)
|
||||||
|
curveToRelative(0f, -59.4f, -11.6f, -117f, -34.6f, -171.3f)
|
||||||
|
arcToRelative(
|
||||||
|
440.45f,
|
||||||
|
440.45f,
|
||||||
|
0f,
|
||||||
|
isMoreThanHalf = false,
|
||||||
|
isPositiveArc = false,
|
||||||
|
-94.3f,
|
||||||
|
-139.9f
|
||||||
|
)
|
||||||
|
arcToRelative(
|
||||||
|
437.71f,
|
||||||
|
437.71f,
|
||||||
|
0f,
|
||||||
|
isMoreThanHalf = false,
|
||||||
|
isPositiveArc = false,
|
||||||
|
-139.9f,
|
||||||
|
-94.3f
|
||||||
|
)
|
||||||
|
curveTo(629f, 83.6f, 571.4f, 72f, 512f, 72f)
|
||||||
|
curveToRelative(-19.9f, 0f, -36f, -16.1f, -36f, -36f)
|
||||||
|
reflectiveCurveToRelative(16.1f, -36f, 36f, -36f)
|
||||||
|
curveToRelative(69.1f, 0f, 136.2f, 13.5f, 199.3f, 40.3f)
|
||||||
|
curveTo(772.3f, 66f, 827f, 103f, 874f, 150f)
|
||||||
|
curveToRelative(47f, 47f, 83.9f, 101.8f, 109.7f, 162.7f)
|
||||||
|
curveToRelative(26.7f, 63.1f, 40.2f, 130.2f, 40.2f, 199.3f)
|
||||||
|
curveToRelative(0.1f, 19.9f, -16f, 36f, -35.9f, 36f)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
return loading!!
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package top.fatweb.oxygen.toolbox.icon
|
package top.fatweb.oxygen.toolbox.icon
|
||||||
|
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.drawable.PictureDrawable
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.AccessTime
|
import androidx.compose.material.icons.filled.AccessTime
|
||||||
import androidx.compose.material.icons.filled.Build
|
import androidx.compose.material.icons.filled.Build
|
||||||
@@ -16,6 +18,12 @@ import androidx.compose.material.icons.rounded.Home
|
|||||||
import androidx.compose.material.icons.rounded.KeyboardArrowDown
|
import androidx.compose.material.icons.rounded.KeyboardArrowDown
|
||||||
import androidx.compose.material.icons.rounded.Search
|
import androidx.compose.material.icons.rounded.Search
|
||||||
import androidx.compose.material.icons.rounded.Star
|
import androidx.compose.material.icons.rounded.Star
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
|
import com.caverock.androidsvg.SVG
|
||||||
|
import kotlin.io.encoding.Base64
|
||||||
|
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||||
|
|
||||||
object OxygenIcons {
|
object OxygenIcons {
|
||||||
val ArrowDown = Icons.Rounded.KeyboardArrowDown
|
val ArrowDown = Icons.Rounded.KeyboardArrowDown
|
||||||
@@ -33,4 +41,23 @@ object OxygenIcons {
|
|||||||
val StarBorder = Icons.Outlined.StarBorder
|
val StarBorder = Icons.Outlined.StarBorder
|
||||||
val Time = Icons.Default.AccessTime
|
val Time = Icons.Default.AccessTime
|
||||||
val Tool = Icons.Default.Build
|
val Tool = Icons.Default.Build
|
||||||
|
|
||||||
|
fun fromSvgBase64(base64String: String): ImageBitmap {
|
||||||
|
val svg = SVG.getFromString(base64DecodeToString(base64String))
|
||||||
|
val drawable = PictureDrawable(svg.renderToPicture())
|
||||||
|
return drawable.toBitmap().asImageBitmap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromPngBase64(base64String: String): ImageBitmap {
|
||||||
|
val byteArray = base64DecodeToByteArray(base64String)
|
||||||
|
return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size).asImageBitmap()
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalEncodingApi::class)
|
||||||
|
private fun base64DecodeToString(base64String: String): String =
|
||||||
|
Base64.decode(base64String).decodeToString()
|
||||||
|
|
||||||
|
@OptIn(ExperimentalEncodingApi::class)
|
||||||
|
private fun base64DecodeToByteArray(base64String: String): ByteArray =
|
||||||
|
Base64.decode(base64String)
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,8 @@ fun OxygenNavHost(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
appState: OxygenAppState,
|
appState: OxygenAppState,
|
||||||
onShowSnackbar: suspend (String, String?) -> Boolean,
|
onShowSnackbar: suspend (String, String?) -> Boolean,
|
||||||
startDestination: String
|
startDestination: String,
|
||||||
|
handleOnCanScrollChange: (Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
val navController = appState.navController
|
val navController = appState.navController
|
||||||
NavHost(
|
NavHost(
|
||||||
@@ -29,7 +30,8 @@ fun OxygenNavHost(
|
|||||||
onBackClick = navController::popBackStack
|
onBackClick = navController::popBackStack
|
||||||
)
|
)
|
||||||
toolsScreen(
|
toolsScreen(
|
||||||
|
onShowSnackbar = onShowSnackbar,
|
||||||
|
handleOnCanScrollChange = handleOnCanScrollChange
|
||||||
)
|
)
|
||||||
starScreen(
|
starScreen(
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,16 @@ const val TOOLS_ROUTE = "tools_route"
|
|||||||
|
|
||||||
fun NavController.navigateToTools(navOptions: NavOptions) = navigate(TOOLS_ROUTE, navOptions)
|
fun NavController.navigateToTools(navOptions: NavOptions) = navigate(TOOLS_ROUTE, navOptions)
|
||||||
|
|
||||||
fun NavGraphBuilder.toolsScreen() {
|
fun NavGraphBuilder.toolsScreen(
|
||||||
|
onShowSnackbar: suspend (String, String?) -> Boolean,
|
||||||
|
handleOnCanScrollChange: (Boolean) -> Unit
|
||||||
|
) {
|
||||||
composable(
|
composable(
|
||||||
route = TOOLS_ROUTE
|
route = TOOLS_ROUTE
|
||||||
) {
|
) {
|
||||||
ToolsRoute()
|
ToolsRoute(
|
||||||
|
onShowSnackbar = onShowSnackbar,
|
||||||
|
handleOnCanScrollChange = handleOnCanScrollChange
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,12 @@ fun OxygenApp(appState: OxygenAppState) {
|
|||||||
|
|
||||||
val noConnectMessage = stringResource(R.string.core_no_connect)
|
val noConnectMessage = stringResource(R.string.core_no_connect)
|
||||||
|
|
||||||
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
var canScroll by remember { mutableStateOf(false) }
|
||||||
|
val handleOnCanScrollChange = { value: Boolean ->
|
||||||
|
canScroll = value
|
||||||
|
}
|
||||||
|
val topAppBarScrollBehavior =
|
||||||
|
TopAppBarDefaults.enterAlwaysScrollBehavior(canScroll = { canScroll })
|
||||||
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
|
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
|
||||||
|
|
||||||
LaunchedEffect(isOffline) {
|
LaunchedEffect(isOffline) {
|
||||||
@@ -175,7 +180,8 @@ fun OxygenApp(appState: OxygenAppState) {
|
|||||||
startDestination = when (appState.launchPageConfig) {
|
startDestination = when (appState.launchPageConfig) {
|
||||||
LaunchPageConfig.TOOLS -> TOOLS_ROUTE
|
LaunchPageConfig.TOOLS -> TOOLS_ROUTE
|
||||||
LaunchPageConfig.STAR -> STAR_ROUTE
|
LaunchPageConfig.STAR -> STAR_ROUTE
|
||||||
}
|
},
|
||||||
|
handleOnCanScrollChange = handleOnCanScrollChange
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ package top.fatweb.oxygen.toolbox.ui.about
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.activity.compose.ReportDrawnWhen
|
import androidx.activity.compose.ReportDrawnWhen
|
||||||
|
import androidx.compose.animation.core.Ease
|
||||||
|
import androidx.compose.animation.core.animateFloat
|
||||||
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
|
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.gestures.Orientation
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -21,6 +26,7 @@ import androidx.compose.foundation.layout.only
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.safeDrawing
|
import androidx.compose.foundation.layout.safeDrawing
|
||||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.systemBars
|
import androidx.compose.foundation.layout.systemBars
|
||||||
import androidx.compose.foundation.layout.widthIn
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
||||||
@@ -33,6 +39,7 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -46,6 +53,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -54,6 +62,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import top.fatweb.oxygen.toolbox.R
|
import top.fatweb.oxygen.toolbox.R
|
||||||
|
import top.fatweb.oxygen.toolbox.icon.Loading
|
||||||
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
|
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
|
||||||
import top.fatweb.oxygen.toolbox.ui.component.OxygenTopAppBar
|
import top.fatweb.oxygen.toolbox.ui.component.OxygenTopAppBar
|
||||||
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.DraggableScrollbar
|
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.DraggableScrollbar
|
||||||
@@ -105,6 +114,8 @@ internal fun LibrariesScreen(
|
|||||||
|
|
||||||
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(canScroll = { state.canScrollForward })
|
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(canScroll = { state.canScrollForward })
|
||||||
|
|
||||||
|
val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition")
|
||||||
|
|
||||||
var activeSearch by remember { mutableStateOf(false) }
|
var activeSearch by remember { mutableStateOf(false) }
|
||||||
var searchValue by remember { mutableStateOf("") }
|
var searchValue by remember { mutableStateOf("") }
|
||||||
|
|
||||||
@@ -156,8 +167,26 @@ internal fun LibrariesScreen(
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
when (librariesScreenUiState) {
|
when (librariesScreenUiState) {
|
||||||
LibrariesScreenUiState.Loading -> {
|
LibrariesScreenUiState.Loading -> {Column(
|
||||||
Text(text = stringResource(R.string.feature_settings_loading))
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
val angle by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 0F,
|
||||||
|
targetValue = 360F,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(800, easing = Ease),
|
||||||
|
), label = "angle"
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(32.dp)
|
||||||
|
.graphicsLayer { rotationZ = angle },
|
||||||
|
imageVector = OxygenIcons.Loading,
|
||||||
|
contentDescription = ""
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LibrariesScreenUiState.Nothing -> {
|
LibrariesScreenUiState.Nothing -> {
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package top.fatweb.oxygen.toolbox.ui.component
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import top.fatweb.oxygen.toolbox.R
|
||||||
|
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
|
||||||
|
import top.fatweb.oxygen.toolbox.model.tool.Tool
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ToolCard(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
tool: Tool,
|
||||||
|
onClickToolCard: () -> Unit
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = modifier,
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
|
||||||
|
onClick = onClickToolCard
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
) {
|
||||||
|
ToolVer(ver = tool.ver)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
ToolIcon(icon = tool.icon)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
ToolInfo(
|
||||||
|
toolName = tool.name,
|
||||||
|
toolId = tool.toolId,
|
||||||
|
toolDesc = tool.description
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
AuthorInfo(
|
||||||
|
avatar = tool.author.avatar,
|
||||||
|
nickname = tool.author.nickname
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ToolVer(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
ver: String
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = modifier,
|
||||||
|
colors = CardDefaults.cardColors(contentColor = MaterialTheme.colorScheme.onSecondaryContainer)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(color = MaterialTheme.colorScheme.surfaceContainer)
|
||||||
|
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
text = ver
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ToolIcon(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
icon: String
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
modifier = Modifier.size(80.dp),
|
||||||
|
bitmap = OxygenIcons.fromSvgBase64(icon),
|
||||||
|
contentDescription = ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ToolInfo(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
toolName: String,
|
||||||
|
toolId: String,
|
||||||
|
toolDesc: String
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
fontWeight = FontWeight.ExtraBold,
|
||||||
|
text = toolName
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
text = "ID: $toolId"
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.outline,
|
||||||
|
text = "${stringResource(R.string.feature_tools_description)}: $toolDesc"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AuthorInfo(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
avatar: String,
|
||||||
|
nickname: String
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally)
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(24.dp),
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceContainer),
|
||||||
|
bitmap = OxygenIcons.fromPngBase64(avatar), contentDescription = "Avatar"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
text = nickname
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,22 @@
|
|||||||
package top.fatweb.oxygen.toolbox.ui.settings
|
package top.fatweb.oxygen.toolbox.ui.settings
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.core.Ease
|
||||||
|
import androidx.compose.animation.core.animateFloat
|
||||||
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
|
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.widthIn
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
@@ -26,6 +34,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -34,6 +43,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import top.fatweb.oxygen.toolbox.R
|
import top.fatweb.oxygen.toolbox.R
|
||||||
|
import top.fatweb.oxygen.toolbox.icon.Loading
|
||||||
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
|
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
|
||||||
import top.fatweb.oxygen.toolbox.model.userdata.DarkThemeConfig
|
import top.fatweb.oxygen.toolbox.model.userdata.DarkThemeConfig
|
||||||
import top.fatweb.oxygen.toolbox.model.userdata.LanguageConfig
|
import top.fatweb.oxygen.toolbox.model.userdata.LanguageConfig
|
||||||
@@ -82,6 +92,7 @@ fun SettingsDialog(
|
|||||||
onNavigateToAbout: () -> Unit
|
onNavigateToAbout: () -> Unit
|
||||||
) {
|
) {
|
||||||
val configuration = LocalConfiguration.current
|
val configuration = LocalConfiguration.current
|
||||||
|
val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition")
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
@@ -101,10 +112,26 @@ fun SettingsDialog(
|
|||||||
) {
|
) {
|
||||||
when (settingsUiState) {
|
when (settingsUiState) {
|
||||||
SettingsUiState.Loading -> {
|
SettingsUiState.Loading -> {
|
||||||
Text(
|
Column(
|
||||||
modifier = Modifier.padding(vertical = 16.dp),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
text = stringResource(R.string.feature_settings_loading)
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
val angle by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 0F,
|
||||||
|
targetValue = 360F,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(800, easing = Ease),
|
||||||
|
), label = "angle"
|
||||||
)
|
)
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(32.dp)
|
||||||
|
.graphicsLayer { rotationZ = angle },
|
||||||
|
imageVector = OxygenIcons.Loading,
|
||||||
|
contentDescription = ""
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is SettingsUiState.Success -> {
|
is SettingsUiState.Success -> {
|
||||||
|
|||||||
@@ -2,17 +2,21 @@ package top.fatweb.oxygen.toolbox.ui.tool
|
|||||||
|
|
||||||
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope
|
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope
|
||||||
import androidx.compose.foundation.lazy.staggeredgrid.items
|
import androidx.compose.foundation.lazy.staggeredgrid.items
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import top.fatweb.oxygen.toolbox.model.tool.Tool
|
import top.fatweb.oxygen.toolbox.model.tool.Tool
|
||||||
|
import top.fatweb.oxygen.toolbox.ui.component.ToolCard
|
||||||
|
|
||||||
fun LazyStaggeredGridScope.toolsPanel(
|
fun LazyStaggeredGridScope.toolsPanel(
|
||||||
toolStorePagingItems: LazyPagingItems<Tool>
|
toolStorePagingItems: LazyPagingItems<Tool>,
|
||||||
|
onClickToolCard: (username: String, toolId: String) -> Unit
|
||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
items = toolStorePagingItems.itemSnapshotList,
|
items = toolStorePagingItems.itemSnapshotList,
|
||||||
key = { it!!.id },
|
key = { it!!.id },
|
||||||
) {
|
) {
|
||||||
Text(text = it!!.name)
|
ToolCard(
|
||||||
|
tool = it!!,
|
||||||
|
onClickToolCard = {onClickToolCard(it.author.username, it.toolId)}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,26 @@
|
|||||||
package top.fatweb.oxygen.toolbox.ui.tool
|
package top.fatweb.oxygen.toolbox.ui.tool
|
||||||
|
|
||||||
import android.util.Log
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.ReportDrawnWhen
|
import androidx.activity.compose.ReportDrawnWhen
|
||||||
|
import androidx.compose.animation.core.Ease
|
||||||
|
import androidx.compose.animation.core.animateFloat
|
||||||
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
|
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.gestures.Orientation
|
import androidx.compose.foundation.gestures.Orientation
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.safeDrawing
|
import androidx.compose.foundation.layout.safeDrawing
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.systemBars
|
import androidx.compose.foundation.layout.systemBars
|
||||||
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
||||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
@@ -20,14 +28,21 @@ import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
|
|||||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
||||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan
|
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan
|
||||||
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
|
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.paging.LoadState
|
import androidx.paging.LoadState
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
|
import top.fatweb.oxygen.toolbox.icon.Loading
|
||||||
|
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
|
||||||
import top.fatweb.oxygen.toolbox.model.tool.Tool
|
import top.fatweb.oxygen.toolbox.model.tool.Tool
|
||||||
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.DraggableScrollbar
|
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.DraggableScrollbar
|
||||||
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.rememberDraggableScroller
|
import top.fatweb.oxygen.toolbox.ui.component.scrollbar.rememberDraggableScroller
|
||||||
@@ -36,12 +51,16 @@ import top.fatweb.oxygen.toolbox.ui.component.scrollbar.scrollbarState
|
|||||||
@Composable
|
@Composable
|
||||||
internal fun ToolsRoute(
|
internal fun ToolsRoute(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: ToolsScreenViewModel = hiltViewModel()
|
viewModel: ToolsScreenViewModel = hiltViewModel(),
|
||||||
|
onShowSnackbar: suspend (String, String?) -> Boolean,
|
||||||
|
handleOnCanScrollChange: (Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
val toolStorePagingItems = viewModel.getStoreData().collectAsLazyPagingItems()
|
val toolStorePagingItems = viewModel.storeData.collectAsLazyPagingItems()
|
||||||
|
|
||||||
ToolsScreen(
|
ToolsScreen(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
onShowSnackbar = onShowSnackbar,
|
||||||
|
handleOnCanScrollChange = handleOnCanScrollChange,
|
||||||
toolStorePagingItems = toolStorePagingItems
|
toolStorePagingItems = toolStorePagingItems
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -49,11 +68,14 @@ internal fun ToolsRoute(
|
|||||||
@Composable
|
@Composable
|
||||||
internal fun ToolsScreen(
|
internal fun ToolsScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
onShowSnackbar: suspend (String, String?) -> Boolean,
|
||||||
|
handleOnCanScrollChange: (Boolean) -> Unit,
|
||||||
toolStorePagingItems: LazyPagingItems<Tool>
|
toolStorePagingItems: LazyPagingItems<Tool>
|
||||||
) {
|
) {
|
||||||
val isToolLoading = toolStorePagingItems.loadState.refresh is LoadState.Loading
|
val context = LocalContext.current
|
||||||
|
val isToolLoading =
|
||||||
Log.d("TAG", "ToolsScreen: ${toolStorePagingItems.loadState}")
|
toolStorePagingItems.loadState.refresh is LoadState.Loading
|
||||||
|
|| toolStorePagingItems.loadState.append is LoadState.Loading
|
||||||
|
|
||||||
ReportDrawnWhen { !isToolLoading }
|
ReportDrawnWhen { !isToolLoading }
|
||||||
|
|
||||||
@@ -62,18 +84,30 @@ internal fun ToolsScreen(
|
|||||||
val state = rememberLazyStaggeredGridState()
|
val state = rememberLazyStaggeredGridState()
|
||||||
val scrollbarState = state.scrollbarState(itemsAvailable = itemsAvailable)
|
val scrollbarState = state.scrollbarState(itemsAvailable = itemsAvailable)
|
||||||
|
|
||||||
|
val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition")
|
||||||
|
|
||||||
|
val handleOnClickToolCard = { username: String, toolId: String ->
|
||||||
|
Toast.makeText(context, "$username:$toolId", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(state.canScrollForward) {
|
||||||
|
handleOnCanScrollChange(state.canScrollForward)
|
||||||
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier.fillMaxSize()
|
modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
LazyVerticalStaggeredGrid(
|
LazyVerticalStaggeredGrid(
|
||||||
columns = StaggeredGridCells.Adaptive(300.dp),
|
columns = StaggeredGridCells.Adaptive(160.dp),
|
||||||
contentPadding = PaddingValues(16.dp),
|
contentPadding = PaddingValues(16.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
verticalItemSpacing = 24.dp,
|
verticalItemSpacing = 24.dp,
|
||||||
state = state
|
state = state
|
||||||
) {
|
) {
|
||||||
|
|
||||||
toolsPanel(toolStorePagingItems = toolStorePagingItems)
|
toolsPanel(
|
||||||
|
toolStorePagingItems = toolStorePagingItems,
|
||||||
|
onClickToolCard = handleOnClickToolCard
|
||||||
|
)
|
||||||
|
|
||||||
item(span = StaggeredGridItemSpan.FullLine) {
|
item(span = StaggeredGridItemSpan.FullLine) {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
@@ -81,6 +115,29 @@ internal fun ToolsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toolStorePagingItems.loadState.refresh is LoadState.Loading || toolStorePagingItems.loadState.append is LoadState.Loading) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
val angle by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 0F,
|
||||||
|
targetValue = 360F,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(800, easing = Ease),
|
||||||
|
), label = "angle"
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(32.dp)
|
||||||
|
.graphicsLayer { rotationZ = angle },
|
||||||
|
imageVector = OxygenIcons.Loading,
|
||||||
|
contentDescription = ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state.DraggableScrollbar(
|
state.DraggableScrollbar(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
@@ -92,25 +149,3 @@ internal fun ToolsScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
@OxygenPreviews
|
|
||||||
@Composable
|
|
||||||
fun ToolsScreenLoadingPreview() {
|
|
||||||
OxygenTheme {
|
|
||||||
ToolsScreen(toolsScreenUiState = ToolsScreenUiState.Loading)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OxygenPreviews
|
|
||||||
@Composable
|
|
||||||
fun ToolsScreenPreview() {
|
|
||||||
OxygenTheme {
|
|
||||||
ToolsScreen(
|
|
||||||
toolsScreenUiState = ToolsScreenUiState.Success(
|
|
||||||
runBlocking {
|
|
||||||
ToolDataSource().tool.first()
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|||||||
@@ -24,15 +24,13 @@ class ToolsScreenViewModel @Inject constructor(
|
|||||||
private val currentPage = savedStateHandle.getStateFlow(CURRENT_PAGE, 1)
|
private val currentPage = savedStateHandle.getStateFlow(CURRENT_PAGE, 1)
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
fun getStoreData(): Flow<PagingData<Tool>> {
|
val storeData: Flow<PagingData<Tool>> = combine(
|
||||||
return combine(
|
|
||||||
searchValue,
|
searchValue,
|
||||||
currentPage,
|
currentPage,
|
||||||
::Pair
|
::Pair
|
||||||
).flatMapLatest { (searchValue, currentPage) ->
|
).flatMapLatest { (searchValue, currentPage) ->
|
||||||
toolRepository.getStore(searchValue, currentPage).cachedIn(viewModelScope)
|
toolRepository.getStore(searchValue, currentPage).cachedIn(viewModelScope)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface ToolsScreenUiState {
|
sealed interface ToolsScreenUiState {
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
<string name="core_unknown">未知</string>
|
<string name="core_unknown">未知</string>
|
||||||
<string name="core_website">网站</string>
|
<string name="core_website">网站</string>
|
||||||
<string name="core_search">搜索</string>
|
<string name="core_search">搜索</string>
|
||||||
|
<string name="core_loading">加载中…</string>
|
||||||
<string name="core_no_connect">⚠️ 无法连接至互联网</string>
|
<string name="core_no_connect">⚠️ 无法连接至互联网</string>
|
||||||
<string name="feature_tools_title">工具</string>
|
<string name="feature_tools_title">工具</string>
|
||||||
<string name="feature_star_title">收藏</string>
|
<string name="feature_star_title">收藏</string>
|
||||||
<string name="feature_settings_title">设置</string>
|
<string name="feature_settings_title">设置</string>
|
||||||
<string name="feature_settings_loading">加载中…</string>
|
|
||||||
<string name="feature_settings_language">语言</string>
|
<string name="feature_settings_language">语言</string>
|
||||||
<string name="feature_settings_language_system_default">系统默认</string>
|
<string name="feature_settings_language_system_default">系统默认</string>
|
||||||
<string name="feature_settings_launch_page">启动页</string>
|
<string name="feature_settings_launch_page">启动页</string>
|
||||||
@@ -34,4 +34,5 @@
|
|||||||
<string name="feature_settings_more_about">关于</string>
|
<string name="feature_settings_more_about">关于</string>
|
||||||
<string name="feature_settings_top_app_bar_action_icon_description">更多</string>
|
<string name="feature_settings_top_app_bar_action_icon_description">更多</string>
|
||||||
<string name="feature_settings_top_app_bar_navigation_icon_description">搜索</string>
|
<string name="feature_settings_top_app_bar_navigation_icon_description">搜索</string>
|
||||||
|
<string name="feature_tools_description">简介</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -8,11 +8,11 @@
|
|||||||
<string name="core_unknown">Unknown</string>
|
<string name="core_unknown">Unknown</string>
|
||||||
<string name="core_website">Website</string>
|
<string name="core_website">Website</string>
|
||||||
<string name="core_search">Search</string>
|
<string name="core_search">Search</string>
|
||||||
|
<string name="core_loading">Loading…</string>
|
||||||
<string name="core_no_connect">⚠️ Unable to connect to the internet</string>
|
<string name="core_no_connect">⚠️ Unable to connect to the internet</string>
|
||||||
<string name="feature_tools_title">Tools</string>
|
<string name="feature_tools_title">Tools</string>
|
||||||
<string name="feature_star_title">Star</string>
|
<string name="feature_star_title">Star</string>
|
||||||
<string name="feature_settings_title">Settings</string>
|
<string name="feature_settings_title">Settings</string>
|
||||||
<string name="feature_settings_loading">Loading…</string>
|
|
||||||
<string name="feature_settings_language">Language</string>
|
<string name="feature_settings_language">Language</string>
|
||||||
<string name="feature_settings_language_system_default">System Default</string>
|
<string name="feature_settings_language_system_default">System Default</string>
|
||||||
<string name="feature_settings_language_chinese" translatable="false">中文</string>
|
<string name="feature_settings_language_chinese" translatable="false">中文</string>
|
||||||
@@ -35,4 +35,5 @@
|
|||||||
<string name="feature_settings_more_about">About</string>
|
<string name="feature_settings_more_about">About</string>
|
||||||
<string name="feature_settings_top_app_bar_action_icon_description">More</string>
|
<string name="feature_settings_top_app_bar_action_icon_description">More</string>
|
||||||
<string name="feature_settings_top_app_bar_navigation_icon_description">Search</string>
|
<string name="feature_settings_top_app_bar_navigation_icon_description">Search</string>
|
||||||
|
<string name="feature_tools_description">Desc</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -29,6 +29,7 @@ kotlinxSerializationJson = "1.6.3"
|
|||||||
retrofit = "2.9.0"
|
retrofit = "2.9.0"
|
||||||
retrofitKotlinxSerializationJson = "1.0.0"
|
retrofitKotlinxSerializationJson = "1.0.0"
|
||||||
okhttp = "4.12.0"
|
okhttp = "4.12.0"
|
||||||
|
androidsvg = "1.4"
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||||
@@ -89,3 +90,4 @@ retrofit-kotlin-serialization = { group = "com.jakewharton.retrofit", name = "re
|
|||||||
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
|
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
|
||||||
paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.ref = "paging" }
|
paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.ref = "paging" }
|
||||||
paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "paging" }
|
paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "paging" }
|
||||||
|
androidsvg-aar = { group = "com.caverock", name = "androidsvg-aar", version.ref = "androidsvg" }
|
||||||
|
|||||||
Reference in New Issue
Block a user