Advenced search layout.
BIN
app/src/main/ic_launcher-playstore.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
|
|
@ -1,7 +1,9 @@
|
||||||
package com.pixelized.rplexicon.model
|
package com.pixelized.rplexicon.model
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
|
||||||
|
@Stable
|
||||||
data class Lexicon(
|
data class Lexicon(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|
@ -12,12 +14,15 @@ data class Lexicon(
|
||||||
val description: String?,
|
val description: String?,
|
||||||
val history: String?,
|
val history: String?,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@Stable
|
||||||
enum class Gender {
|
enum class Gender {
|
||||||
MALE,
|
MALE,
|
||||||
FEMALE,
|
FEMALE,
|
||||||
UNDETERMINED
|
UNDETERMINED,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
enum class Race {
|
enum class Race {
|
||||||
ELF,
|
ELF,
|
||||||
HALFLING,
|
HALFLING,
|
||||||
|
|
@ -32,6 +37,6 @@ data class Lexicon(
|
||||||
GENASI,
|
GENASI,
|
||||||
DEEP_GNOME,
|
DEEP_GNOME,
|
||||||
GOLIATH,
|
GOLIATH,
|
||||||
UNDETERMINED
|
UNDETERMINED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.pixelized.rplexicon.ui.composable
|
||||||
|
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
|
|
||||||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
fun ScrollingKeyboardHandler(
|
||||||
|
lazyListState: LazyListState
|
||||||
|
) {
|
||||||
|
val keyboard = LocalSoftwareKeyboardController.current
|
||||||
|
|
||||||
|
if (lazyListState.isScrollInProgress) {
|
||||||
|
keyboard?.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.NavOptionsBuilder
|
import androidx.navigation.NavOptionsBuilder
|
||||||
import androidx.navigation.NavType
|
import androidx.navigation.NavType
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
|
import com.pixelized.rplexicon.model.Lexicon
|
||||||
import com.pixelized.rplexicon.ui.navigation.NavigationAnimation
|
import com.pixelized.rplexicon.ui.navigation.NavigationAnimation
|
||||||
import com.pixelized.rplexicon.ui.navigation.animatedComposable
|
import com.pixelized.rplexicon.ui.navigation.animatedComposable
|
||||||
import com.pixelized.rplexicon.ui.screens.detail.CharacterDetailScreen
|
import com.pixelized.rplexicon.ui.screens.detail.CharacterDetailScreen
|
||||||
|
|
@ -15,17 +16,53 @@ import com.pixelized.rplexicon.utilitary.extentions.ARG
|
||||||
|
|
||||||
private const val ROUTE = "CharacterDetail"
|
private const val ROUTE = "CharacterDetail"
|
||||||
private const val ARG_ID = "id"
|
private const val ARG_ID = "id"
|
||||||
val CHARACTER_DETAIL_ROUTE = "$ROUTE?${ARG_ID.ARG}"
|
private const val ARG_HIGHLIGHT = "highlight"
|
||||||
|
private const val ARG_RACE = "race"
|
||||||
|
private const val ARG_HIGHLIGHT_RACE = "highlightRace"
|
||||||
|
private const val ARG_GENDER = "gender"
|
||||||
|
private const val ARG_HIGHLIGHT_GENDER = "highlightGender"
|
||||||
|
|
||||||
|
//CharacterDetail
|
||||||
|
// ?id=0
|
||||||
|
// &highlight=null
|
||||||
|
// &race=false
|
||||||
|
// &highlightRace=UNDETERMINED
|
||||||
|
// &gender=false
|
||||||
|
// &highlightGender=UNDETERMINED
|
||||||
|
|
||||||
|
|
||||||
|
val CHARACTER_DETAIL_ROUTE = ROUTE +
|
||||||
|
"?${ARG_ID.ARG}" +
|
||||||
|
"&${ARG_HIGHLIGHT.ARG}" +
|
||||||
|
"&${ARG_RACE.ARG}" +
|
||||||
|
"&${ARG_HIGHLIGHT_RACE.ARG}" +
|
||||||
|
"&${ARG_GENDER.ARG}" +
|
||||||
|
"&${ARG_HIGHLIGHT_GENDER.ARG}"
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
@Immutable
|
@Immutable
|
||||||
data class CharacterDetailArgument(
|
data class CharacterDetailArgument(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
|
val highlight: String?,
|
||||||
|
val race: Lexicon.Race,
|
||||||
|
val highlightRace: Boolean,
|
||||||
|
val gender: Lexicon.Gender,
|
||||||
|
val highlightGender: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
val SavedStateHandle.characterDetailArgument: CharacterDetailArgument
|
val SavedStateHandle.characterDetailArgument: CharacterDetailArgument
|
||||||
get() = CharacterDetailArgument(
|
get() = CharacterDetailArgument(
|
||||||
id = get(ARG_ID) ?: error("CharacterDetailArgument argument: id")
|
id = get(ARG_ID)
|
||||||
|
?: error("CharacterDetailArgument argument: $ARG_ID"),
|
||||||
|
race = get(ARG_RACE)
|
||||||
|
?: error("CharacterDetailArgument argument: $ARG_RACE"),
|
||||||
|
highlightRace = get(ARG_HIGHLIGHT_RACE)
|
||||||
|
?: error("CharacterDetailArgument argument: $ARG_HIGHLIGHT_RACE"),
|
||||||
|
gender = get(ARG_GENDER)
|
||||||
|
?: error("CharacterDetailArgument argument: $ARG_GENDER"),
|
||||||
|
highlightGender = get(ARG_HIGHLIGHT_GENDER)
|
||||||
|
?: error("CharacterDetailArgument argument: $ARG_HIGHLIGHT_GENDER"),
|
||||||
|
highlight = get(ARG_HIGHLIGHT),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun NavGraphBuilder.composableCharacterDetail() {
|
fun NavGraphBuilder.composableCharacterDetail() {
|
||||||
|
|
@ -34,7 +71,23 @@ fun NavGraphBuilder.composableCharacterDetail() {
|
||||||
arguments = listOf(
|
arguments = listOf(
|
||||||
navArgument(name = ARG_ID) {
|
navArgument(name = ARG_ID) {
|
||||||
type = NavType.IntType
|
type = NavType.IntType
|
||||||
}
|
},
|
||||||
|
navArgument(name = ARG_HIGHLIGHT) {
|
||||||
|
type = NavType.StringType
|
||||||
|
nullable = true
|
||||||
|
},
|
||||||
|
navArgument(name = ARG_RACE) {
|
||||||
|
type = NavType.EnumType(Lexicon.Race::class.java)
|
||||||
|
},
|
||||||
|
navArgument(name = ARG_HIGHLIGHT_RACE) {
|
||||||
|
type = NavType.BoolType
|
||||||
|
},
|
||||||
|
navArgument(name = ARG_GENDER) {
|
||||||
|
type = NavType.EnumType(Lexicon.Gender::class.java)
|
||||||
|
},
|
||||||
|
navArgument(name = ARG_HIGHLIGHT_GENDER) {
|
||||||
|
type = NavType.BoolType
|
||||||
|
},
|
||||||
),
|
),
|
||||||
animation = NavigationAnimation.Push,
|
animation = NavigationAnimation.Push,
|
||||||
) {
|
) {
|
||||||
|
|
@ -44,8 +97,18 @@ fun NavGraphBuilder.composableCharacterDetail() {
|
||||||
|
|
||||||
fun NavHostController.navigateToCharacterDetail(
|
fun NavHostController.navigateToCharacterDetail(
|
||||||
id: Int,
|
id: Int,
|
||||||
|
highlight: String? = null,
|
||||||
|
race: Lexicon.Race? = null,
|
||||||
|
gender: Lexicon.Gender? = null,
|
||||||
option: NavOptionsBuilder.() -> Unit = {},
|
option: NavOptionsBuilder.() -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val route = "$ROUTE?$ARG_ID=$id"
|
val route = ROUTE +
|
||||||
|
"?$ARG_ID=$id" +
|
||||||
|
"&$ARG_HIGHLIGHT=$highlight" +
|
||||||
|
"&$ARG_RACE=${race ?: Lexicon.Race.UNDETERMINED}" +
|
||||||
|
"&$ARG_HIGHLIGHT_RACE=${race != null}" +
|
||||||
|
"&$ARG_GENDER=${gender ?: Lexicon.Gender.UNDETERMINED}" +
|
||||||
|
"&$ARG_HIGHLIGHT_GENDER=${gender != null}"
|
||||||
|
|
||||||
navigate(route = route, builder = option)
|
navigate(route = route, builder = option)
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,6 @@ package com.pixelized.rplexicon.ui.screens.detail
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.compose.foundation.ScrollState
|
import androidx.compose.foundation.ScrollState
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
|
@ -51,14 +50,23 @@ import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
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 com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
|
import com.pixelized.rplexicon.model.Lexicon
|
||||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
|
import com.pixelized.rplexicon.utilitary.composable.stringResource
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.annotatedSpan
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.annotatedString
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.finderRegex
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.foldAll
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.highlightRegex
|
||||||
import com.pixelized.rplexicon.utilitary.rememberLoadingTransition
|
import com.pixelized.rplexicon.utilitary.rememberLoadingTransition
|
||||||
import com.skydoves.landscapist.ImageOptions
|
import com.skydoves.landscapist.ImageOptions
|
||||||
import com.skydoves.landscapist.glide.GlideImage
|
import com.skydoves.landscapist.glide.GlideImage
|
||||||
|
|
@ -66,15 +74,79 @@ import com.skydoves.landscapist.glide.GlideImageState
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class CharacterDetailUio(
|
data class CharacterDetailUio(
|
||||||
val name: String?,
|
val name: String,
|
||||||
val diminutive: String?,
|
val diminutive: String?,
|
||||||
@StringRes val gender: Int,
|
val gender: Lexicon.Gender,
|
||||||
@StringRes val race: Int,
|
val race: Lexicon.Race,
|
||||||
val portrait: List<Uri>,
|
val portrait: List<Uri>,
|
||||||
val description: String?,
|
val description: String?,
|
||||||
val history: String?,
|
val history: String?,
|
||||||
|
val search: String?,
|
||||||
|
val highlightGender: Boolean?,
|
||||||
|
val highlightRace: Boolean?,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class AnnotatedCharacterDetailUio(
|
||||||
|
val name: AnnotatedString,
|
||||||
|
val diminutive: AnnotatedString?,
|
||||||
|
val gender: AnnotatedString,
|
||||||
|
val race: AnnotatedString,
|
||||||
|
val portrait: List<Uri>,
|
||||||
|
val description: AnnotatedString?,
|
||||||
|
val history: AnnotatedString?,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
fun CharacterDetailUio.annotated(): AnnotatedCharacterDetailUio {
|
||||||
|
val colorScheme = MaterialTheme.colorScheme
|
||||||
|
val highlight = remember { SpanStyle(color = colorScheme.primary) }
|
||||||
|
val highlightRegex = remember(search) { search.highlightRegex }
|
||||||
|
val finderRegex = remember(search) { search.finderRegex }
|
||||||
|
val gender = stringResource(id = gender, short = true)
|
||||||
|
val race = stringResource(id = race)
|
||||||
|
|
||||||
|
return AnnotatedCharacterDetailUio(
|
||||||
|
name = AnnotatedString(
|
||||||
|
text = name,
|
||||||
|
spanStyles = highlightRegex?.annotatedSpan(
|
||||||
|
input = name,
|
||||||
|
spanStyle = highlight,
|
||||||
|
) ?: emptyList()
|
||||||
|
),
|
||||||
|
diminutive = highlightRegex?.annotatedString(
|
||||||
|
input = diminutive ?: "",
|
||||||
|
spanStyle = highlight
|
||||||
|
),
|
||||||
|
gender = gender.let {
|
||||||
|
AnnotatedString(
|
||||||
|
text = it,
|
||||||
|
spanStyles = when (highlightGender) {
|
||||||
|
true -> listOf(AnnotatedString.Range(highlight, 0, it.length))
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
race = race.let {
|
||||||
|
AnnotatedString(
|
||||||
|
text = it,
|
||||||
|
spanStyles = when (highlightRace) {
|
||||||
|
true -> listOf(AnnotatedString.Range(highlight, 0, it.length))
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
description = finderRegex?.foldAll(description)?.let { description ->
|
||||||
|
highlightRegex?.annotatedString(description, spanStyle = highlight)
|
||||||
|
},
|
||||||
|
history = finderRegex?.foldAll(history)?.let { history ->
|
||||||
|
highlightRegex?.annotatedString(history, spanStyle = highlight)
|
||||||
|
},
|
||||||
|
portrait = portrait,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CharacterDetailScreen(
|
fun CharacterDetailScreen(
|
||||||
viewModel: CharacterDetailViewModel = hiltViewModel(),
|
viewModel: CharacterDetailViewModel = hiltViewModel(),
|
||||||
|
|
@ -100,6 +172,7 @@ private fun CharacterDetailScreenContent(
|
||||||
) {
|
) {
|
||||||
val colorScheme = MaterialTheme.colorScheme
|
val colorScheme = MaterialTheme.colorScheme
|
||||||
val typography = MaterialTheme.typography
|
val typography = MaterialTheme.typography
|
||||||
|
val annotatedItem = item.value.annotated()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -123,7 +196,7 @@ private fun CharacterDetailScreenContent(
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.padding(paddingValues = paddingValues),
|
modifier = Modifier.padding(paddingValues = paddingValues),
|
||||||
) {
|
) {
|
||||||
item.value.portrait.firstOrNull()?.let { uri ->
|
annotatedItem.portrait.firstOrNull()?.let { uri ->
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
@ -169,14 +242,14 @@ private fun CharacterDetailScreenContent(
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
) {
|
) {
|
||||||
item.value.name?.let {
|
annotatedItem.name.let {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.alignByBaseline(),
|
modifier = Modifier.alignByBaseline(),
|
||||||
style = typography.headlineSmall,
|
style = typography.headlineSmall,
|
||||||
text = it,
|
text = it,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item.value.diminutive?.let {
|
annotatedItem.diminutive?.let {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.alignByBaseline(),
|
modifier = Modifier.alignByBaseline(),
|
||||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||||
|
|
@ -190,14 +263,14 @@ private fun CharacterDetailScreenContent(
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||||
text = stringResource(id = item.value.gender),
|
text = annotatedItem.gender,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||||
text = stringResource(id = item.value.race),
|
text = annotatedItem.race,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item.value.description?.let {
|
annotatedItem.description?.let {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
|
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
|
||||||
style = typography.titleMedium,
|
style = typography.titleMedium,
|
||||||
|
|
@ -216,7 +289,7 @@ private fun CharacterDetailScreenContent(
|
||||||
text = it,
|
text = it,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item.value.history?.let {
|
annotatedItem.history?.let {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
|
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
|
||||||
style = typography.titleMedium,
|
style = typography.titleMedium,
|
||||||
|
|
@ -228,7 +301,7 @@ private fun CharacterDetailScreenContent(
|
||||||
text = it,
|
text = it,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (item.value.portrait.isNotEmpty()) {
|
if (annotatedItem.portrait.isNotEmpty()) {
|
||||||
val configuration = LocalConfiguration.current
|
val configuration = LocalConfiguration.current
|
||||||
val maxSize = remember { (configuration.screenWidthDp.dp - 16.dp * 2) }
|
val maxSize = remember { (configuration.screenWidthDp.dp - 16.dp * 2) }
|
||||||
Text(
|
Text(
|
||||||
|
|
@ -240,7 +313,7 @@ private fun CharacterDetailScreenContent(
|
||||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
items(items = item.value.portrait) {
|
items(items = annotatedItem.portrait) {
|
||||||
val transition = rememberLoadingTransition { it }
|
val transition = rememberLoadingTransition { it }
|
||||||
GlideImage(
|
GlideImage(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -298,13 +371,16 @@ private fun CharacterDetailScreenContentPreview() {
|
||||||
CharacterDetailUio(
|
CharacterDetailUio(
|
||||||
name = "Brulkhai",
|
name = "Brulkhai",
|
||||||
diminutive = "./ Bru",
|
diminutive = "./ Bru",
|
||||||
gender = R.string.gender_female,
|
gender = Lexicon.Gender.FEMALE,
|
||||||
race = R.string.race_half_orc,
|
race = Lexicon.Race.HALF_ORC,
|
||||||
portrait = listOf(
|
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/889/large/bayard-wu-0716.jpg?1468642855"),
|
||||||
),
|
),
|
||||||
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).",
|
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,
|
history = null,
|
||||||
|
search = "Bru",
|
||||||
|
highlightGender = true,
|
||||||
|
highlightRace = true,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.pixelized.rplexicon.R
|
|
||||||
import com.pixelized.rplexicon.model.Lexicon
|
|
||||||
import com.pixelized.rplexicon.repository.LexiconRepository
|
import com.pixelized.rplexicon.repository.LexiconRepository
|
||||||
import com.pixelized.rplexicon.ui.navigation.screens.characterDetailArgument
|
import com.pixelized.rplexicon.ui.navigation.screens.characterDetailArgument
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
|
@ -13,40 +11,28 @@ import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class CharacterDetailViewModel @Inject constructor(
|
class CharacterDetailViewModel @Inject constructor(
|
||||||
private val savedStateHandle: SavedStateHandle,
|
savedStateHandle: SavedStateHandle,
|
||||||
private val repository: LexiconRepository,
|
repository: LexiconRepository,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val character: State<CharacterDetailUio> = mutableStateOf(init())
|
val character: State<CharacterDetailUio>
|
||||||
|
|
||||||
private fun init(): CharacterDetailUio {
|
init {
|
||||||
val source = repository.data.value[savedStateHandle.characterDetailArgument.id]
|
val argument = savedStateHandle.characterDetailArgument
|
||||||
return CharacterDetailUio(
|
val source = repository.data.value[argument.id]
|
||||||
name = source.name,
|
|
||||||
diminutive = source.diminutive?.let { "./ $it" },
|
character = mutableStateOf(
|
||||||
gender = when (source.gender) {
|
CharacterDetailUio(
|
||||||
Lexicon.Gender.MALE -> R.string.gender_male
|
name = source.name,
|
||||||
Lexicon.Gender.FEMALE -> R.string.gender_female
|
diminutive = source.diminutive?.let { "./ $it" },
|
||||||
Lexicon.Gender.UNDETERMINED -> R.string.gender_undetermined
|
gender = source.gender,
|
||||||
},
|
race = source.race,
|
||||||
race = when (source.race) {
|
portrait = source.portrait,
|
||||||
Lexicon.Race.ELF -> R.string.race_elf
|
description = source.description,
|
||||||
Lexicon.Race.HALFLING -> R.string.race_halfling
|
history = source.history,
|
||||||
Lexicon.Race.HUMAN -> R.string.race_human
|
search = argument.highlight,
|
||||||
Lexicon.Race.DWARF -> R.string.race_dwarf
|
highlightGender = argument.highlightGender && argument.gender == source.gender,
|
||||||
Lexicon.Race.HALF_ELF -> R.string.race_half_elf
|
highlightRace = argument.highlightRace && argument.race == source.race,
|
||||||
Lexicon.Race.HALF_ORC -> R.string.race_half_orc
|
)
|
||||||
Lexicon.Race.DRAGONBORN -> R.string.race_dragonborn
|
|
||||||
Lexicon.Race.GNOME -> R.string.race_gnome
|
|
||||||
Lexicon.Race.TIEFLING -> R.string.race_tiefling
|
|
||||||
Lexicon.Race.AARAKOCRA -> R.string.race_aarakocra
|
|
||||||
Lexicon.Race.GENASI -> R.string.race_genasi
|
|
||||||
Lexicon.Race.DEEP_GNOME -> R.string.race_deep_gnome
|
|
||||||
Lexicon.Race.GOLIATH -> R.string.race_goliath
|
|
||||||
Lexicon.Race.UNDETERMINED -> R.string.race_undetermined
|
|
||||||
},
|
|
||||||
portrait = source.portrait,
|
|
||||||
description = source.description,
|
|
||||||
history = source.history,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
|
@ -16,6 +17,7 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
|
@ -55,7 +57,10 @@ fun LexiconItem(
|
||||||
) {
|
) {
|
||||||
val typography = MaterialTheme.typography
|
val typography = MaterialTheme.typography
|
||||||
|
|
||||||
Surface(modifier = modifier) {
|
Box(
|
||||||
|
modifier = modifier,
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,13 @@ import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.with
|
import androidx.compose.animation.with
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.systemBarsPadding
|
import androidx.compose.foundation.layout.systemBarsPadding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
|
@ -34,6 +34,7 @@ import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -66,6 +67,7 @@ import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Default
|
||||||
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Permission
|
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Permission
|
||||||
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Structure
|
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Structure
|
||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
@ -131,8 +133,9 @@ fun LexiconScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(
|
@OptIn(
|
||||||
ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class,
|
ExperimentalMaterial3Api::class,
|
||||||
ExperimentalFoundationApi::class, ExperimentalAnimationApi::class
|
ExperimentalMaterialApi::class,
|
||||||
|
ExperimentalAnimationApi::class,
|
||||||
)
|
)
|
||||||
@Composable
|
@Composable
|
||||||
private fun LexiconScreenContent(
|
private fun LexiconScreenContent(
|
||||||
|
|
@ -208,7 +211,7 @@ private fun LexiconScreenContent(
|
||||||
) {
|
) {
|
||||||
items(count = 6) {
|
items(count = 6) {
|
||||||
LexiconItem(
|
LexiconItem(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.heightIn(min = MaterialTheme.lexicon.dimens.item),
|
||||||
item = LexiconItemUio.Brulkhai,
|
item = LexiconItemUio.Brulkhai,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -231,8 +234,8 @@ private fun LexiconScreenContent(
|
||||||
) {
|
) {
|
||||||
LexiconItem(
|
LexiconItem(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.animateItemPlacement()
|
.clickable { onItem(it) }
|
||||||
.clickable { onItem(it) },
|
.heightIn(min = MaterialTheme.lexicon.dimens.item),
|
||||||
item = it,
|
item = it,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,274 @@
|
||||||
|
package com.pixelized.rplexicon.ui.screens.search
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.drawBehind
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
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.model.Lexicon
|
||||||
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
|
import com.pixelized.rplexicon.utilitary.composable.stringResource
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.annotatedSpan
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.annotatedString
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.finderRegex
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.foldAll
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.highlightRegex
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class SearchItemUio(
|
||||||
|
val id: Int,
|
||||||
|
val name: String,
|
||||||
|
val diminutive: String?,
|
||||||
|
val gender: Lexicon.Gender,
|
||||||
|
val race: Lexicon.Race,
|
||||||
|
val description: String?,
|
||||||
|
val history: String?,
|
||||||
|
val search: String,
|
||||||
|
val highlightGender: Boolean,
|
||||||
|
val highlightRace: Boolean,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun preview(
|
||||||
|
id: Int = 0,
|
||||||
|
name: String = "Brulkhai",
|
||||||
|
diminutive: String? = "Bru",
|
||||||
|
gender: Lexicon.Gender = Lexicon.Gender.FEMALE,
|
||||||
|
race: Lexicon.Race = Lexicon.Race.HALF_ORC,
|
||||||
|
description: String? = "Brulkhai, ou plus simplement Bru, est une demi-orc de 38 ans 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.\n" +
|
||||||
|
"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.\n" +
|
||||||
|
"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: String? = null,
|
||||||
|
search: String = "",
|
||||||
|
highlightGender: Boolean = false,
|
||||||
|
highlightRace: Boolean = false,
|
||||||
|
): SearchItemUio {
|
||||||
|
return SearchItemUio(
|
||||||
|
id = id,
|
||||||
|
name = name,
|
||||||
|
diminutive = diminutive,
|
||||||
|
gender = gender,
|
||||||
|
race = race,
|
||||||
|
description = description,
|
||||||
|
history = history,
|
||||||
|
search = search,
|
||||||
|
highlightGender = highlightGender,
|
||||||
|
highlightRace = highlightRace,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
class AnnotatedSearchItemUio(
|
||||||
|
val id: Int,
|
||||||
|
val name: AnnotatedString,
|
||||||
|
val diminutive: AnnotatedString?,
|
||||||
|
val gender: AnnotatedString,
|
||||||
|
val race: AnnotatedString,
|
||||||
|
val description: AnnotatedString?,
|
||||||
|
val history: AnnotatedString?,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
private fun SearchItemUio.annotate(): AnnotatedSearchItemUio {
|
||||||
|
val colorScheme = MaterialTheme.colorScheme
|
||||||
|
val highlight = remember { SpanStyle(color = colorScheme.primary) }
|
||||||
|
val highlightRegex = remember(search) { search.highlightRegex }
|
||||||
|
val finderRegex = remember(search) { search.finderRegex }
|
||||||
|
val gender = stringResource(id = gender, short = true)
|
||||||
|
val race = stringResource(id = race)
|
||||||
|
|
||||||
|
return remember(search) {
|
||||||
|
AnnotatedSearchItemUio(
|
||||||
|
id = id,
|
||||||
|
name = AnnotatedString(
|
||||||
|
text = name,
|
||||||
|
spanStyles = highlightRegex?.annotatedSpan(
|
||||||
|
input = name,
|
||||||
|
spanStyle = highlight,
|
||||||
|
) ?: emptyList()
|
||||||
|
),
|
||||||
|
diminutive = highlightRegex?.annotatedString(
|
||||||
|
input = diminutive ?: "",
|
||||||
|
spanStyle = highlight
|
||||||
|
),
|
||||||
|
gender = gender.let {
|
||||||
|
AnnotatedString(
|
||||||
|
text = it,
|
||||||
|
spanStyles = when (highlightGender) {
|
||||||
|
true -> listOf(AnnotatedString.Range(highlight, 0, it.length))
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
race = race.let {
|
||||||
|
AnnotatedString(
|
||||||
|
text = it,
|
||||||
|
spanStyles = when (highlightRace) {
|
||||||
|
true -> listOf(AnnotatedString.Range(highlight, 0, it.length))
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
description = finderRegex?.foldAll(description)?.let { description ->
|
||||||
|
highlightRegex?.annotatedString(description, spanStyle = highlight)
|
||||||
|
},
|
||||||
|
history = finderRegex?.foldAll(history)?.let { history ->
|
||||||
|
highlightRegex?.annotatedString(history, spanStyle = highlight)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
|
@Composable
|
||||||
|
fun SearchItem(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
item: SearchItemUio,
|
||||||
|
) {
|
||||||
|
val typography = MaterialTheme.typography
|
||||||
|
val colorScheme = MaterialTheme.colorScheme
|
||||||
|
val annotatedItem = item.annotate()
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = modifier,
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 8.dp, horizontal = 16.dp)
|
||||||
|
.animateContentSize(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
|
) {
|
||||||
|
FlowRow(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = remember { typography.bodyLarge.copy(fontWeight = FontWeight.Bold) },
|
||||||
|
maxLines = 1,
|
||||||
|
text = annotatedItem.name,
|
||||||
|
)
|
||||||
|
annotatedItem.diminutive?.let {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = typography.labelMedium,
|
||||||
|
maxLines = 1,
|
||||||
|
text = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.offset(y = (-2).dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||||
|
maxLines = 1,
|
||||||
|
text = annotatedItem.gender
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||||
|
maxLines = 1,
|
||||||
|
text = annotatedItem.race
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.drawBehind {
|
||||||
|
drawLine(
|
||||||
|
color = colorScheme.primary,
|
||||||
|
start = Offset(0f, 0f),
|
||||||
|
end = Offset(0f, size.height),
|
||||||
|
strokeWidth = 2.dp.toPx()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
) {
|
||||||
|
annotatedItem.description?.let {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(start = 8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = remember { typography.labelSmall.copy(fontWeight = FontWeight.Bold) },
|
||||||
|
text = stringResource(id = R.string.search_item_description),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
style = typography.labelSmall,
|
||||||
|
text = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
annotatedItem.history?.let {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(start = 8.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = remember { typography.labelSmall.copy(fontWeight = FontWeight.Bold) },
|
||||||
|
text = stringResource(id = R.string.search_item_history),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
style = typography.labelSmall,
|
||||||
|
text = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview
|
||||||
|
private fun SearchItemPreview(
|
||||||
|
@PreviewParameter(SearchItemPreviewProvider::class) preview: SearchItemUio,
|
||||||
|
) {
|
||||||
|
LexiconTheme {
|
||||||
|
Surface {
|
||||||
|
SearchItem(
|
||||||
|
item = preview,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SearchItemPreviewProvider : PreviewParameterProvider<SearchItemUio> {
|
||||||
|
override val values: Sequence<SearchItemUio> = sequenceOf(
|
||||||
|
SearchItemUio.preview(),
|
||||||
|
SearchItemUio.preview(search = "bru"),
|
||||||
|
SearchItemUio.preview(search = "Brulkhai"),
|
||||||
|
SearchItemUio.preview(search = "elle"),
|
||||||
|
SearchItemUio.preview(highlightGender = true),
|
||||||
|
SearchItemUio.preview(highlightRace = true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.imePadding
|
import androidx.compose.foundation.layout.imePadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.systemBarsPadding
|
import androidx.compose.foundation.layout.systemBarsPadding
|
||||||
|
|
@ -21,6 +22,7 @@ import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -30,6 +32,7 @@ import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
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.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
@ -41,15 +44,15 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.pixelized.rplexicon.NO_WINDOW_INSETS
|
import com.pixelized.rplexicon.NO_WINDOW_INSETS
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
import com.pixelized.rplexicon.model.Lexicon
|
import com.pixelized.rplexicon.model.Lexicon
|
||||||
|
import com.pixelized.rplexicon.ui.composable.ScrollingKeyboardHandler
|
||||||
import com.pixelized.rplexicon.ui.composable.form.DropDownField
|
import com.pixelized.rplexicon.ui.composable.form.DropDownField
|
||||||
import com.pixelized.rplexicon.ui.composable.form.DropDownFieldUio
|
import com.pixelized.rplexicon.ui.composable.form.DropDownFieldUio
|
||||||
import com.pixelized.rplexicon.ui.composable.form.TextField
|
import com.pixelized.rplexicon.ui.composable.form.TextField
|
||||||
import com.pixelized.rplexicon.ui.composable.form.TextFieldUio
|
import com.pixelized.rplexicon.ui.composable.form.TextFieldUio
|
||||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToCharacterDetail
|
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
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class SearchFormUio(
|
data class SearchFormUio(
|
||||||
|
|
@ -63,6 +66,7 @@ fun SearchScreen(
|
||||||
viewModel: SearchViewModel = hiltViewModel(),
|
viewModel: SearchViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val screen = LocalScreenNavHost.current
|
val screen = LocalScreenNavHost.current
|
||||||
|
val lazyState = rememberLazyListState()
|
||||||
|
|
||||||
Surface {
|
Surface {
|
||||||
SearchScreenContent(
|
SearchScreenContent(
|
||||||
|
|
@ -70,15 +74,25 @@ fun SearchScreen(
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.systemBarsPadding()
|
.systemBarsPadding()
|
||||||
.imePadding(),
|
.imePadding(),
|
||||||
|
lazyColumnState = lazyState,
|
||||||
items = viewModel.filter,
|
items = viewModel.filter,
|
||||||
form = viewModel.form,
|
form = viewModel.form,
|
||||||
onItem = {
|
onItem = { item ->
|
||||||
screen.navigateToCharacterDetail(id = it.id)
|
val form = viewModel.form
|
||||||
|
screen.navigateToCharacterDetail(
|
||||||
|
id = item.id,
|
||||||
|
highlight = form.search.value.value.takeIf { it.isNotEmpty() },
|
||||||
|
race = form.race.value.value.first,
|
||||||
|
gender = form.gender.value.value.first,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
onBack = {
|
onBack = {
|
||||||
screen.popBackStack()
|
screen.popBackStack()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
ScrollingKeyboardHandler(
|
||||||
|
lazyListState = lazyState,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,10 +101,10 @@ fun SearchScreen(
|
||||||
private fun SearchScreenContent(
|
private fun SearchScreenContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
lazyColumnState: LazyListState = rememberLazyListState(),
|
lazyColumnState: LazyListState = rememberLazyListState(),
|
||||||
items: State<List<LexiconItemUio>>,
|
items: State<List<SearchItemUio>>,
|
||||||
form: SearchFormUio,
|
form: SearchFormUio,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onItem: (LexiconItemUio) -> Unit,
|
onItem: (SearchItemUio) -> Unit,
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -147,12 +161,12 @@ private fun SearchScreenContent(
|
||||||
items(
|
items(
|
||||||
items = items.value,
|
items = items.value,
|
||||||
key = { it.id },
|
key = { it.id },
|
||||||
contentType = { "Lexicon" },
|
contentType = { "Search" },
|
||||||
) {
|
) {
|
||||||
LexiconItem(
|
SearchItem(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.animateItemPlacement()
|
.clickable { onItem(it) }
|
||||||
.clickable { onItem(it) },
|
.heightIn(min = MaterialTheme.lexicon.dimens.item),
|
||||||
item = it,
|
item = it,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -185,12 +199,17 @@ private fun SearchScreenContentPreview() {
|
||||||
items = remember {
|
items = remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
listOf(
|
listOf(
|
||||||
LexiconItemUio(
|
SearchItemUio(
|
||||||
id = 0,
|
id = 0,
|
||||||
name = "Brulkhai",
|
name = "Brulkhai",
|
||||||
diminutive = "Bru",
|
diminutive = "Bru",
|
||||||
gender = R.string.gender_female_short,
|
gender = Lexicon.Gender.FEMALE,
|
||||||
race = R.string.race_half_orc,
|
race = Lexicon.Race.HALF_ORC,
|
||||||
|
description = null,
|
||||||
|
history = null,
|
||||||
|
search = "",
|
||||||
|
highlightGender = false,
|
||||||
|
highlightRace = false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -10,86 +10,77 @@ import com.pixelized.rplexicon.model.Lexicon.Race
|
||||||
import com.pixelized.rplexicon.repository.LexiconRepository
|
import com.pixelized.rplexicon.repository.LexiconRepository
|
||||||
import com.pixelized.rplexicon.ui.composable.form.DropDownFieldUio
|
import com.pixelized.rplexicon.ui.composable.form.DropDownFieldUio
|
||||||
import com.pixelized.rplexicon.ui.composable.form.TextFieldUio
|
import com.pixelized.rplexicon.ui.composable.form.TextFieldUio
|
||||||
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconItemUio
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SearchViewModel @Inject constructor(
|
class SearchViewModel @Inject constructor(
|
||||||
private val repository: LexiconRepository,
|
repository: LexiconRepository,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val search = mutableStateOf("")
|
private val _search = mutableStateOf("")
|
||||||
private val gender = mutableStateOf<Pair<Gender?, String>>(null to "")
|
private val _gender = mutableStateOf<Pair<Gender?, String>>(null to "")
|
||||||
private val race = mutableStateOf<Pair<Race?, String>>(null to "")
|
private val _race = mutableStateOf<Pair<Race?, String>>(null to "")
|
||||||
|
|
||||||
val form = SearchFormUio(
|
val form = SearchFormUio(
|
||||||
search = TextFieldUio(
|
search = TextFieldUio(
|
||||||
label = R.string.search_field_search,
|
label = R.string.search_field_search,
|
||||||
value = search,
|
value = _search,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
search.value = it
|
_search.value = it
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
gender = DropDownFieldUio(
|
gender = DropDownFieldUio(
|
||||||
label = R.string.search_field_gender,
|
label = R.string.search_field_gender,
|
||||||
values = genders(),
|
values = genders(),
|
||||||
value = gender,
|
value = _gender,
|
||||||
onValueChange = { id, value -> gender.value = id to value },
|
onValueChange = { id, value -> _gender.value = id to value },
|
||||||
),
|
),
|
||||||
race = DropDownFieldUio(
|
race = DropDownFieldUio(
|
||||||
label = R.string.search_field_race,
|
label = R.string.search_field_race,
|
||||||
values = races(),
|
values = races(),
|
||||||
value = race,
|
value = _race,
|
||||||
onValueChange = { id, value -> race.value = id to value },
|
onValueChange = { id, value -> _race.value = id to value },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
private var data: List<Lexicon> = repository.data.value
|
private var data: List<Lexicon> = repository.data.value
|
||||||
|
|
||||||
val filter = derivedStateOf {
|
val filter = derivedStateOf {
|
||||||
data
|
data.filter { item ->
|
||||||
.filter { item ->
|
val gender = _gender.value.first?.let { it == item.gender }
|
||||||
val gender = gender.value.first?.let { it == item.gender }
|
val race = _race.value.first?.let { it == item.race }
|
||||||
val race = race.value.first?.let { it == item.race }
|
val search = _search.value.takeIf { it.isNotEmpty() }?.let {
|
||||||
val search = search.value.takeIf { it.isNotEmpty() }?.let {
|
val name = item.name.contains(_search.value, true)
|
||||||
val name = item.name.contains(search.value, true)
|
val diminutive = item.diminutive?.contains(_search.value, true) == true
|
||||||
val diminutive = item.diminutive?.contains(search.value, true) == true
|
val description = item.description?.contains(_search.value, true) == true
|
||||||
val description = item.description?.contains(search.value, true) == true
|
val history = item.history?.contains(_search.value, true) == true
|
||||||
val history = item.history?.contains(search.value, true) == true
|
name || diminutive || description || history
|
||||||
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 }
|
(gender == null || gender) && (race == null || race) && (search == null || search)
|
||||||
|
}.map {
|
||||||
|
it.toSearchUio()
|
||||||
|
}.sortedBy {
|
||||||
|
it.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Lexicon.toSearchUio(
|
||||||
|
search: String = _search.value,
|
||||||
|
highlightGender: Boolean = this.gender == _gender.value.first,
|
||||||
|
highlightRace: Boolean = this.race == _race.value.first,
|
||||||
|
) = SearchItemUio(
|
||||||
|
id = this.id,
|
||||||
|
name = this.name,
|
||||||
|
diminutive = diminutive?.takeIf { it.isNotBlank() }?.let { "./ $it" },
|
||||||
|
gender = this.gender,
|
||||||
|
race = this.race,
|
||||||
|
description = this.description,
|
||||||
|
history = this.history,
|
||||||
|
search = search,
|
||||||
|
highlightGender = highlightGender,
|
||||||
|
highlightRace = highlightRace,
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun genders() = listOf(
|
private fun genders() = listOf(
|
||||||
Gender.MALE to R.string.gender_male,
|
Gender.MALE to R.string.gender_male,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import androidx.core.view.WindowCompat
|
||||||
import com.pixelized.rplexicon.ui.theme.colors.LexiconColors
|
import com.pixelized.rplexicon.ui.theme.colors.LexiconColors
|
||||||
import com.pixelized.rplexicon.ui.theme.colors.darkColorScheme
|
import com.pixelized.rplexicon.ui.theme.colors.darkColorScheme
|
||||||
import com.pixelized.rplexicon.ui.theme.colors.lightColorScheme
|
import com.pixelized.rplexicon.ui.theme.colors.lightColorScheme
|
||||||
|
import com.pixelized.rplexicon.ui.theme.dimen.LexiconDimens
|
||||||
|
import com.pixelized.rplexicon.ui.theme.dimen.lexiconDimen
|
||||||
import com.pixelized.rplexicon.ui.theme.shape.LexiconShapes
|
import com.pixelized.rplexicon.ui.theme.shape.LexiconShapes
|
||||||
import com.pixelized.rplexicon.ui.theme.shape.lexiconShapes
|
import com.pixelized.rplexicon.ui.theme.shape.lexiconShapes
|
||||||
|
|
||||||
|
|
@ -26,6 +28,7 @@ val LocalLexiconTheme = compositionLocalOf<LexiconTheme> {
|
||||||
data class LexiconTheme(
|
data class LexiconTheme(
|
||||||
val colorScheme: LexiconColors,
|
val colorScheme: LexiconColors,
|
||||||
val shapes: LexiconShapes,
|
val shapes: LexiconShapes,
|
||||||
|
val dimens: LexiconDimens,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -39,7 +42,8 @@ fun LexiconTheme(
|
||||||
true -> darkColorScheme()
|
true -> darkColorScheme()
|
||||||
else -> lightColorScheme()
|
else -> lightColorScheme()
|
||||||
},
|
},
|
||||||
shapes = lexiconShapes()
|
shapes = lexiconShapes(),
|
||||||
|
dimens = lexiconDimen(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.pixelized.rplexicon.ui.theme.dimen
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
@Immutable
|
||||||
|
data class LexiconDimens(
|
||||||
|
val item: Dp
|
||||||
|
)
|
||||||
|
|
||||||
|
fun lexiconDimen(
|
||||||
|
item: Dp = 48.dp
|
||||||
|
) = LexiconDimens(
|
||||||
|
item = item,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.pixelized.rplexicon.utilitary.composable
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
|
import com.pixelized.rplexicon.R
|
||||||
|
import com.pixelized.rplexicon.model.Lexicon
|
||||||
|
import androidx.compose.ui.res.stringResource as androidStringResource
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@ReadOnlyComposable
|
||||||
|
fun stringResource(id: Lexicon.Gender, short: Boolean = false): String {
|
||||||
|
return androidStringResource(
|
||||||
|
id = when (short) {
|
||||||
|
true -> when (id) {
|
||||||
|
Lexicon.Gender.MALE -> R.string.gender_male_short
|
||||||
|
Lexicon.Gender.FEMALE -> R.string.gender_female_short
|
||||||
|
Lexicon.Gender.UNDETERMINED -> R.string.gender_undetermined_short
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> when (id) {
|
||||||
|
Lexicon.Gender.MALE -> R.string.gender_male
|
||||||
|
Lexicon.Gender.FEMALE -> R.string.gender_female
|
||||||
|
Lexicon.Gender.UNDETERMINED -> R.string.gender_undetermined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.pixelized.rplexicon.utilitary.composable
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
|
import com.pixelized.rplexicon.R
|
||||||
|
import com.pixelized.rplexicon.model.Lexicon
|
||||||
|
import androidx.compose.ui.res.stringResource as androidStringResource
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@ReadOnlyComposable
|
||||||
|
fun stringResource(id: Lexicon.Race): String {
|
||||||
|
return androidStringResource(
|
||||||
|
id = when (id) {
|
||||||
|
Lexicon.Race.ELF -> R.string.race_elf
|
||||||
|
Lexicon.Race.HALFLING -> R.string.race_halfling
|
||||||
|
Lexicon.Race.HUMAN -> R.string.race_human
|
||||||
|
Lexicon.Race.DWARF -> R.string.race_dwarf
|
||||||
|
Lexicon.Race.HALF_ELF -> R.string.race_half_elf
|
||||||
|
Lexicon.Race.HALF_ORC -> R.string.race_half_orc
|
||||||
|
Lexicon.Race.DRAGONBORN -> R.string.race_dragonborn
|
||||||
|
Lexicon.Race.GNOME -> R.string.race_gnome
|
||||||
|
Lexicon.Race.TIEFLING -> R.string.race_tiefling
|
||||||
|
Lexicon.Race.AARAKOCRA -> R.string.race_aarakocra
|
||||||
|
Lexicon.Race.GENASI -> R.string.race_genasi
|
||||||
|
Lexicon.Race.DEEP_GNOME -> R.string.race_deep_gnome
|
||||||
|
Lexicon.Race.GOLIATH -> R.string.race_goliath
|
||||||
|
Lexicon.Race.UNDETERMINED -> R.string.race_undetermined
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.pixelized.rplexicon.utilitary.extentions
|
||||||
|
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
|
||||||
|
fun Regex?.foldAll(
|
||||||
|
input: CharSequence?,
|
||||||
|
startIndex: Int = 0,
|
||||||
|
): String? = input?.let {
|
||||||
|
var previous: MatchResult? = null
|
||||||
|
this?.findAll(it, startIndex)?.fold("") { acc, item ->
|
||||||
|
val dummy = acc + when {
|
||||||
|
previous == null && item.range.first == 0 -> item.value
|
||||||
|
previous == null && item.range.first != 0 -> "...${item.value}"
|
||||||
|
item.range.first <= (previous?.range?.last ?: 0) + 1 -> item.value
|
||||||
|
else -> " ... ${item.value}"
|
||||||
|
}
|
||||||
|
previous = item
|
||||||
|
dummy
|
||||||
|
}
|
||||||
|
}?.takeIf { it.isNotEmpty() }?.let { "$it..." }
|
||||||
|
|
||||||
|
fun Regex.annotatedSpan(
|
||||||
|
input: String,
|
||||||
|
startIndex: Int = 0,
|
||||||
|
spanStyle: SpanStyle
|
||||||
|
): List<AnnotatedString.Range<SpanStyle>> {
|
||||||
|
return findAll(input = input, startIndex = startIndex).map {
|
||||||
|
AnnotatedString.Range(
|
||||||
|
item = spanStyle,
|
||||||
|
start = it.range.first,
|
||||||
|
end = it.range.last + 1
|
||||||
|
)
|
||||||
|
}.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Regex.annotatedString(
|
||||||
|
input: String,
|
||||||
|
startIndex: Int = 0,
|
||||||
|
spanStyle: SpanStyle
|
||||||
|
): AnnotatedString {
|
||||||
|
return AnnotatedString(
|
||||||
|
text = input,
|
||||||
|
spanStyles = annotatedSpan(input, startIndex, spanStyle)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
package com.pixelized.rplexicon.utilitary.extentions
|
|
||||||
|
|
||||||
val String.ARG: String get() = "$this={$this}"
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.pixelized.rplexicon.utilitary.extentions
|
||||||
|
|
||||||
|
val String.ARG: String get() = "$this={$this}"
|
||||||
|
|
||||||
|
val String?.highlightRegex: Regex?
|
||||||
|
get() = this?.takeIf { it.isNotEmpty() }?.let {
|
||||||
|
Regex(pattern = Regex.escape(it), option = RegexOption.IGNORE_CASE)
|
||||||
|
}
|
||||||
|
|
||||||
|
val String?.finderRegex: Regex?
|
||||||
|
get() = this?.takeIf { it.isNotEmpty() }?.let {
|
||||||
|
Regex(
|
||||||
|
pattern = "\\w*.{0,30}${Regex.escape(it)}\\w*.{0,30}\\w*",
|
||||||
|
option = RegexOption.IGNORE_CASE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
18
app/src/main/res/drawable/ic_dungeons_and_dragons_48dp.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
<path
|
||||||
|
android:fillColor="#e53935"
|
||||||
|
android:pathData="M35.25,31.001C36.283,31.724 37,33 37,34c0,1.25 -0.25,2.125 -1.125,3c0,0 0.125,0.125 0.75,0.125c3.5,0 4.375,-1.944 4.375,-2.625c0,-2 -2,-2.5 -2,-2.5c0.625,-0.015 1.218,0.034 1.806,0.185c1.683,0.431 2.943,1.374 3.729,2.924c0.306,0.604 0.456,1.206 0.464,1.794c0.016,1.159 -0.5,2.346 -1.625,3.346C41.525,41.894 38.125,42 38,42c0.329,0.363 1.25,1.125 2.625,1.125C41.25,43.125 42,43 42,43c-0.036,0.047 -0.75,0.875 -2.64,1.576C38.584,44.835 37.798,44.988 37,45c-0.47,0.007 -0.943,-0.035 -1.42,-0.132c-1.906,-0.386 -3.215,-1.502 -4.036,-3.206C31.27,41.094 31,39.875 31,39.625c-0.618,0.604 -0.977,1.337 -1,2.125c0.125,1.625 1.013,2.232 1,2.25c-0.144,-0.059 -3,-0.625 -3,-4.625c-0.013,-0.642 0.003,-1.161 0,-1.75c-0.005,-1.031 -0.475,-1.89 -1.282,-2.555c-1.359,-1.12 -2.723,-2.236 -4.093,-3.343c-0.935,-0.756 -1.518,-1.686 -1.531,-2.901c-0.008,-0.758 -0.367,-1.313 -0.959,-1.76c-0.372,-0.281 -0.749,-0.561 -1.086,-0.88c-0.595,-0.564 -0.706,-1.262 -0.503,-2.047c0.035,0.029 0.064,0.041 0.068,0.059c0.233,0.944 0.953,1.287 1.831,1.429c0.471,0.076 0.951,0.109 1.416,0.208c0.488,0.105 0.765,0.449 0.877,0.92c0.069,0.291 0.124,0.586 0.192,0.877c0.131,0.561 0.416,0.795 0.997,0.832c0.382,0.024 0.765,0.043 1.146,0.081c0.597,0.06 1.115,0.299 1.578,0.676c1.614,1.314 3.236,2.619 4.851,3.931C31.917,33.488 32.625,34 33.375,34c0.875,0 2,-0.625 2,-1.875C35.375,31.375 35.25,31.001 35.25,31.001z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#e53935"
|
||||||
|
android:pathData="M25.891,36.073c-1.139,-0.939 -2.284,-1.872 -3.431,-2.802 -0.641,1.039 -3.108,4.728 -6.589,4.728 -1.726,0 -3.306,-1.013 -4.121,-2.592 -0.49,-0.95 -0.75,-2.052 -0.75,-3.031 0,-4.25 2.675,-6.575 2.739,-6.658 -0.726,-0.179 -1.273,-0.103 -1.864,0.29 -0.598,0.398 -1.679,1.077 -1.758,1.119C10,27.125 10,27.125 10,27l0.375,-2.5C9.799,24.694 8.5,25.375 7,26.875c-0.75,0.75 -1.357,1.416 -1.357,1.416C5.25,26.875 5.875,24.375 5.997,23.5 6.069,22.99 5.872,23 5.872,23c-0.25,0 -0.997,0.765 -1.372,1.156 -1.159,1.206 -1.895,2.584 -2.31,4.208C2.112,28.672 2.063,29.063 2,29.375 2.024,29.383 2.625,28.5 3.125,28.5c0.25,0 0.265,0.383 0.257,0.518 -0.013,0.209 -0.084,0.418 -0.15,0.621C2.768,31.074 2.503,32.525 2.492,34c-0.005,0.676 0.044,1.356 0.151,2.042 0.183,1.167 0.467,2.021 0.981,3.083 0.726,1.5 2.033,2.812 2.125,2.875 -0.063,-0.214 -0.188,-0.505 -0.209,-0.67 -0.042,-0.319 -0.107,-0.647 -0.072,-0.962 0.044,-0.396 0.39,-0.486 0.678,-0.206 0.129,0.126 1.545,1.465 2.38,2.011 1.893,1.236 3.89,1.82 5.974,1.827 1.083,0.004 2.189,-0.148 3.317,-0.444 1.632,-0.429 3.072,-1.223 4.356,-2.295 1.645,-1.372 2.912,-3.132 3.921,-4.989C26.028,36.205 25.969,36.137 25.891,36.073zM31.32,22.083c-1.992,1.314 -3.654,3.497 -5.105,5.364 -0.01,0.013 -0.016,0.027 -0.023,0.04 0.451,0.164 0.881,0.4 1.28,0.725 0.533,0.434 1.067,0.865 1.601,1.298 0.043,-0.087 0.083,-0.166 0.118,-0.239 0.989,-2.037 2.183,-4.476 3.696,-6.181 1.478,-1.666 4.073,-2.181 5.999,-1.251 0.24,0.116 0.434,0.167 0.624,0.16 0.194,-0.007 0.384,-0.073 0.615,-0.193 1.506,-0.778 3.133,-1.17 4.829,-1.159C45.288,20.65 46,20.75 46,20.75c-0.625,-0.625 -1.444,-1.26 -2.156,-1.661C42.519,18.344 41.157,18 39.75,18c-2,0 -3,0.624 -3.535,0.624 -0.215,0 -0.32,-0.058 -0.394,-0.156 -0.065,-0.085 -0.04,-0.276 0.01,-0.395C36.125,17.5 36.934,17.045 37,17c-0.625,0 -2.003,0.741 -2.796,1.355 -0.533,0.412 -1.062,0.829 -1.599,1.236 -0.108,0.082 -0.238,0.147 -0.368,0.185 -0.353,0.103 -0.533,-0.071 -0.429,-0.414 0.047,-0.154 0.119,-0.304 0.199,-0.444 0.129,-0.227 0.275,-0.445 0.425,-0.683 -3.807,2.391 -4.459,5.671 -4.432,5.641 1.118,-1.216 2.382,-2.165 3.875,-2.875 1.495,-0.711 3.125,-1.125 5,-1.125 0,0 0.125,0.101 0.125,0.125 0,0 -0.75,0 -1.545,0.237C33.954,20.583 32.592,21.243 31.32,22.083z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#e53935"
|
||||||
|
android:pathData="M16.75,11.25c0,0.5 0.132,0.997 0.275,1.413c0.372,1.086 1.086,1.964 1.899,2.76c0.802,0.785 1.649,1.525 2.467,2.294c0.825,0.775 1.542,1.63 1.968,2.686c0.241,0.597 0.36,1.216 0.266,1.858c-0.026,0.179 0.04,0.265 0.199,0.333c0.406,0.173 0.664,0.46 0.686,0.915c0.01,0.215 0.144,0.181 0.273,0.114c0.716,-0.372 1.045,-0.935 1.091,-0.997c0.082,0.171 0.127,0.383 0.125,0.625c-0.012,1.346 -1.205,3.555 -2.328,3.995c0.015,-0.098 0.026,-0.177 0.04,-0.256c0.158,-0.888 -0.284,-1.616 -1.153,-1.906c-0.587,-0.196 -1.072,-0.2 -1.685,-0.209c-0.094,-0.001 -0.357,0 -0.5,0c0.047,-0.108 0.393,-0.823 0.47,-1.101c0.11,-0.395 -0.034,-0.677 -0.439,-0.766c-0.439,-0.097 -0.896,-0.158 -1.345,-0.156c-1.032,0.005 -2.055,0.129 -3.059,0.372c-0.14,0.034 -0.366,0.106 -0.503,0.151c0.875,0 2,0.375 2,0.375s-0.5,1 -0.5,1.625c0,0.5 0.25,0.875 0.5,0.875S17.886,26.082 18,26c-0.023,0.136 -0.161,0.855 -0.18,1.193C17.791,27.724 18,28 18.625,28c0.125,0 0.25,0 0.375,0c-0.25,0.875 -2.132,2.027 -3.25,2c-0.074,-0.002 -0.143,-0.009 -0.208,-0.022C16,29.75 16.69,28.875 16.69,28.501l-1.492,0.19c0,0 0.509,-0.635 0.672,-0.954c0.423,-0.825 0.47,-1.668 -0.09,-2.456c-0.473,-0.666 -1.239,-0.951 -2.03,-1.03c-1.25,0 -1.9,0.35 -2,0.375c0.569,-1.24 1.381,-2.005 2.625,-2.625c-1,-0.5 -2,-0.625 -2,-0.625c1,-0.625 2.5,-1.125 3.625,-1.375c-0.125,0 -0.437,0.004 -0.5,0c-3.75,0 -5.219,1.938 -5.25,2c0.605,0.104 1,0.125 1.75,0.375c0,0 -2,0.375 -5,2.625c0.02,-0.113 0.375,-0.875 0.375,-1.625c0,-2.625 -3.324,-2.099 -3.375,-2.125c0.081,-0.073 0.75,-0.625 2.863,-1.645c0.396,-0.214 1.512,-0.73 1.512,-1.355c0,-0.875 -1.735,-2.15 -1.875,-2.25c1.375,0 2.147,0.129 3.25,0.375c-0.588,-1.004 -0.752,-2.05 -0.486,-3.088c0.426,0.8 1.038,1.428 1.784,1.92c0.686,0.453 1.409,0.852 2.111,1.282c0.532,0.326 1.084,0.628 1.576,1.005c0.378,0.29 0.677,0.681 1.008,1.028c0.055,0.058 0.099,0.126 0.149,0.189c0.029,-0.014 0.057,-0.028 0.086,-0.042c-0.079,-0.197 -0.142,-0.402 -0.239,-0.591c-0.393,-0.765 -0.974,-1.378 -1.693,-1.852c-0.295,-0.195 -0.456,-0.426 -0.513,-0.753c-0.171,-0.969 -0.175,-1.919 0.011,-2.85C13.645,12.121 14.25,11 14.25,11v0.125c0.034,1.325 0.538,2.386 1.375,3.375c0.878,1.037 2.027,1.805 3.017,2.73c1.733,1.645 2.471,2.568 2.608,2.77c0,0 -0.051,-0.214 -0.082,-0.283c-0.476,-1.091 -1.247,-1.97 -2.11,-2.778c-0.75,-0.702 -1.512,-1.393 -2.275,-2.082c-0.276,-0.25 -0.439,-0.547 -0.483,-0.91C16.125,12.5 16.737,11.259 16.75,11.25zM19,20c0,0 0.08,0.497 0.199,0.743C19.75,21.875 21,22 21,22s0,-0.875 -1.25,-1.75C19.381,19.992 19,20 19,20z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#e53935"
|
||||||
|
android:pathData="M8,12.625C8.25,11.375 9,10 9,10s0.463,1.573 1.25,2.625c0.389,0.52 0.909,1.098 1.514,1.497c0.114,0.075 0.229,0.149 0.346,0.222c-0.022,-0.674 0.031,-1.334 0.159,-1.974c0.109,-0.545 0.492,-1.328 0.73,-1.784V10.5C13,8.567 14.343,7 16,7s3,1.567 3,3.5c0,1.377 -0.111,2.059 -0.271,2.5c-0.014,0.037 -0.028,0.071 -0.042,0.106c0.131,0.212 0.287,0.426 0.465,0.645c0.011,0.013 0.02,0.026 0.031,0.039c0.187,0.226 0.4,0.459 0.65,0.704c0.471,0.461 0.976,0.922 1.463,1.367c0.33,0.301 0.66,0.602 0.985,0.908c0.079,0.074 0.142,0.147 0.217,0.221C24.042,15.314 25,13.142 25,11c0,-5 -4.029,-8 -9,-8s-9,3.125 -9,8c0,1.216 0.318,2.439 0.865,3.58C7.829,14.041 7.892,13.167 8,12.625z" />
|
||||||
|
</vector>
|
||||||
|
|
@ -1,30 +1,23 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
android:width="108dp"
|
||||||
android:height="108dp"
|
android:height="108dp"
|
||||||
android:viewportWidth="108"
|
android:viewportWidth="48"
|
||||||
android:viewportHeight="108">
|
android:viewportHeight="48">
|
||||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
<group android:scaleX="0.54"
|
||||||
<aapt:attr name="android:fillColor">
|
android:scaleY="0.54"
|
||||||
<gradient
|
android:translateX="11.04"
|
||||||
android:endX="85.84757"
|
android:translateY="11.04">
|
||||||
android:endY="92.4963"
|
<path
|
||||||
android:startX="42.9492"
|
android:fillColor="#e53935"
|
||||||
android:startY="49.59793"
|
android:pathData="M35.25,31.001C36.283,31.724 37,33 37,34c0,1.25 -0.25,2.125 -1.125,3c0,0 0.125,0.125 0.75,0.125c3.5,0 4.375,-1.944 4.375,-2.625c0,-2 -2,-2.5 -2,-2.5c0.625,-0.015 1.218,0.034 1.806,0.185c1.683,0.431 2.943,1.374 3.729,2.924c0.306,0.604 0.456,1.206 0.464,1.794c0.016,1.159 -0.5,2.346 -1.625,3.346C41.525,41.894 38.125,42 38,42c0.329,0.363 1.25,1.125 2.625,1.125C41.25,43.125 42,43 42,43c-0.036,0.047 -0.75,0.875 -2.64,1.576C38.584,44.835 37.798,44.988 37,45c-0.47,0.007 -0.943,-0.035 -1.42,-0.132c-1.906,-0.386 -3.215,-1.502 -4.036,-3.206C31.27,41.094 31,39.875 31,39.625c-0.618,0.604 -0.977,1.337 -1,2.125c0.125,1.625 1.013,2.232 1,2.25c-0.144,-0.059 -3,-0.625 -3,-4.625c-0.013,-0.642 0.003,-1.161 0,-1.75c-0.005,-1.031 -0.475,-1.89 -1.282,-2.555c-1.359,-1.12 -2.723,-2.236 -4.093,-3.343c-0.935,-0.756 -1.518,-1.686 -1.531,-2.901c-0.008,-0.758 -0.367,-1.313 -0.959,-1.76c-0.372,-0.281 -0.749,-0.561 -1.086,-0.88c-0.595,-0.564 -0.706,-1.262 -0.503,-2.047c0.035,0.029 0.064,0.041 0.068,0.059c0.233,0.944 0.953,1.287 1.831,1.429c0.471,0.076 0.951,0.109 1.416,0.208c0.488,0.105 0.765,0.449 0.877,0.92c0.069,0.291 0.124,0.586 0.192,0.877c0.131,0.561 0.416,0.795 0.997,0.832c0.382,0.024 0.765,0.043 1.146,0.081c0.597,0.06 1.115,0.299 1.578,0.676c1.614,1.314 3.236,2.619 4.851,3.931C31.917,33.488 32.625,34 33.375,34c0.875,0 2,-0.625 2,-1.875C35.375,31.375 35.25,31.001 35.25,31.001z" />
|
||||||
android:type="linear">
|
<path
|
||||||
<item
|
android:fillColor="#e53935"
|
||||||
android:color="#44000000"
|
android:pathData="M25.891,36.073c-1.139,-0.939 -2.284,-1.872 -3.431,-2.802 -0.641,1.039 -3.108,4.728 -6.589,4.728 -1.726,0 -3.306,-1.013 -4.121,-2.592 -0.49,-0.95 -0.75,-2.052 -0.75,-3.031 0,-4.25 2.675,-6.575 2.739,-6.658 -0.726,-0.179 -1.273,-0.103 -1.864,0.29 -0.598,0.398 -1.679,1.077 -1.758,1.119C10,27.125 10,27.125 10,27l0.375,-2.5C9.799,24.694 8.5,25.375 7,26.875c-0.75,0.75 -1.357,1.416 -1.357,1.416C5.25,26.875 5.875,24.375 5.997,23.5 6.069,22.99 5.872,23 5.872,23c-0.25,0 -0.997,0.765 -1.372,1.156 -1.159,1.206 -1.895,2.584 -2.31,4.208C2.112,28.672 2.063,29.063 2,29.375 2.024,29.383 2.625,28.5 3.125,28.5c0.25,0 0.265,0.383 0.257,0.518 -0.013,0.209 -0.084,0.418 -0.15,0.621C2.768,31.074 2.503,32.525 2.492,34c-0.005,0.676 0.044,1.356 0.151,2.042 0.183,1.167 0.467,2.021 0.981,3.083 0.726,1.5 2.033,2.812 2.125,2.875 -0.063,-0.214 -0.188,-0.505 -0.209,-0.67 -0.042,-0.319 -0.107,-0.647 -0.072,-0.962 0.044,-0.396 0.39,-0.486 0.678,-0.206 0.129,0.126 1.545,1.465 2.38,2.011 1.893,1.236 3.89,1.82 5.974,1.827 1.083,0.004 2.189,-0.148 3.317,-0.444 1.632,-0.429 3.072,-1.223 4.356,-2.295 1.645,-1.372 2.912,-3.132 3.921,-4.989C26.028,36.205 25.969,36.137 25.891,36.073zM31.32,22.083c-1.992,1.314 -3.654,3.497 -5.105,5.364 -0.01,0.013 -0.016,0.027 -0.023,0.04 0.451,0.164 0.881,0.4 1.28,0.725 0.533,0.434 1.067,0.865 1.601,1.298 0.043,-0.087 0.083,-0.166 0.118,-0.239 0.989,-2.037 2.183,-4.476 3.696,-6.181 1.478,-1.666 4.073,-2.181 5.999,-1.251 0.24,0.116 0.434,0.167 0.624,0.16 0.194,-0.007 0.384,-0.073 0.615,-0.193 1.506,-0.778 3.133,-1.17 4.829,-1.159C45.288,20.65 46,20.75 46,20.75c-0.625,-0.625 -1.444,-1.26 -2.156,-1.661C42.519,18.344 41.157,18 39.75,18c-2,0 -3,0.624 -3.535,0.624 -0.215,0 -0.32,-0.058 -0.394,-0.156 -0.065,-0.085 -0.04,-0.276 0.01,-0.395C36.125,17.5 36.934,17.045 37,17c-0.625,0 -2.003,0.741 -2.796,1.355 -0.533,0.412 -1.062,0.829 -1.599,1.236 -0.108,0.082 -0.238,0.147 -0.368,0.185 -0.353,0.103 -0.533,-0.071 -0.429,-0.414 0.047,-0.154 0.119,-0.304 0.199,-0.444 0.129,-0.227 0.275,-0.445 0.425,-0.683 -3.807,2.391 -4.459,5.671 -4.432,5.641 1.118,-1.216 2.382,-2.165 3.875,-2.875 1.495,-0.711 3.125,-1.125 5,-1.125 0,0 0.125,0.101 0.125,0.125 0,0 -0.75,0 -1.545,0.237C33.954,20.583 32.592,21.243 31.32,22.083z" />
|
||||||
android:offset="0.0" />
|
<path
|
||||||
<item
|
android:fillColor="#e53935"
|
||||||
android:color="#00000000"
|
android:pathData="M16.75,11.25c0,0.5 0.132,0.997 0.275,1.413c0.372,1.086 1.086,1.964 1.899,2.76c0.802,0.785 1.649,1.525 2.467,2.294c0.825,0.775 1.542,1.63 1.968,2.686c0.241,0.597 0.36,1.216 0.266,1.858c-0.026,0.179 0.04,0.265 0.199,0.333c0.406,0.173 0.664,0.46 0.686,0.915c0.01,0.215 0.144,0.181 0.273,0.114c0.716,-0.372 1.045,-0.935 1.091,-0.997c0.082,0.171 0.127,0.383 0.125,0.625c-0.012,1.346 -1.205,3.555 -2.328,3.995c0.015,-0.098 0.026,-0.177 0.04,-0.256c0.158,-0.888 -0.284,-1.616 -1.153,-1.906c-0.587,-0.196 -1.072,-0.2 -1.685,-0.209c-0.094,-0.001 -0.357,0 -0.5,0c0.047,-0.108 0.393,-0.823 0.47,-1.101c0.11,-0.395 -0.034,-0.677 -0.439,-0.766c-0.439,-0.097 -0.896,-0.158 -1.345,-0.156c-1.032,0.005 -2.055,0.129 -3.059,0.372c-0.14,0.034 -0.366,0.106 -0.503,0.151c0.875,0 2,0.375 2,0.375s-0.5,1 -0.5,1.625c0,0.5 0.25,0.875 0.5,0.875S17.886,26.082 18,26c-0.023,0.136 -0.161,0.855 -0.18,1.193C17.791,27.724 18,28 18.625,28c0.125,0 0.25,0 0.375,0c-0.25,0.875 -2.132,2.027 -3.25,2c-0.074,-0.002 -0.143,-0.009 -0.208,-0.022C16,29.75 16.69,28.875 16.69,28.501l-1.492,0.19c0,0 0.509,-0.635 0.672,-0.954c0.423,-0.825 0.47,-1.668 -0.09,-2.456c-0.473,-0.666 -1.239,-0.951 -2.03,-1.03c-1.25,0 -1.9,0.35 -2,0.375c0.569,-1.24 1.381,-2.005 2.625,-2.625c-1,-0.5 -2,-0.625 -2,-0.625c1,-0.625 2.5,-1.125 3.625,-1.375c-0.125,0 -0.437,0.004 -0.5,0c-3.75,0 -5.219,1.938 -5.25,2c0.605,0.104 1,0.125 1.75,0.375c0,0 -2,0.375 -5,2.625c0.02,-0.113 0.375,-0.875 0.375,-1.625c0,-2.625 -3.324,-2.099 -3.375,-2.125c0.081,-0.073 0.75,-0.625 2.863,-1.645c0.396,-0.214 1.512,-0.73 1.512,-1.355c0,-0.875 -1.735,-2.15 -1.875,-2.25c1.375,0 2.147,0.129 3.25,0.375c-0.588,-1.004 -0.752,-2.05 -0.486,-3.088c0.426,0.8 1.038,1.428 1.784,1.92c0.686,0.453 1.409,0.852 2.111,1.282c0.532,0.326 1.084,0.628 1.576,1.005c0.378,0.29 0.677,0.681 1.008,1.028c0.055,0.058 0.099,0.126 0.149,0.189c0.029,-0.014 0.057,-0.028 0.086,-0.042c-0.079,-0.197 -0.142,-0.402 -0.239,-0.591c-0.393,-0.765 -0.974,-1.378 -1.693,-1.852c-0.295,-0.195 -0.456,-0.426 -0.513,-0.753c-0.171,-0.969 -0.175,-1.919 0.011,-2.85C13.645,12.121 14.25,11 14.25,11v0.125c0.034,1.325 0.538,2.386 1.375,3.375c0.878,1.037 2.027,1.805 3.017,2.73c1.733,1.645 2.471,2.568 2.608,2.77c0,0 -0.051,-0.214 -0.082,-0.283c-0.476,-1.091 -1.247,-1.97 -2.11,-2.778c-0.75,-0.702 -1.512,-1.393 -2.275,-2.082c-0.276,-0.25 -0.439,-0.547 -0.483,-0.91C16.125,12.5 16.737,11.259 16.75,11.25zM19,20c0,0 0.08,0.497 0.199,0.743C19.75,21.875 21,22 21,22s0,-0.875 -1.25,-1.75C19.381,19.992 19,20 19,20z" />
|
||||||
android:offset="1.0" />
|
<path
|
||||||
</gradient>
|
android:fillColor="#e53935"
|
||||||
</aapt:attr>
|
android:pathData="M8,12.625C8.25,11.375 9,10 9,10s0.463,1.573 1.25,2.625c0.389,0.52 0.909,1.098 1.514,1.497c0.114,0.075 0.229,0.149 0.346,0.222c-0.022,-0.674 0.031,-1.334 0.159,-1.974c0.109,-0.545 0.492,-1.328 0.73,-1.784V10.5C13,8.567 14.343,7 16,7s3,1.567 3,3.5c0,1.377 -0.111,2.059 -0.271,2.5c-0.014,0.037 -0.028,0.071 -0.042,0.106c0.131,0.212 0.287,0.426 0.465,0.645c0.011,0.013 0.02,0.026 0.031,0.039c0.187,0.226 0.4,0.459 0.65,0.704c0.471,0.461 0.976,0.922 1.463,1.367c0.33,0.301 0.66,0.602 0.985,0.908c0.079,0.074 0.142,0.147 0.217,0.221C24.042,15.314 25,13.142 25,11c0,-5 -4.029,-8 -9,-8s-9,3.125 -9,8c0,1.216 0.318,2.439 0.865,3.58C7.829,14.041 7.892,13.167 8,12.625z" />
|
||||||
</path>
|
</group>
|
||||||
<path
|
</vector>
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:fillType="nonZero"
|
|
||||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="#00000000" />
|
|
||||||
</vector>
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 982 B After Width: | Height: | Size: 1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 9.4 KiB |
|
|
@ -39,4 +39,6 @@
|
||||||
<string name="search_field_search">Rechercher</string>
|
<string name="search_field_search">Rechercher</string>
|
||||||
<string name="search_field_race">Race</string>
|
<string name="search_field_race">Race</string>
|
||||||
<string name="search_field_gender">Sexe</string>
|
<string name="search_field_gender">Sexe</string>
|
||||||
|
<string name="search_item_description">Description :</string>
|
||||||
|
<string name="search_item_history">Histoire :</string>
|
||||||
</resources>
|
</resources>
|
||||||
4
app/src/main/res/values/ic_launcher_background.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#1C1B1F</color>
|
||||||
|
</resources>
|
||||||
|
|
@ -39,4 +39,6 @@
|
||||||
<string name="search_field_search">Search</string>
|
<string name="search_field_search">Search</string>
|
||||||
<string name="search_field_race">Race</string>
|
<string name="search_field_race">Race</string>
|
||||||
<string name="search_field_gender">Gender</string>
|
<string name="search_field_gender">Gender</string>
|
||||||
|
<string name="search_item_description">Description:</string>
|
||||||
|
<string name="search_item_history">History:</string>
|
||||||
</resources>
|
</resources>
|
||||||