Basic search implementation.
This commit is contained in:
parent
dfc6c4e673
commit
af5fb8f33c
21 changed files with 806 additions and 298 deletions
|
|
@ -97,6 +97,9 @@ dependencies {
|
|||
implementation("com.google.accompanist:accompanist-navigation-animation:0.30.1")
|
||||
implementation("com.google.accompanist:accompanist-placeholder:0.30.1")
|
||||
|
||||
// Splash Screen support prior to Android 12
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
|
||||
// Google service
|
||||
implementation("com.google.android.gms:play-services-auth:20.6.0")
|
||||
implementation(
|
||||
|
|
|
|||
|
|
@ -9,19 +9,23 @@
|
|||
<application
|
||||
android:name=".MainApplication"
|
||||
android:allowBackup="true"
|
||||
android:colorMode="wideColorGamut"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Lexique"
|
||||
android:theme="@style/Theme.Lexicon.NoActionBar"
|
||||
tools:targetApi="31">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Lexique">
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.Lexicon.Starting"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
|
@ -29,5 +33,4 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
@ -4,6 +4,7 @@ import android.app.Activity
|
|||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -15,13 +16,19 @@ import androidx.compose.runtime.CompositionLocalProvider
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.pixelized.rplexicon.ui.navigation.ScreenNavHost
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
val LocalActivity = staticCompositionLocalOf<Activity> { error("Activity not available") }
|
||||
val LocalSnack =
|
||||
staticCompositionLocalOf<SnackbarHostState> { error("SnackbarHostState not available") }
|
||||
val LocalActivity = staticCompositionLocalOf<Activity> {
|
||||
error("Activity not available")
|
||||
}
|
||||
val LocalSnack = staticCompositionLocalOf<SnackbarHostState> {
|
||||
error("SnackbarHostState not available")
|
||||
}
|
||||
val NO_WINDOW_INSETS = WindowInsets(0, 0, 0, 0)
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
|
@ -29,6 +36,14 @@ class MainActivity : ComponentActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Disable system inset consuming.
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
// splashscreen management
|
||||
installSplashScreen().apply {
|
||||
setKeepOnScreenCondition { false }
|
||||
}
|
||||
|
||||
setContent {
|
||||
LexiconTheme {
|
||||
CompositionLocalProvider(
|
||||
|
|
@ -36,6 +51,7 @@ class MainActivity : ComponentActivity() {
|
|||
LocalSnack provides remember { SnackbarHostState() }
|
||||
) {
|
||||
Scaffold(
|
||||
contentWindowInsets = NO_WINDOW_INSETS,
|
||||
content = { padding ->
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -5,15 +5,14 @@ import androidx.compose.animation.core.*
|
|||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
|
|
@ -25,24 +24,24 @@ import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
|||
|
||||
@Composable
|
||||
fun FloatingActionButton(
|
||||
modifier: Modifier = Modifier,
|
||||
expended: Boolean,
|
||||
enabled: Boolean = true,
|
||||
innerSpacing: Dp = 16.dp,
|
||||
contentPadding: PaddingValues = FlyingBlueFloatingActionButtonDefault.ContentPadding,
|
||||
elevation: ButtonElevation? = ButtonDefaults.elevation(
|
||||
defaultElevation = 0.dp,
|
||||
pressedElevation = 0.dp,
|
||||
disabledElevation = 0.dp,
|
||||
),
|
||||
shape: Shape = CircleShape,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
colors: ButtonColors = ButtonDefaults.buttonColors(),
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
shape: Shape = ButtonDefaults.outlinedShape,
|
||||
colors: ButtonColors = ButtonDefaults.outlinedButtonColors(),
|
||||
elevation: ButtonElevation? = null,
|
||||
border: BorderStroke? = BorderStroke(
|
||||
width = 2.dp,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
contentPadding: PaddingValues = PaddingValues(all = 0.dp),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
expended: Boolean,
|
||||
innerSpacing: Dp = 8.dp,
|
||||
icon: @Composable (RowScope.() -> Unit),
|
||||
text: @Composable (RowScope.() -> Unit),
|
||||
) {
|
||||
LocalButton(
|
||||
OutlinedButton(
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
|
|
@ -51,13 +50,16 @@ fun FloatingActionButton(
|
|||
shape = shape,
|
||||
colors = colors,
|
||||
contentPadding = contentPadding,
|
||||
border = border,
|
||||
content = {
|
||||
FabContent(
|
||||
expended = expended,
|
||||
innerSpacing = innerSpacing,
|
||||
icon = icon,
|
||||
text = text,
|
||||
)
|
||||
BoxWithConstraints {
|
||||
FabContent(
|
||||
expended = expended,
|
||||
innerSpacing = innerSpacing,
|
||||
icon = icon,
|
||||
text = text,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -69,9 +71,10 @@ private fun BoxWithConstraintsScope.FabContent(
|
|||
icon: @Composable (RowScope.() -> Unit),
|
||||
text: @Composable (RowScope.() -> Unit),
|
||||
) {
|
||||
val maxWidth = if (LocalView.current.isInEditMode) 300.dp else maxWidth
|
||||
val width by animateDpAsState(
|
||||
label = "FabContentWidth",
|
||||
targetValue = if (expended) maxWidth else minWidth,
|
||||
targetValue = if (expended) maxWidth else 56.dp,
|
||||
animationSpec = when (expended) {
|
||||
true -> tween(durationMillis = 300, easing = FastOutSlowInEasing)
|
||||
else -> tween(durationMillis = 300, delayMillis = 100, easing = FastOutSlowInEasing)
|
||||
|
|
@ -105,56 +108,6 @@ private fun BoxWithConstraintsScope.FabContent(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun LocalButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
elevation: ButtonElevation? = ButtonDefaults.elevation(),
|
||||
shape: Shape = MaterialTheme.shapes.small,
|
||||
border: BorderStroke? = null,
|
||||
colors: ButtonColors = ButtonDefaults.buttonColors(),
|
||||
contentPadding: PaddingValues = FlyingBlueFloatingActionButtonDefault.ContentPadding,
|
||||
content: @Composable BoxWithConstraintsScope.() -> Unit
|
||||
) {
|
||||
val contentColor by colors.contentColor(enabled)
|
||||
Surface(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
shape = shape,
|
||||
color = colors.backgroundColor(enabled).value,
|
||||
contentColor = contentColor.copy(alpha = 1f),
|
||||
border = border,
|
||||
elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
|
||||
interactionSource = interactionSource,
|
||||
) {
|
||||
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
|
||||
ProvideTextStyle(
|
||||
value = MaterialTheme.typography.button
|
||||
) {
|
||||
BoxWithConstraints(
|
||||
Modifier
|
||||
.defaultMinSize(
|
||||
minWidth = FlyingBlueFloatingActionButtonDefault.MinWidth,
|
||||
minHeight = FlyingBlueFloatingActionButtonDefault.MinHeight
|
||||
)
|
||||
.padding(contentPadding),
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object FlyingBlueFloatingActionButtonDefault {
|
||||
val ContentPadding = PaddingValues(all = 0.dp)
|
||||
val MinWidth = 56.dp
|
||||
val MinHeight = 56.dp
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
private fun FloatingActionButtonPreview(
|
||||
|
|
@ -162,6 +115,7 @@ private fun FloatingActionButtonPreview(
|
|||
) {
|
||||
LexiconTheme {
|
||||
FloatingActionButton(
|
||||
modifier = Modifier.padding(all = 16.dp),
|
||||
expended = expended,
|
||||
icon = {
|
||||
Icon(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,168 @@
|
|||
package com.pixelized.rplexicon.ui.composable.form
|
||||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.with
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
||||
|
||||
@Stable
|
||||
data class DropDownFieldUio<T>(
|
||||
@StringRes val label: Int,
|
||||
val values: List<Pair<T, Int>>,
|
||||
val value: State<Pair<T?, String>>,
|
||||
val onValueChange: (T?, String) -> Unit,
|
||||
) {
|
||||
companion object {
|
||||
fun <T> preview(@StringRes label: Int, id: T?, value: String) = DropDownFieldUio(
|
||||
label = label,
|
||||
values = emptyList(),
|
||||
value = mutableStateOf(id to value),
|
||||
onValueChange = { _, _ -> },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun <T> DropDownField(
|
||||
modifier: Modifier = Modifier,
|
||||
field: DropDownFieldUio<T>,
|
||||
) {
|
||||
var expended by remember(field) { mutableStateOf(false) }
|
||||
|
||||
ExposedDropdownMenuBox(
|
||||
modifier = modifier,
|
||||
expanded = expended,
|
||||
onExpandedChange = { expended = !expended && field.value.value.first == null },
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.menuAnchor(),
|
||||
shape = MaterialTheme.lexicon.shapes.textField,
|
||||
readOnly = true,
|
||||
singleLine = true,
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(id = field.label)
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
AnimatedContent(
|
||||
modifier = Modifier.size(size = 48.dp),
|
||||
targetState = field.value.value.first != null,
|
||||
transitionSpec = { fadeIn() with fadeOut() },
|
||||
label = "DropDownFieldTrailingIconAnimation",
|
||||
) {
|
||||
when (it) {
|
||||
true -> IconButton(
|
||||
onClick = { field.onValueChange(null, "") },
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(size = 18.dp),
|
||||
painter = painterResource(id = R.drawable.ic_clear_24),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
else -> Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(size = 24.dp),
|
||||
painter = painterResource(id = R.drawable.ic_arrow_down_24),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
),
|
||||
value = field.value.value.second,
|
||||
onValueChange = {},
|
||||
)
|
||||
|
||||
DropdownMenu(
|
||||
expanded = expended,
|
||||
onDismissRequest = { expended = false },
|
||||
) {
|
||||
field.values.forEach {
|
||||
val label = stringResource(id = it.second)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
expended = false
|
||||
field.onValueChange(it.first, label)
|
||||
},
|
||||
text = {
|
||||
Text(text = label)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = UI_MODE_NIGHT_NO)
|
||||
@Preview(uiMode = UI_MODE_NIGHT_YES)
|
||||
private fun DropDownFieldPreview(
|
||||
@PreviewParameter(DropDownFieldPreviewProvider::class) preview: Pair<String?, String>,
|
||||
) {
|
||||
LexiconTheme {
|
||||
Surface {
|
||||
DropDownField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 8.dp),
|
||||
field = DropDownFieldUio(
|
||||
label = R.string.lexicon_search,
|
||||
values = emptyList(),
|
||||
value = remember { mutableStateOf(preview) },
|
||||
onValueChange = { _, _ -> },
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DropDownFieldPreviewProvider : PreviewParameterProvider<Pair<String?, String>> {
|
||||
override val values: Sequence<Pair<String?, String>> = sequenceOf(null to "", "" to "preview")
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package com.pixelized.rplexicon.ui.composable.form
|
||||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
||||
|
||||
@Stable
|
||||
data class TextFieldUio(
|
||||
@StringRes val label: Int,
|
||||
val value: State<String>,
|
||||
val onValueChange: (String) -> Unit,
|
||||
) {
|
||||
companion object {
|
||||
fun preview(@StringRes label: Int) = TextFieldUio(
|
||||
label = label,
|
||||
value = mutableStateOf(""),
|
||||
onValueChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TextField(
|
||||
modifier: Modifier = Modifier,
|
||||
field: TextFieldUio,
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = modifier,
|
||||
shape = MaterialTheme.lexicon.shapes.textField,
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
),
|
||||
label = {
|
||||
Text(text = stringResource(id = field.label))
|
||||
},
|
||||
trailingIcon = {
|
||||
AnimatedVisibility(
|
||||
visible = field.value.value.isNotEmpty(),
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
IconButton(onClick = { field.onValueChange("") }) {
|
||||
Icon(
|
||||
modifier = Modifier.size(size = 18.dp),
|
||||
painter = painterResource(id = R.drawable.ic_clear_24),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
value = field.value.value,
|
||||
onValueChange = field.onValueChange,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = UI_MODE_NIGHT_NO)
|
||||
@Preview(uiMode = UI_MODE_NIGHT_YES)
|
||||
private fun TextFieldPreview(
|
||||
@PreviewParameter(TextFieldPreviewProvider::class) preview: String,
|
||||
) {
|
||||
LexiconTheme {
|
||||
Surface {
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 8.dp),
|
||||
field = TextFieldUio(
|
||||
label = R.string.lexicon_search,
|
||||
value = remember { mutableStateOf(preview) },
|
||||
onValueChange = {},
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TextFieldPreviewProvider : PreviewParameterProvider<String> {
|
||||
override val values: Sequence<String> = sequenceOf("", "preview")
|
||||
}
|
||||
|
|
@ -5,9 +5,9 @@ import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
|||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -16,13 +16,13 @@ import androidx.compose.foundation.layout.Spacer
|
|||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -71,9 +71,12 @@ fun AuthenticationScreen(
|
|||
val state = authenticationVM.rememberAuthenticationState()
|
||||
|
||||
Surface {
|
||||
PartyBackground()
|
||||
|
||||
AuthenticationScreenContent(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
.padding(all = 16.dp),
|
||||
version = versionVM.version,
|
||||
onGoogleSignIn = {
|
||||
|
|
@ -108,22 +111,19 @@ private fun AuthenticationScreenContent(
|
|||
) {
|
||||
val typography = MaterialTheme.typography
|
||||
|
||||
PartyBackground()
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.Bottom),
|
||||
horizontalAlignment = Alignment.End,
|
||||
) {
|
||||
Button(
|
||||
OutlinedButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
width = 2.dp,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
shape = CircleShape,
|
||||
),
|
||||
colors = ButtonDefaults.outlinedButtonColors(),
|
||||
.height(56.dp),
|
||||
border = BorderStroke(
|
||||
width = 2.dp,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
onClick = onGoogleSignIn,
|
||||
) {
|
||||
Text(text = rememeberGoogleStringResource())
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import android.net.Uri
|
|||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -17,9 +16,9 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
|
|
@ -40,6 +39,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -47,6 +47,7 @@ import androidx.compose.ui.graphics.ColorFilter
|
|||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.graphics.Shadow
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -58,8 +59,10 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import com.pixelized.rplexicon.utilitary.rememberLoadingTransition
|
||||
import com.skydoves.landscapist.ImageOptions
|
||||
import com.skydoves.landscapist.glide.GlideImage
|
||||
import com.skydoves.landscapist.glide.GlideImageState
|
||||
|
||||
@Stable
|
||||
data class CharacterDetailUio(
|
||||
|
|
@ -83,7 +86,6 @@ fun CharacterDetailScreen(
|
|||
modifier = Modifier.fillMaxSize(),
|
||||
item = viewModel.character,
|
||||
onBack = { screen.popBackStack() },
|
||||
onImage = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -95,7 +97,6 @@ private fun CharacterDetailScreenContent(
|
|||
state: ScrollState = rememberScrollState(),
|
||||
item: State<CharacterDetailUio>,
|
||||
onBack: () -> Unit,
|
||||
onImage: (Uri) -> Unit,
|
||||
) {
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
val typography = MaterialTheme.typography
|
||||
|
|
@ -129,9 +130,16 @@ private fun CharacterDetailScreenContent(
|
|||
.aspectRatio(ratio = 1f)
|
||||
.scrollOffset(scrollState = state) { -it / 2 },
|
||||
) {
|
||||
val transition = rememberLoadingTransition { uri }
|
||||
GlideImage(
|
||||
modifier = Modifier.matchParentSize(),
|
||||
imageModel = { uri.toString() },
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.alpha(alpha = transition.alpha),
|
||||
onImageStateChanged = {
|
||||
if (it is GlideImageState.Success) {
|
||||
transition.target = 1f
|
||||
}
|
||||
},
|
||||
imageOptions = ImageOptions(
|
||||
alignment = Alignment.TopCenter,
|
||||
contentScale = ContentScale.Crop,
|
||||
|
|
@ -141,7 +149,8 @@ private fun CharacterDetailScreenContent(
|
|||
)
|
||||
},
|
||||
),
|
||||
previewPlaceholder = R.drawable.ic_empty,
|
||||
imageModel = { uri.toString() },
|
||||
previewPlaceholder = R.drawable.im_brulkhai,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
|
@ -220,6 +229,8 @@ private fun CharacterDetailScreenContent(
|
|||
)
|
||||
}
|
||||
if (item.value.portrait.isNotEmpty()) {
|
||||
val configuration = LocalConfiguration.current
|
||||
val maxSize = remember { (configuration.screenWidthDp.dp - 16.dp * 2) }
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
|
||||
style = typography.titleMedium,
|
||||
|
|
@ -230,15 +241,21 @@ private fun CharacterDetailScreenContent(
|
|||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
items(items = item.value.portrait) {
|
||||
val transition = rememberLoadingTransition { it }
|
||||
GlideImage(
|
||||
modifier = Modifier
|
||||
.clickable { onImage(it) }
|
||||
.height(320.dp),
|
||||
imageModel = { it },
|
||||
.sizeIn(maxWidth = maxSize, maxHeight = maxSize)
|
||||
.alpha(alpha = transition.alpha),
|
||||
onImageStateChanged = {
|
||||
if (it is GlideImageState.Success) {
|
||||
transition.target = 1f
|
||||
}
|
||||
},
|
||||
imageOptions = ImageOptions(
|
||||
contentScale = ContentScale.FillHeight
|
||||
),
|
||||
previewPlaceholder = R.drawable.ic_empty,
|
||||
imageModel = { it },
|
||||
previewPlaceholder = R.drawable.im_brulkhai,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -285,15 +302,6 @@ private fun CharacterDetailScreenContentPreview() {
|
|||
race = R.string.race_half_orc,
|
||||
portrait = listOf(
|
||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/889/large/bayard-wu-0716.jpg?1468642855"),
|
||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/877/large/bayard-wu-0714.jpg?1468642665"),
|
||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/887/large/bayard-wu-0715.jpg?1468642839"),
|
||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/891/large/bayard-wu-0623-03.jpg?1468642872"),
|
||||
Uri.parse("https://cdna.artstation.com/p/assets/images/images/002/869/868/large/bayard-wu-0622-03.jpg?1466664135"),
|
||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/002/869/871/large/bayard-wu-0622-04.jpg?1466664153"),
|
||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/347/181/large/bayard-wu-1217.jpg?1482770883"),
|
||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/297/635/large/bayard-wu-1215.jpg?1482166826"),
|
||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/297/631/large/bayard-wu-1209.jpg?1482166803"),
|
||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/297/641/large/bayard-wu-1212.jpg?1482166838"),
|
||||
),
|
||||
description = "Brulkhai, ou plus simplement Bru, est solidement bâti. Elle mesure 192 cm pour 110 kg de muscles lorsqu’elle est en bonne santé. Elle a les cheveux châtains, les yeux noisettes et la peau couleur gris-vert typique de son espèce. D’un tempérament taciturne, elle parle peu et de façon concise. Elle est parfois brutale, aussi bien physiquement que verbalement, Elle ne prend cependant aucun plaisir à malmener ceux qu’elle considère plus faibles qu’elle. D’une nature simple et honnête, elle ne mâche pas ses mots et ne dissimule généralement pas ses pensées. Son intelligence modeste est plus le reflet d’un manque d’éducation et d’une capacité limitée à gérer ses émotions qu’à une débilité congénitale. Elle voue à la force un culte car c’est par son expression qu’elle se sent vraiment vivante et éprouve de grandes difficultés vis à vis de ceux qu’elle nomme foshnu (bébé, chouineur en commun).",
|
||||
history = null,
|
||||
|
|
@ -304,7 +312,6 @@ private fun CharacterDetailScreenContentPreview() {
|
|||
modifier = Modifier.fillMaxSize(),
|
||||
item = character,
|
||||
onBack = { },
|
||||
onImage = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,49 +55,53 @@ fun LexiconItem(
|
|||
) {
|
||||
val typography = MaterialTheme.typography
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
Surface(modifier = modifier) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = remember { typography.bodyLarge.copy(fontWeight = FontWeight.Bold) },
|
||||
maxLines = 1,
|
||||
text = item.name,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = typography.labelMedium,
|
||||
maxLines = 1,
|
||||
text = item.diminutive ?: ""
|
||||
)
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||
maxLines = 1,
|
||||
text = stringResource(id = item.gender)
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||
maxLines = 1,
|
||||
text = stringResource(id = item.race)
|
||||
)
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = remember { typography.bodyLarge.copy(fontWeight = FontWeight.Bold) },
|
||||
maxLines = 1,
|
||||
text = item.name,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = typography.labelMedium,
|
||||
maxLines = 1,
|
||||
text = item.diminutive ?: ""
|
||||
)
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||
maxLines = 1,
|
||||
text = stringResource(id = item.gender)
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||
maxLines = 1,
|
||||
text = stringResource(id = item.race)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -110,9 +114,6 @@ private fun LexiconItemContentPreview() {
|
|||
LexiconTheme {
|
||||
Surface {
|
||||
LexiconItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
item = LexiconItemUio(
|
||||
id = 0,
|
||||
name = "Brulkhai",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
|||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
|
|
@ -17,7 +18,9 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
|
|
@ -30,6 +33,7 @@ import androidx.compose.material.pullrefresh.PullRefreshState
|
|||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -46,12 +50,15 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.rplexicon.LocalSnack
|
||||
import com.pixelized.rplexicon.NO_WINDOW_INSETS
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.ui.composable.FloatingActionButton
|
||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToCharacterDetail
|
||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToSearch
|
||||
|
|
@ -100,6 +107,7 @@ fun LexiconScreen(
|
|||
|
||||
Surface {
|
||||
LexiconScreenContent(
|
||||
modifier = Modifier.systemBarsPadding(),
|
||||
items = viewModel.items,
|
||||
lazyColumnState = lazyListState,
|
||||
refreshState = refresh,
|
||||
|
|
@ -139,35 +147,44 @@ private fun LexiconScreenContent(
|
|||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
contentWindowInsets = NO_WINDOW_INSETS,
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
windowInsets = NO_WINDOW_INSETS,
|
||||
title = {
|
||||
Text(text = stringResource(id = R.string.app_name))
|
||||
},
|
||||
)
|
||||
},
|
||||
// floatingActionButton = {
|
||||
// FloatingActionButton(
|
||||
// modifier = Modifier.padding(start = 32.dp),
|
||||
// expended = isFabExpended.value,
|
||||
// onClick = onSearch,
|
||||
// icon = {
|
||||
// Icon(
|
||||
// tint = MaterialTheme.colorScheme.onPrimary,
|
||||
// painter = painterResource(id = R.drawable.ic_baseline_search_24),
|
||||
// contentDescription = null,
|
||||
// )
|
||||
// },
|
||||
// text = {
|
||||
// val typography = MaterialTheme.typography
|
||||
// Text(
|
||||
// color = MaterialTheme.colorScheme.onPrimary,
|
||||
// style = remember { typography.bodyLarge.copy(fontWeight = FontWeight.Bold) },
|
||||
// text = "Rechercher",
|
||||
// )
|
||||
// },
|
||||
// )
|
||||
// }
|
||||
floatingActionButton = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.padding(start = 32.dp), // `Fix` Scaffold content size for FAB.
|
||||
contentAlignment = Alignment.CenterEnd,
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = items.value.isNotEmpty(),
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
FloatingActionButton(
|
||||
expended = isFabExpended.value,
|
||||
onClick = onSearch,
|
||||
icon = {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_baseline_search_24),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(text = stringResource(id = R.string.lexicon_search))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) { padding ->
|
||||
Box(
|
||||
modifier = Modifier.padding(paddingValues = padding),
|
||||
|
|
@ -186,15 +203,12 @@ private fun LexiconScreenContent(
|
|||
state = lazyColumnState,
|
||||
contentPadding = PaddingValues(
|
||||
top = 8.dp,
|
||||
bottom = 8.dp + 16.dp // + 56.dp + 16.dp,
|
||||
bottom = 8.dp + 16.dp + 56.dp + 16.dp,
|
||||
),
|
||||
) {
|
||||
items(count = 6) {
|
||||
LexiconItem(
|
||||
modifier = Modifier
|
||||
.animateItemPlacement()
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
item = LexiconItemUio.Brulkhai,
|
||||
)
|
||||
}
|
||||
|
|
@ -207,7 +221,7 @@ private fun LexiconScreenContent(
|
|||
state = lazyColumnState,
|
||||
contentPadding = PaddingValues(
|
||||
top = 8.dp,
|
||||
bottom = 8.dp + 16.dp // + 56.dp + 16.dp,
|
||||
bottom = 8.dp + 16.dp + 56.dp + 16.dp,
|
||||
),
|
||||
) {
|
||||
items(
|
||||
|
|
@ -218,9 +232,7 @@ private fun LexiconScreenContent(
|
|||
LexiconItem(
|
||||
modifier = Modifier
|
||||
.animateItemPlacement()
|
||||
.clickable { onItem(it) }
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
.clickable { onItem(it) },
|
||||
item = it,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,46 +2,79 @@ package com.pixelized.rplexicon.ui.screens.search
|
|||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ExposedDropdownMenuBox
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.rplexicon.NO_WINDOW_INSETS
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.model.Lexicon
|
||||
import com.pixelized.rplexicon.ui.composable.form.DropDownField
|
||||
import com.pixelized.rplexicon.ui.composable.form.DropDownFieldUio
|
||||
import com.pixelized.rplexicon.ui.composable.form.TextField
|
||||
import com.pixelized.rplexicon.ui.composable.form.TextFieldUio
|
||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToCharacterDetail
|
||||
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconItem
|
||||
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconItemUio
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
|
||||
@Stable
|
||||
data class SearchFormUio(
|
||||
val search: TextFieldUio,
|
||||
val gender: DropDownFieldUio<Lexicon.Gender>,
|
||||
val race: DropDownFieldUio<Lexicon.Race>,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun SearchScreen() {
|
||||
fun SearchScreen(
|
||||
viewModel: SearchViewModel = hiltViewModel(),
|
||||
) {
|
||||
val screen = LocalScreenNavHost.current
|
||||
|
||||
Surface {
|
||||
SearchScreenContent(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding()
|
||||
.imePadding(),
|
||||
items = viewModel.filter,
|
||||
form = viewModel.form,
|
||||
onItem = {
|
||||
screen.navigateToCharacterDetail(id = it.id)
|
||||
},
|
||||
onBack = {
|
||||
screen.popBackStack()
|
||||
}
|
||||
|
|
@ -49,17 +82,23 @@ fun SearchScreen() {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun SearchScreenContent(
|
||||
modifier: Modifier = Modifier,
|
||||
lazyColumnState: LazyListState = rememberLazyListState(),
|
||||
items: State<List<LexiconItemUio>>,
|
||||
form: SearchFormUio,
|
||||
onBack: () -> Unit,
|
||||
onItem: (LexiconItemUio) -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
contentWindowInsets = NO_WINDOW_INSETS,
|
||||
containerColor = Color.Transparent,
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
windowInsets = NO_WINDOW_INSETS,
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBack) {
|
||||
Icon(
|
||||
|
|
@ -69,99 +108,52 @@ private fun SearchScreenContent(
|
|||
}
|
||||
},
|
||||
title = {
|
||||
Text(text = "Rechercher")
|
||||
Text(text = stringResource(id = R.string.search_field_title))
|
||||
},
|
||||
)
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues = it)
|
||||
.padding(all = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues = paddingValues),
|
||||
state = lazyColumnState,
|
||||
contentPadding = PaddingValues(vertical = 8.dp),
|
||||
) {
|
||||
TextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = "",
|
||||
label = {
|
||||
Text("Nom")
|
||||
},
|
||||
onValueChange = { _ -> },
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
),
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
DropDownField(
|
||||
modifier = Modifier.weight(1f),
|
||||
subject = remember { mutableStateOf("1") },
|
||||
subjects = listOf("1", "2"),
|
||||
onChange = { },
|
||||
expanded = remember { mutableStateOf(false) },
|
||||
onExpandedChange = { }
|
||||
)
|
||||
DropDownField(
|
||||
modifier = Modifier.weight(1f),
|
||||
subject = remember { mutableStateOf("1") },
|
||||
subjects = listOf("1", "2"),
|
||||
onChange = { },
|
||||
expanded = remember { mutableStateOf(false) },
|
||||
onExpandedChange = { }
|
||||
)
|
||||
item {
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
TextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
field = form.search,
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
DropDownField(
|
||||
modifier = Modifier.weight(1f),
|
||||
field = form.gender,
|
||||
)
|
||||
DropDownField(
|
||||
modifier = Modifier.weight(1f),
|
||||
field = form.race,
|
||||
)
|
||||
}
|
||||
Divider(modifier = Modifier.padding(top = 16.dp, bottom = 8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun DropDownField(
|
||||
modifier: Modifier = Modifier,
|
||||
subjects: List<String>,
|
||||
subject: State<String>,
|
||||
onChange: (String) -> Unit,
|
||||
expanded: State<Boolean>,
|
||||
onExpandedChange: (Boolean) -> Unit
|
||||
) {
|
||||
ExposedDropdownMenuBox(
|
||||
modifier = modifier,
|
||||
expanded = expanded.value,
|
||||
onExpandedChange = onExpandedChange,
|
||||
) {
|
||||
TextField(
|
||||
modifier = Modifier.clickable { onExpandedChange(true) },
|
||||
readOnly = true,
|
||||
singleLine = true,
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "pouet"
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_baseline_arrow_drop_down_24),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
|
||||
),
|
||||
value = subject.value,
|
||||
onValueChange = {},
|
||||
)
|
||||
|
||||
ExposedDropdownMenu(
|
||||
expanded = expanded.value,
|
||||
onDismissRequest = { onExpandedChange(false) },
|
||||
) {
|
||||
subjects.forEach {
|
||||
DropdownMenuItem(
|
||||
onClick = { onChange(it) },
|
||||
content = { Text(text = it) },
|
||||
items(
|
||||
items = items.value,
|
||||
key = { it.id },
|
||||
contentType = { "Lexicon" },
|
||||
) {
|
||||
LexiconItem(
|
||||
modifier = Modifier
|
||||
.animateItemPlacement()
|
||||
.clickable { onItem(it) },
|
||||
item = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -173,9 +165,37 @@ fun DropDownField(
|
|||
@Preview(uiMode = UI_MODE_NIGHT_YES)
|
||||
private fun SearchScreenContentPreview() {
|
||||
LexiconTheme {
|
||||
val context = LocalContext.current
|
||||
Surface {
|
||||
SearchScreenContent(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
form = SearchFormUio(
|
||||
search = TextFieldUio.preview(R.string.search_field_search),
|
||||
gender = DropDownFieldUio.preview(
|
||||
label = R.string.search_field_gender,
|
||||
id = Lexicon.Gender.FEMALE,
|
||||
value = context.getString(R.string.gender_female),
|
||||
),
|
||||
race = DropDownFieldUio.preview(
|
||||
label = R.string.search_field_race,
|
||||
id = null,
|
||||
value = "",
|
||||
),
|
||||
),
|
||||
items = remember {
|
||||
mutableStateOf(
|
||||
listOf(
|
||||
LexiconItemUio(
|
||||
id = 0,
|
||||
name = "Brulkhai",
|
||||
diminutive = "Bru",
|
||||
gender = R.string.gender_female_short,
|
||||
race = R.string.race_half_orc,
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
onItem = { },
|
||||
onBack = { },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
package com.pixelized.rplexicon.ui.screens.search
|
||||
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.model.Lexicon
|
||||
import com.pixelized.rplexicon.model.Lexicon.Gender
|
||||
import com.pixelized.rplexicon.model.Lexicon.Race
|
||||
import com.pixelized.rplexicon.repository.LexiconRepository
|
||||
import com.pixelized.rplexicon.ui.composable.form.DropDownFieldUio
|
||||
import com.pixelized.rplexicon.ui.composable.form.TextFieldUio
|
||||
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconItemUio
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SearchViewModel @Inject constructor(
|
||||
private val repository: LexiconRepository,
|
||||
) : ViewModel() {
|
||||
private val search = mutableStateOf("")
|
||||
private val gender = mutableStateOf<Pair<Gender?, String>>(null to "")
|
||||
private val race = mutableStateOf<Pair<Race?, String>>(null to "")
|
||||
|
||||
val form = SearchFormUio(
|
||||
search = TextFieldUio(
|
||||
label = R.string.search_field_search,
|
||||
value = search,
|
||||
onValueChange = {
|
||||
search.value = it
|
||||
}
|
||||
),
|
||||
gender = DropDownFieldUio(
|
||||
label = R.string.search_field_gender,
|
||||
values = genders(),
|
||||
value = gender,
|
||||
onValueChange = { id, value -> gender.value = id to value },
|
||||
),
|
||||
race = DropDownFieldUio(
|
||||
label = R.string.search_field_race,
|
||||
values = races(),
|
||||
value = race,
|
||||
onValueChange = { id, value -> race.value = id to value },
|
||||
),
|
||||
)
|
||||
|
||||
private var data: List<Lexicon> = repository.data.value
|
||||
|
||||
val filter = derivedStateOf {
|
||||
data
|
||||
.filter { item ->
|
||||
val gender = gender.value.first?.let { it == item.gender }
|
||||
val race = race.value.first?.let { it == item.race }
|
||||
val search = search.value.takeIf { it.isNotEmpty() }?.let {
|
||||
val name = item.name.contains(search.value, true)
|
||||
val diminutive = item.diminutive?.contains(search.value, true) == true
|
||||
val description = item.description?.contains(search.value, true) == true
|
||||
val history = item.history?.contains(search.value, true) == true
|
||||
name || diminutive || description || history
|
||||
}
|
||||
(gender == null || gender) && (race == null || race) && (search == null || search)
|
||||
}.map {
|
||||
LexiconItemUio(
|
||||
id = it.id,
|
||||
name = it.name,
|
||||
diminutive = it.diminutive?.takeIf { it.isNotBlank() }?.let { "./ $it" },
|
||||
gender = when (it.gender) {
|
||||
Gender.MALE -> R.string.gender_male_short
|
||||
Gender.FEMALE -> R.string.gender_female_short
|
||||
Gender.UNDETERMINED -> R.string.gender_undetermined_short
|
||||
},
|
||||
race = when (it.race) {
|
||||
Race.ELF -> R.string.race_elf
|
||||
Race.HALFLING -> R.string.race_halfling
|
||||
Race.HUMAN -> R.string.race_human
|
||||
Race.DWARF -> R.string.race_dwarf
|
||||
Race.HALF_ELF -> R.string.race_half_elf
|
||||
Race.HALF_ORC -> R.string.race_half_orc
|
||||
Race.DRAGONBORN -> R.string.race_dragonborn
|
||||
Race.GNOME -> R.string.race_gnome
|
||||
Race.TIEFLING -> R.string.race_tiefling
|
||||
Race.AARAKOCRA -> R.string.race_aarakocra
|
||||
Race.GENASI -> R.string.race_genasi
|
||||
Race.DEEP_GNOME -> R.string.race_deep_gnome
|
||||
Race.GOLIATH -> R.string.race_goliath
|
||||
Race.UNDETERMINED -> R.string.race_undetermined
|
||||
},
|
||||
)
|
||||
}
|
||||
.sortedBy { it.name }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun genders() = listOf(
|
||||
Gender.MALE to R.string.gender_male,
|
||||
Gender.FEMALE to R.string.gender_female,
|
||||
Gender.UNDETERMINED to R.string.gender_undetermined,
|
||||
)
|
||||
|
||||
private fun races() = listOf(
|
||||
Race.ELF to R.string.race_elf,
|
||||
Race.HALFLING to R.string.race_halfling,
|
||||
Race.HUMAN to R.string.race_human,
|
||||
Race.DWARF to R.string.race_dwarf,
|
||||
Race.HALF_ELF to R.string.race_half_elf,
|
||||
Race.HALF_ORC to R.string.race_half_orc,
|
||||
Race.DRAGONBORN to R.string.race_dragonborn,
|
||||
Race.GNOME to R.string.race_gnome,
|
||||
Race.TIEFLING to R.string.race_tiefling,
|
||||
Race.AARAKOCRA to R.string.race_aarakocra,
|
||||
Race.GENASI to R.string.race_genasi,
|
||||
Race.DEEP_GNOME to R.string.race_deep_gnome,
|
||||
Race.GOLIATH to R.string.race_goliath,
|
||||
Race.UNDETERMINED to R.string.race_undetermined,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,11 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.pixelized.rplexicon.ui.theme.colors.LexiconColors
|
||||
import com.pixelized.rplexicon.ui.theme.colors.darkColorScheme
|
||||
import com.pixelized.rplexicon.ui.theme.colors.lightColorScheme
|
||||
import com.pixelized.rplexicon.ui.theme.shape.LexiconShapes
|
||||
import com.pixelized.rplexicon.ui.theme.shape.lexiconShapes
|
||||
|
||||
val LocalLexiconTheme = compositionLocalOf<LexiconTheme> {
|
||||
error("LocalLexiconTheme not ready yet.")
|
||||
|
|
@ -20,6 +25,7 @@ val LocalLexiconTheme = compositionLocalOf<LexiconTheme> {
|
|||
@Stable
|
||||
data class LexiconTheme(
|
||||
val colorScheme: LexiconColors,
|
||||
val shapes: LexiconShapes,
|
||||
)
|
||||
|
||||
@Composable
|
||||
|
|
@ -32,7 +38,8 @@ fun LexiconTheme(
|
|||
colorScheme = when (darkTheme) {
|
||||
true -> darkColorScheme()
|
||||
else -> lightColorScheme()
|
||||
}
|
||||
},
|
||||
shapes = lexiconShapes()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -40,10 +47,8 @@ fun LexiconTheme(
|
|||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
lexiconTheme.colorScheme.base.background.toArgb().let {
|
||||
window.statusBarColor = it
|
||||
window.navigationBarColor = it
|
||||
}
|
||||
window.statusBarColor = lexiconTheme.colorScheme.status.toArgb()
|
||||
window.navigationBarColor = lexiconTheme.colorScheme.navigation.toArgb()
|
||||
WindowCompat.getInsetsController(window, view).let {
|
||||
it.isAppearanceLightStatusBars = !darkTheme
|
||||
it.isAppearanceLightNavigationBars = !darkTheme
|
||||
|
|
@ -56,6 +61,7 @@ fun LexiconTheme(
|
|||
) {
|
||||
MaterialTheme(
|
||||
colorScheme = lexiconTheme.colorScheme.base,
|
||||
shapes = lexiconTheme.shapes.base,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.rplexicon.ui.theme
|
||||
package com.pixelized.rplexicon.ui.theme.colors
|
||||
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
|
|
@ -6,13 +6,13 @@ import androidx.compose.material3.lightColorScheme
|
|||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.pixelized.rplexicon.ui.theme.colors.BaseDark
|
||||
import com.pixelized.rplexicon.ui.theme.colors.BaseLight
|
||||
|
||||
@Stable
|
||||
@Immutable
|
||||
class LexiconColors(
|
||||
val base: ColorScheme,
|
||||
val status: Color,
|
||||
val navigation: Color,
|
||||
val placeholder: Color,
|
||||
)
|
||||
|
||||
|
|
@ -24,9 +24,13 @@ fun darkColorScheme(
|
|||
tertiary = BaseDark.Pink80,
|
||||
onPrimary = Color.White,
|
||||
),
|
||||
status: Color = Color.Transparent,
|
||||
navigation: Color = Color.Transparent,
|
||||
placeholder: Color = Color(red = 49, green = 48, blue = 51),
|
||||
) = LexiconColors(
|
||||
base = base,
|
||||
status = status,
|
||||
navigation = navigation,
|
||||
placeholder = placeholder,
|
||||
)
|
||||
|
||||
|
|
@ -38,8 +42,12 @@ fun lightColorScheme(
|
|||
tertiary = BaseLight.Pink40,
|
||||
onPrimary = Color.White,
|
||||
),
|
||||
status: Color = Color.Transparent,
|
||||
navigation: Color = Color.Transparent,
|
||||
placeholder: Color = Color(red = 230, green = 225, blue = 229),
|
||||
) = LexiconColors(
|
||||
base = base,
|
||||
status = status,
|
||||
navigation = navigation,
|
||||
placeholder = placeholder,
|
||||
)
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.pixelized.rplexicon.ui.theme.shape
|
||||
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Shapes
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
|
||||
@Stable
|
||||
class LexiconShapes(
|
||||
val base: Shapes,
|
||||
val textField: Shape
|
||||
)
|
||||
|
||||
@Stable
|
||||
fun lexiconShapes(
|
||||
base: Shapes = Shapes(),
|
||||
textField: Shape = CircleShape,
|
||||
) = LexiconShapes(
|
||||
base = base,
|
||||
textField = textField,
|
||||
)
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.pixelized.rplexicon.utilitary
|
||||
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
|
||||
@Stable
|
||||
class GlideLoadingTransition(
|
||||
target: MutableState<Float>,
|
||||
alpha: State<Float>
|
||||
) {
|
||||
var target by target
|
||||
val alpha by alpha
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberLoadingTransition(model: () -> Any?): GlideLoadingTransition {
|
||||
val isInEditMode = LocalView.current.isInEditMode
|
||||
val key = model()
|
||||
val target = remember(key) {
|
||||
mutableStateOf(if (isInEditMode) 1f else 0f)
|
||||
}
|
||||
val alpha = animateFloatAsState(
|
||||
targetValue = target.value,
|
||||
label = "RememberLoadingTransition"
|
||||
)
|
||||
return remember(key) {
|
||||
GlideLoadingTransition(
|
||||
target = target,
|
||||
alpha = alpha,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7,10l5,5 5,-5z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_clear_24.xml
Normal file
5
app/src/main/res/drawable/ic_clear_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
||||
|
|
@ -28,8 +28,15 @@
|
|||
|
||||
<string name="action_google_sign_in">Se connecter avec</string>
|
||||
|
||||
<string name="lexicon_search">Rechercher</string>
|
||||
|
||||
<string name="detail_title">Détails du personnage</string>
|
||||
<string name="detail_description">Description</string>
|
||||
<string name="detail_history">History</string>
|
||||
<string name="detail_history">Histoire</string>
|
||||
<string name="detail_portrait">Portrait</string>
|
||||
|
||||
<string name="search_field_title">Rechercher</string>
|
||||
<string name="search_field_search">Rechercher</string>
|
||||
<string name="search_field_race">Race</string>
|
||||
<string name="search_field_gender">Sexe</string>
|
||||
</resources>
|
||||
|
|
@ -28,8 +28,15 @@
|
|||
|
||||
<string name="action_google_sign_in">Sign in with</string>
|
||||
|
||||
<string name="lexicon_search">Search</string>
|
||||
|
||||
<string name="detail_title">Character\'s details</string>
|
||||
<string name="detail_description">Description</string>
|
||||
<string name="detail_history">Histoire</string>
|
||||
<string name="detail_history">History</string>
|
||||
<string name="detail_portrait">Portrait</string>
|
||||
|
||||
<string name="search_field_title">Search</string>
|
||||
<string name="search_field_search">Search</string>
|
||||
<string name="search_field_race">Race</string>
|
||||
<string name="search_field_gender">Gender</string>
|
||||
</resources>
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Lexique" parent="android:Theme.Material.Light.NoActionBar" />
|
||||
<style name="Theme.Lexicon.Starting" parent="Theme.SplashScreen">
|
||||
<item name="postSplashScreenTheme">@style/Theme.Lexicon.NoActionBar</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Lexicon.NoActionBar" parent="android:Theme.Material.Light.NoActionBar" />
|
||||
</resources>
|
||||
Loading…
Add table
Add a link
Reference in a new issue