Disable search fab and add a skeleton of search screen.

This commit is contained in:
Andres Gomez, Thomas (ITDV CC) - AF (ext) 2023-07-16 17:52:19 +02:00
parent 54e09e5f1d
commit 70aa2a7972
15 changed files with 296 additions and 49 deletions

View file

@ -0,0 +1,20 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.pixelized.rplexicon",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "0.1.0",
"outputFile": "app-release.apk"
}
],
"elementType": "File"
}

View file

@ -23,12 +23,7 @@ class AuthenticationRepository @Inject constructor(
val credential: GoogleAccountCredential val credential: GoogleAccountCredential
get() { get() {
val credential = GoogleAccountCredential val credential = GoogleAccountCredential
.usingOAuth2( .usingOAuth2(context, capabilities)
context, listOf(
SheetsScopes.SPREADSHEETS,
SheetsScopes.SPREADSHEETS_READONLY,
)
)
.setBackOff(ExponentialBackOff()) .setBackOff(ExponentialBackOff())
credential.selectedAccount = signInCredential.value?.let { credential.selectedAccount = signInCredential.value?.let {
@ -43,4 +38,11 @@ class AuthenticationRepository @Inject constructor(
) { ) {
signInCredential.value = credential signInCredential.value = credential
} }
companion object {
private val capabilities = listOf(
// SheetsScopes.SPREADSHEETS,
SheetsScopes.SPREADSHEETS_READONLY,
)
}
} }

View file

@ -95,16 +95,16 @@ class LexiconRepository @Inject constructor(
Lexicon( Lexicon(
id = index, id = index,
name = name, name = name,
diminutive = diminutive, diminutive = diminutive?.takeIf { it.isNotBlank() },
gender = when (gender) { gender = when (gender) {
"Male" -> Lexicon.Gender.MALE "Male" -> Lexicon.Gender.MALE
"Femelle" -> Lexicon.Gender.FEMALE "Femelle" -> Lexicon.Gender.FEMALE
else -> Lexicon.Gender.UNDETERMINED else -> Lexicon.Gender.UNDETERMINED
}, },
race = race, race = race?.takeIf { it.isNotBlank() },
portrait = portrait?.split("\n")?.mapNotNull { it.toUriOrNull() } ?: emptyList(), portrait = portrait?.split("\n")?.mapNotNull { it.toUriOrNull() } ?: emptyList(),
description = description, description = description?.takeIf { it.isNotBlank() },
history = history, history = history?.takeIf { it.isNotBlank() },
) )
} else { } else {
null null
@ -119,7 +119,7 @@ class LexiconRepository @Inject constructor(
} }
private fun String?.toUriOrNull(): Uri? = try { private fun String?.toUriOrNull(): Uri? = try {
this?.toUri() this?.takeIf { it.isNotBlank() }?.toUri()
} catch (_: Exception) { } catch (_: Exception) {
null null
} }

View file

@ -9,6 +9,11 @@ import androidx.navigation.NavHostController
import androidx.navigation.NavOptionsBuilder import androidx.navigation.NavOptionsBuilder
import com.google.accompanist.navigation.animation.AnimatedNavHost import com.google.accompanist.navigation.animation.AnimatedNavHost
import com.google.accompanist.navigation.animation.rememberAnimatedNavController import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import com.pixelized.rplexicon.ui.navigation.screens.AUTHENTICATION_ROUTE
import com.pixelized.rplexicon.ui.navigation.screens.composableAuthentication
import com.pixelized.rplexicon.ui.navigation.screens.composableCharacterDetail
import com.pixelized.rplexicon.ui.navigation.screens.composableLexicon
import com.pixelized.rplexicon.ui.navigation.screens.composableSearch
val LocalScreenNavHost = staticCompositionLocalOf<NavHostController> { val LocalScreenNavHost = staticCompositionLocalOf<NavHostController> {
error("LocalScreenNavHost not ready") error("LocalScreenNavHost not ready")
@ -32,12 +37,11 @@ fun ScreenNavHost(
composableAuthentication() composableAuthentication()
composableLexicon(lazyListState = lexiconListState) composableLexicon(lazyListState = lexiconListState)
composableCharacterDetail() composableCharacterDetail()
composableSearch()
} }
} }
} }
fun defaultOption(): NavOptionsBuilder.() -> Unit = { }
fun rootOption(): NavOptionsBuilder.() -> Unit = { fun rootOption(): NavOptionsBuilder.() -> Unit = {
launchSingleTop = true launchSingleTop = true
restoreState = true restoreState = true

View file

@ -1,8 +1,10 @@
package com.pixelized.rplexicon.ui.navigation package com.pixelized.rplexicon.ui.navigation.screens
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.NavOptionsBuilder import androidx.navigation.NavOptionsBuilder
import com.pixelized.rplexicon.ui.navigation.NavigationAnimation
import com.pixelized.rplexicon.ui.navigation.animatedComposable
import com.pixelized.rplexicon.ui.screens.authentication.AuthenticationScreen import com.pixelized.rplexicon.ui.screens.authentication.AuthenticationScreen
private const val ROUTE = "authentication" private const val ROUTE = "authentication"

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.ui.navigation package com.pixelized.rplexicon.ui.navigation.screens
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
@ -8,6 +8,8 @@ import androidx.navigation.NavHostController
import androidx.navigation.NavOptionsBuilder import androidx.navigation.NavOptionsBuilder
import androidx.navigation.NavType import androidx.navigation.NavType
import androidx.navigation.navArgument import androidx.navigation.navArgument
import com.pixelized.rplexicon.ui.navigation.NavigationAnimation
import com.pixelized.rplexicon.ui.navigation.animatedComposable
import com.pixelized.rplexicon.ui.screens.detail.CharacterDetailScreen import com.pixelized.rplexicon.ui.screens.detail.CharacterDetailScreen
import com.pixelized.rplexicon.utilitary.extentions.ARG import com.pixelized.rplexicon.utilitary.extentions.ARG

View file

@ -1,10 +1,11 @@
package com.pixelized.rplexicon.ui.navigation package com.pixelized.rplexicon.ui.navigation.screens
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.NavOptionsBuilder import androidx.navigation.NavOptionsBuilder
import com.pixelized.rplexicon.ui.navigation.NavigationAnimation
import com.pixelized.rplexicon.ui.navigation.animatedComposable
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconScreen import com.pixelized.rplexicon.ui.screens.lexicon.LexiconScreen
private const val ROUTE = "lexicon" private const val ROUTE = "lexicon"

View file

@ -0,0 +1,27 @@
package com.pixelized.rplexicon.ui.navigation.screens
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.NavOptionsBuilder
import com.pixelized.rplexicon.ui.navigation.NavigationAnimation
import com.pixelized.rplexicon.ui.navigation.animatedComposable
import com.pixelized.rplexicon.ui.screens.search.SearchScreen
private const val ROUTE = "search"
const val SEARCH_ROUTE = ROUTE
fun NavGraphBuilder.composableSearch() {
animatedComposable(
route = SEARCH_ROUTE,
animation = NavigationAnimation.Push,
) {
SearchScreen()
}
}
fun NavHostController.navigateToSearch(
option: NavOptionsBuilder.() -> Unit = {},
) {
navigate(route = ROUTE, builder = option)
}

View file

@ -30,7 +30,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.LocalActivity import com.pixelized.rplexicon.LocalActivity
import com.pixelized.rplexicon.R import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
import com.pixelized.rplexicon.ui.navigation.navigateToLexicon import com.pixelized.rplexicon.ui.navigation.screens.navigateToLexicon
import com.pixelized.rplexicon.ui.navigation.rootOption import com.pixelized.rplexicon.ui.navigation.rootOption
import com.pixelized.rplexicon.ui.theme.LexiconTheme import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.ui.theme.colors.GoogleColorPalette import com.pixelized.rplexicon.ui.theme.colors.GoogleColorPalette

View file

@ -175,13 +175,13 @@ private fun CharacterDetailScreenContent(
modifier = Modifier.padding(horizontal = 16.dp), modifier = Modifier.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp) horizontalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
item.value.race?.let { item.value.gender?.let {
Text( Text(
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) }, style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
text = it, text = it,
) )
} }
item.value.gender?.let { item.value.race?.let {
Text( Text(
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) }, style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
text = it, text = it,

View file

@ -6,7 +6,7 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.pixelized.rplexicon.model.Lexicon import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.repository.LexiconRepository import com.pixelized.rplexicon.repository.LexiconRepository
import com.pixelized.rplexicon.ui.navigation.characterDetailArgument import com.pixelized.rplexicon.ui.navigation.screens.characterDetailArgument
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
@ -21,11 +21,11 @@ class CharacterDetailViewModel @Inject constructor(
val source = repository.data.value[savedStateHandle.characterDetailArgument.id] val source = repository.data.value[savedStateHandle.characterDetailArgument.id]
return CharacterDetailUio( return CharacterDetailUio(
name = source.name, name = source.name,
diminutive = source.diminutive?.takeIf { it.isNotBlank() }?.let { "./ $it" }, diminutive = source.diminutive?.let { "./ $it" },
gender = when (source.gender) { gender = when (source.gender) {
Lexicon.Gender.MALE -> "homme" Lexicon.Gender.MALE -> "Male"
Lexicon.Gender.FEMALE -> "femme" Lexicon.Gender.FEMALE -> "Femelle"
Lexicon.Gender.UNDETERMINED -> "inconnu" Lexicon.Gender.UNDETERMINED -> "Inconnu"
}, },
race = source.race, race = source.race,
portrait = source.portrait, portrait = source.portrait,

View file

@ -48,7 +48,8 @@ import com.pixelized.rplexicon.LocalSnack
import com.pixelized.rplexicon.R import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.FloatingActionButton import com.pixelized.rplexicon.ui.composable.FloatingActionButton
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
import com.pixelized.rplexicon.ui.navigation.navigateToCharacterDetail import com.pixelized.rplexicon.ui.navigation.screens.navigateToCharacterDetail
import com.pixelized.rplexicon.ui.navigation.screens.navigateToSearch
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Default import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Default
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Permission import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Permission
import com.pixelized.rplexicon.ui.theme.LexiconTheme import com.pixelized.rplexicon.ui.theme.LexiconTheme
@ -96,7 +97,7 @@ fun LexiconScreen(
refreshing = viewModel.isLoading, refreshing = viewModel.isLoading,
isFabExpended = isFabExpended, isFabExpended = isFabExpended,
onSearch = { onSearch = {
screen.navigateToSearch()
}, },
onItem = { onItem = {
screen.navigateToCharacterDetail(id = it.id) screen.navigateToCharacterDetail(id = it.id)
@ -134,26 +135,26 @@ private fun LexiconScreenContent(
) )
}, },
floatingActionButton = { floatingActionButton = {
FloatingActionButton( // FloatingActionButton(
modifier = Modifier.padding(start = 32.dp), // modifier = Modifier.padding(start = 32.dp),
expended = isFabExpended.value, // expended = isFabExpended.value,
onClick = onSearch, // onClick = onSearch,
icon = { // icon = {
Icon( // Icon(
tint = MaterialTheme.colorScheme.onPrimary, // tint = MaterialTheme.colorScheme.onPrimary,
painter = painterResource(id = R.drawable.ic_baseline_search_24), // painter = painterResource(id = R.drawable.ic_baseline_search_24),
contentDescription = null, // contentDescription = null,
) // )
}, // },
text = { // text = {
val typography = MaterialTheme.typography // val typography = MaterialTheme.typography
Text( // Text(
color = MaterialTheme.colorScheme.onPrimary, // color = MaterialTheme.colorScheme.onPrimary,
style = remember { typography.bodyLarge.copy(fontWeight = FontWeight.Bold) }, // style = remember { typography.bodyLarge.copy(fontWeight = FontWeight.Bold) },
text = "Rechercher", // text = "Rechercher",
) // )
}, // },
) // )
} }
) { ) {
Box( Box(
@ -167,7 +168,7 @@ private fun LexiconScreenContent(
state = lazyColumnState, state = lazyColumnState,
contentPadding = PaddingValues( contentPadding = PaddingValues(
top = 8.dp, top = 8.dp,
bottom = 8.dp + 16.dp + 56.dp + 16.dp bottom = 8.dp + 16.dp // + 56.dp + 16.dp,
), ),
) { ) {
items(items = items.value) { item -> items(items = items.value) { item ->

View file

@ -37,7 +37,7 @@ class LexiconViewModel @Inject constructor(
name = item.name, name = item.name,
diminutive = item.diminutive?.takeIf { it.isNotBlank() }?.let { "./ $it" }, diminutive = item.diminutive?.takeIf { it.isNotBlank() }?.let { "./ $it" },
gender = when (item.gender) { gender = when (item.gender) {
Lexicon.Gender.MALE -> "h." Lexicon.Gender.MALE -> "m."
Lexicon.Gender.FEMALE -> "f." Lexicon.Gender.FEMALE -> "f."
Lexicon.Gender.UNDETERMINED -> "u." Lexicon.Gender.UNDETERMINED -> "u."
}, },

View file

@ -0,0 +1,183 @@
package com.pixelized.rplexicon.ui.screens.search
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ExposedDropdownMenuBox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
import com.pixelized.rplexicon.ui.theme.LexiconTheme
@Composable
fun SearchScreen() {
val screen = LocalScreenNavHost.current
Surface {
SearchScreenContent(
modifier = Modifier.fillMaxSize(),
onBack = {
screen.popBackStack()
}
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SearchScreenContent(
modifier: Modifier = Modifier,
onBack: () -> Unit,
) {
Scaffold(
modifier = modifier,
containerColor = Color.Transparent,
topBar = {
TopAppBar(
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
painter = painterResource(id = R.drawable.ic_arrow_back_ios_new_24),
contentDescription = null
)
}
},
title = {
Text(text = "Rechercher")
},
)
},
) {
Column(
modifier = Modifier
.padding(paddingValues = it)
.padding(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
TextField(
modifier = Modifier.fillMaxWidth(),
value = "",
label = {
Text("Nom")
},
onValueChange = { _ -> },
colors = TextFieldDefaults.colors(
focusedContainerColor = MaterialTheme.colorScheme.surface,
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
),
)
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
DropDownField(
modifier = Modifier.weight(1f),
subject = remember { mutableStateOf("1") },
subjects = listOf("1", "2"),
onChange = { },
expanded = remember { mutableStateOf(false) },
onExpandedChange = { }
)
DropDownField(
modifier = Modifier.weight(1f),
subject = remember { mutableStateOf("1") },
subjects = listOf("1", "2"),
onChange = { },
expanded = remember { mutableStateOf(false) },
onExpandedChange = { }
)
}
}
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun DropDownField(
modifier: Modifier = Modifier,
subjects: List<String>,
subject: State<String>,
onChange: (String) -> Unit,
expanded: State<Boolean>,
onExpandedChange: (Boolean) -> Unit
) {
ExposedDropdownMenuBox(
modifier = modifier,
expanded = expanded.value,
onExpandedChange = onExpandedChange,
) {
TextField(
modifier = Modifier.clickable { onExpandedChange(true) },
readOnly = true,
singleLine = true,
placeholder = {
Text(
text = "pouet"
)
},
trailingIcon = {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_arrow_drop_down_24),
contentDescription = null,
)
},
colors = TextFieldDefaults.colors(
focusedContainerColor = MaterialTheme.colorScheme.surface,
unfocusedContainerColor = MaterialTheme.colorScheme.surface,
),
value = subject.value,
onValueChange = {},
)
ExposedDropdownMenu(
expanded = expanded.value,
onDismissRequest = { onExpandedChange(false) },
) {
subjects.forEach {
DropdownMenuItem(
onClick = { onChange(it) },
content = { Text(text = it) },
)
}
}
}
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)
private fun SearchScreenContentPreview() {
LexiconTheme {
Surface {
SearchScreenContent(
modifier = Modifier.fillMaxSize(),
onBack = { },
)
}
}
}

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M7,10l5,5 5,-5z"/>
</vector>