Advenced search layout.

This commit is contained in:
Andres Gomez, Thomas (ITDV CC) - AF (ext) 2023-07-19 17:58:23 +02:00
parent af5fb8f33c
commit 5ccc5dc882
35 changed files with 759 additions and 163 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View file

@ -1,7 +1,9 @@
package com.pixelized.rplexicon.model
import android.net.Uri
import androidx.compose.runtime.Stable
@Stable
data class Lexicon(
val id: Int,
val name: String,
@ -12,12 +14,15 @@ data class Lexicon(
val description: String?,
val history: String?,
) {
@Stable
enum class Gender {
MALE,
FEMALE,
UNDETERMINED
UNDETERMINED,
}
@Stable
enum class Race {
ELF,
HALFLING,
@ -32,6 +37,6 @@ data class Lexicon(
GENASI,
DEEP_GNOME,
GOLIATH,
UNDETERMINED
UNDETERMINED,
}
}

View file

@ -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()
}
}

View file

@ -8,6 +8,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.NavOptionsBuilder
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.ui.navigation.NavigationAnimation
import com.pixelized.rplexicon.ui.navigation.animatedComposable
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 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
@Immutable
data class CharacterDetailArgument(
val id: Int,
val highlight: String?,
val race: Lexicon.Race,
val highlightRace: Boolean,
val gender: Lexicon.Gender,
val highlightGender: Boolean,
)
val SavedStateHandle.characterDetailArgument: 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() {
@ -34,7 +71,23 @@ fun NavGraphBuilder.composableCharacterDetail() {
arguments = listOf(
navArgument(name = ARG_ID) {
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,
) {
@ -44,8 +97,18 @@ fun NavGraphBuilder.composableCharacterDetail() {
fun NavHostController.navigateToCharacterDetail(
id: Int,
highlight: String? = null,
race: Lexicon.Race? = null,
gender: Lexicon.Gender? = null,
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)
}

View file

@ -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_YES
import android.net.Uri
import androidx.annotation.StringRes
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
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.res.painterResource
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.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
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.skydoves.landscapist.ImageOptions
import com.skydoves.landscapist.glide.GlideImage
@ -66,15 +74,79 @@ import com.skydoves.landscapist.glide.GlideImageState
@Stable
data class CharacterDetailUio(
val name: String?,
val name: String,
val diminutive: String?,
@StringRes val gender: Int,
@StringRes val race: Int,
val gender: Lexicon.Gender,
val race: Lexicon.Race,
val portrait: List<Uri>,
val description: 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
fun CharacterDetailScreen(
viewModel: CharacterDetailViewModel = hiltViewModel(),
@ -100,6 +172,7 @@ private fun CharacterDetailScreenContent(
) {
val colorScheme = MaterialTheme.colorScheme
val typography = MaterialTheme.typography
val annotatedItem = item.value.annotated()
Scaffold(
modifier = modifier,
@ -123,7 +196,7 @@ private fun CharacterDetailScreenContent(
Box(
modifier = Modifier.padding(paddingValues = paddingValues),
) {
item.value.portrait.firstOrNull()?.let { uri ->
annotatedItem.portrait.firstOrNull()?.let { uri ->
Box(
modifier = Modifier
.fillMaxWidth()
@ -169,14 +242,14 @@ private fun CharacterDetailScreenContent(
modifier = Modifier.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
item.value.name?.let {
annotatedItem.name.let {
Text(
modifier = Modifier.alignByBaseline(),
style = typography.headlineSmall,
text = it,
)
}
item.value.diminutive?.let {
annotatedItem.diminutive?.let {
Text(
modifier = Modifier.alignByBaseline(),
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
@ -190,14 +263,14 @@ private fun CharacterDetailScreenContent(
) {
Text(
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
text = stringResource(id = item.value.gender),
text = annotatedItem.gender,
)
Text(
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(
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
style = typography.titleMedium,
@ -216,7 +289,7 @@ private fun CharacterDetailScreenContent(
text = it,
)
}
item.value.history?.let {
annotatedItem.history?.let {
Text(
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
style = typography.titleMedium,
@ -228,7 +301,7 @@ private fun CharacterDetailScreenContent(
text = it,
)
}
if (item.value.portrait.isNotEmpty()) {
if (annotatedItem.portrait.isNotEmpty()) {
val configuration = LocalConfiguration.current
val maxSize = remember { (configuration.screenWidthDp.dp - 16.dp * 2) }
Text(
@ -240,7 +313,7 @@ private fun CharacterDetailScreenContent(
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(items = item.value.portrait) {
items(items = annotatedItem.portrait) {
val transition = rememberLoadingTransition { it }
GlideImage(
modifier = Modifier
@ -298,13 +371,16 @@ private fun CharacterDetailScreenContentPreview() {
CharacterDetailUio(
name = "Brulkhai",
diminutive = "./ Bru",
gender = R.string.gender_female,
race = R.string.race_half_orc,
gender = Lexicon.Gender.FEMALE,
race = Lexicon.Race.HALF_ORC,
portrait = listOf(
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 lorsquelle est en bonne santé. Elle a les cheveux châtains, les yeux noisettes et la peau couleur gris-vert typique de son espèce. Dun 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 quelle considère plus faibles quelle. Dune 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 dun manque déducation et dune capacité limitée à gérer ses émotions quà une débilité congénitale. Elle voue à la force un culte car cest par son expression quelle se sent vraiment vivante et éprouve de grandes difficultés vis à vis de ceux quelle nomme foshnu (bébé, chouineur en commun).",
history = null,
search = "Bru",
highlightGender = true,
highlightRace = true,
)
)
}

View file

@ -4,8 +4,6 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
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.ui.navigation.screens.characterDetailArgument
import dagger.hilt.android.lifecycle.HiltViewModel
@ -13,40 +11,28 @@ import javax.inject.Inject
@HiltViewModel
class CharacterDetailViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
private val repository: LexiconRepository,
savedStateHandle: SavedStateHandle,
repository: LexiconRepository,
) : ViewModel() {
val character: State<CharacterDetailUio> = mutableStateOf(init())
val character: State<CharacterDetailUio>
private fun init(): CharacterDetailUio {
val source = repository.data.value[savedStateHandle.characterDetailArgument.id]
return CharacterDetailUio(
name = source.name,
diminutive = source.diminutive?.let { "./ $it" },
gender = when (source.gender) {
Lexicon.Gender.MALE -> R.string.gender_male
Lexicon.Gender.FEMALE -> R.string.gender_female
Lexicon.Gender.UNDETERMINED -> R.string.gender_undetermined
},
race = when (source.race) {
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
},
portrait = source.portrait,
description = source.description,
history = source.history,
init {
val argument = savedStateHandle.characterDetailArgument
val source = repository.data.value[argument.id]
character = mutableStateOf(
CharacterDetailUio(
name = source.name,
diminutive = source.diminutive?.let { "./ $it" },
gender = source.gender,
race = source.race,
portrait = source.portrait,
description = source.description,
history = source.history,
search = argument.highlight,
highlightGender = argument.highlightGender && argument.gender == source.gender,
highlightRace = argument.highlightRace && argument.race == source.race,
)
)
}
}

View file

@ -4,6 +4,7 @@ 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.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
@ -16,6 +17,7 @@ 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.res.stringResource
import androidx.compose.ui.text.font.FontStyle
@ -55,7 +57,10 @@ fun LexiconItem(
) {
val typography = MaterialTheme.typography
Surface(modifier = modifier) {
Box(
modifier = modifier,
contentAlignment = Alignment.Center,
) {
Column(
modifier = Modifier
.fillMaxWidth()

View file

@ -12,13 +12,13 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.with
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
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.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
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.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
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.Structure
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.lexicon
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
@ -131,8 +133,9 @@ fun LexiconScreen(
}
@OptIn(
ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class,
ExperimentalFoundationApi::class, ExperimentalAnimationApi::class
ExperimentalMaterial3Api::class,
ExperimentalMaterialApi::class,
ExperimentalAnimationApi::class,
)
@Composable
private fun LexiconScreenContent(
@ -208,7 +211,7 @@ private fun LexiconScreenContent(
) {
items(count = 6) {
LexiconItem(
modifier = Modifier.animateItemPlacement(),
modifier = Modifier.heightIn(min = MaterialTheme.lexicon.dimens.item),
item = LexiconItemUio.Brulkhai,
)
}
@ -231,8 +234,8 @@ private fun LexiconScreenContent(
) {
LexiconItem(
modifier = Modifier
.animateItemPlacement()
.clickable { onItem(it) },
.clickable { onItem(it) }
.heightIn(min = MaterialTheme.lexicon.dimens.item),
item = it,
)
}

View file

@ -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 lorsquelle est en bonne santé. Elle a les cheveux châtains, les yeux noisettes et la peau couleur gris-vert typique de son espèce. Dun 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 quelle considère plus faibles quelle.\n" +
"Dune 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 dun manque déducation et dune capacité limitée à gérer ses émotions quà une débilité congénitale.\n" +
"Elle voue à la force un culte car cest par son expression quelle se sent vraiment vivante et éprouve de grandes difficultés vis à vis de ceux quelle 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),
)
}

View file

@ -10,6 +10,7 @@ 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.heightIn
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
@ -21,6 +22,7 @@ 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
@ -30,6 +32,7 @@ import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
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.R
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.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
import com.pixelized.rplexicon.utilitary.extentions.lexicon
@Stable
data class SearchFormUio(
@ -63,6 +66,7 @@ fun SearchScreen(
viewModel: SearchViewModel = hiltViewModel(),
) {
val screen = LocalScreenNavHost.current
val lazyState = rememberLazyListState()
Surface {
SearchScreenContent(
@ -70,15 +74,25 @@ fun SearchScreen(
.fillMaxSize()
.systemBarsPadding()
.imePadding(),
lazyColumnState = lazyState,
items = viewModel.filter,
form = viewModel.form,
onItem = {
screen.navigateToCharacterDetail(id = it.id)
onItem = { item ->
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 = {
screen.popBackStack()
}
)
ScrollingKeyboardHandler(
lazyListState = lazyState,
)
}
}
@ -87,10 +101,10 @@ fun SearchScreen(
private fun SearchScreenContent(
modifier: Modifier = Modifier,
lazyColumnState: LazyListState = rememberLazyListState(),
items: State<List<LexiconItemUio>>,
items: State<List<SearchItemUio>>,
form: SearchFormUio,
onBack: () -> Unit,
onItem: (LexiconItemUio) -> Unit,
onItem: (SearchItemUio) -> Unit,
) {
Scaffold(
modifier = modifier,
@ -147,12 +161,12 @@ private fun SearchScreenContent(
items(
items = items.value,
key = { it.id },
contentType = { "Lexicon" },
contentType = { "Search" },
) {
LexiconItem(
SearchItem(
modifier = Modifier
.animateItemPlacement()
.clickable { onItem(it) },
.clickable { onItem(it) }
.heightIn(min = MaterialTheme.lexicon.dimens.item),
item = it,
)
}
@ -185,12 +199,17 @@ private fun SearchScreenContentPreview() {
items = remember {
mutableStateOf(
listOf(
LexiconItemUio(
SearchItemUio(
id = 0,
name = "Brulkhai",
diminutive = "Bru",
gender = R.string.gender_female_short,
race = R.string.race_half_orc,
gender = Lexicon.Gender.FEMALE,
race = Lexicon.Race.HALF_ORC,
description = null,
history = null,
search = "",
highlightGender = false,
highlightRace = false,
)
)
)

View file

@ -10,86 +10,77 @@ 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,
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 "")
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,
value = _search,
onValueChange = {
search.value = it
_search.value = it
}
),
gender = DropDownFieldUio(
label = R.string.search_field_gender,
values = genders(),
value = gender,
onValueChange = { id, value -> gender.value = id to value },
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 },
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
},
)
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
}
.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 {
private fun genders() = listOf(
Gender.MALE to R.string.gender_male,

View file

@ -15,6 +15,8 @@ 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.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
@ -26,6 +28,7 @@ val LocalLexiconTheme = compositionLocalOf<LexiconTheme> {
data class LexiconTheme(
val colorScheme: LexiconColors,
val shapes: LexiconShapes,
val dimens: LexiconDimens,
)
@Composable
@ -39,7 +42,8 @@ fun LexiconTheme(
true -> darkColorScheme()
else -> lightColorScheme()
},
shapes = lexiconShapes()
shapes = lexiconShapes(),
dimens = lexiconDimen(),
)
}

View file

@ -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,
)

View file

@ -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
}
}
)
}

View file

@ -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
}
)
}

View file

@ -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)
)
}

View file

@ -1,3 +0,0 @@
package com.pixelized.rplexicon.utilitary.extentions
val String.ARG: String get() = "$this={$this}"

View file

@ -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,
)
}

View 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>

View file

@ -1,30 +1,23 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<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">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
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>
android:viewportWidth="48"
android:viewportHeight="48">
<group android:scaleX="0.54"
android:scaleY="0.54"
android:translateX="11.04"
android:translateY="11.04">
<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" />
</group>
</vector>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

After

Width:  |  Height:  |  Size: 1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Before After
Before After

View file

@ -39,4 +39,6 @@
<string name="search_field_search">Rechercher</string>
<string name="search_field_race">Race</string>
<string name="search_field_gender">Sexe</string>
<string name="search_item_description">Description :</string>
<string name="search_item_history">Histoire :</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#1C1B1F</color>
</resources>

View file

@ -39,4 +39,6 @@
<string name="search_field_search">Search</string>
<string name="search_field_race">Race</string>
<string name="search_field_gender">Gender</string>
<string name="search_item_description">Description:</string>
<string name="search_item_history">History:</string>
</resources>