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
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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.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)
|
||||
}
|
||||
|
|
@ -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 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,
|
||||
search = "Bru",
|
||||
highlightGender = true,
|
||||
highlightRace = true,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.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,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
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_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>
|
||||
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_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>
|
||||