Added complete navigation system, added settings page and splitted the code to be easier to understand

This commit is contained in:
Mathieu 2022-01-03 16:46:44 +01:00
parent f318467465
commit 20188c0b66
10 changed files with 363 additions and 175 deletions

View File

@ -18,6 +18,13 @@
<entry key="../../../../../layout/compose-model-1641206474090.xml" value="0.33" /> <entry key="../../../../../layout/compose-model-1641206474090.xml" value="0.33" />
<entry key="../../../../../layout/compose-model-1641212403026.xml" value="0.25" /> <entry key="../../../../../layout/compose-model-1641212403026.xml" value="0.25" />
<entry key="../../../../../layout/compose-model-1641213954769.xml" value="0.33" /> <entry key="../../../../../layout/compose-model-1641213954769.xml" value="0.33" />
<entry key="../../../../../layout/compose-model-1641218837604.xml" value="0.38981481481481484" />
<entry key="../../../../../layout/compose-model-1641218843401.xml" value="0.2935185185185185" />
<entry key="../../../../../layout/compose-model-1641218987625.xml" value="0.38981481481481484" />
<entry key="../../../../../layout/compose-model-1641220644771.xml" value="0.38981481481481484" />
<entry key="../../../../../layout/compose-model-1641220668413.xml" value="0.38981481481481484" />
<entry key="../../../../../layout/compose-model-1641224457648.xml" value="0.38981481481481484" />
<entry key="../../../../../layout/compose-model-1641224617165.xml" value="0.38981481481481484" />
<entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.5307291666666667" /> <entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.5307291666666667" />
<entry key="app/src/main/res/drawable/ic_baseline_keyboard_24.xml" value="0.38981481481481484" /> <entry key="app/src/main/res/drawable/ic_baseline_keyboard_24.xml" value="0.38981481481481484" />
<entry key="app/src/main/res/drawable/ic_baseline_more_vert_24.xml" value="0.38981481481481484" /> <entry key="app/src/main/res/drawable/ic_baseline_more_vert_24.xml" value="0.38981481481481484" />

View File

@ -55,6 +55,7 @@ dependencies {
implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version" implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation "androidx.navigation:navigation-compose:2.4.0-rc01"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
implementation 'androidx.activity:activity-compose:1.4.0' implementation 'androidx.activity:activity-compose:1.4.0'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'

View File

@ -4,30 +4,29 @@ import android.os.Bundle
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.* import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.MaterialTheme
import androidx.compose.material.* import androidx.compose.material.Surface
import androidx.compose.runtime.* import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import androidx.navigation.compose.rememberNavController
import ch.mathieubroillet.jarvis.android.nav.Navigation
import ch.mathieubroillet.jarvis.android.pages.DisplayMainPage
import ch.mathieubroillet.jarvis.android.ui.theme.JarvisComposeTheme import ch.mathieubroillet.jarvis.android.ui.theme.JarvisComposeTheme
import ch.mathieubroillet.jarvis.android.ui.theme.productSansFont
import ch.mathieubroillet.jarvis.android.utils.IconAlertDialogTextField
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
window.setFlags( window.setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
@ -35,6 +34,7 @@ class MainActivity : ComponentActivity() {
) )
setContent { setContent {
// Fix the status & navigation bars staying white when white theme is on with activity in fullscreen // Fix the status & navigation bars staying white when white theme is on with activity in fullscreen
val wic = WindowInsetsControllerCompat(window, window.decorView) val wic = WindowInsetsControllerCompat(window, window.decorView)
wic.isAppearanceLightStatusBars = !isSystemInDarkTheme() wic.isAppearanceLightStatusBars = !isSystemInDarkTheme()
@ -47,176 +47,18 @@ class MainActivity : ComponentActivity() {
.fillMaxWidth() .fillMaxWidth()
.fillMaxHeight() .fillMaxHeight()
) { ) {
//TODO: check if this is okay??? Navigation()
DefaultPreview()
} }
} }
} }
} }
} }
var messageScroll: ScrollState? = null
//Draws the base of the main activity, that includes the 3-dots menu and the "hi text".
@Composable
fun Base() {
var expanded by remember { mutableStateOf(false) }
Column(Modifier.padding(bottom = 25.dp)) {
Row(Modifier.align(Alignment.End)) {
IconAlertDialogTextField(
R.drawable.ic_baseline_keyboard_24,
"Demandez-moi quelque chose",
"Entrez une phrase"
)
IconButton(onClick = { expanded = true }) {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_more_vert_24),
contentDescription = "3 dots button"
)
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
offset = DpOffset((-500).dp, 0.dp)
) {
DropdownMenuItem(onClick = { /* Handle refresh! */ }) {
Text("Effacer la conversation")
}
DropdownMenuItem(onClick = { /* Handle settings! */ }) {
Text("Paramètres")
}
Divider()
DropdownMenuItem(onClick = { /* Handle send feedback! */ }) {
Text("Signaler un problème")
}
}
}
Text(
text = "Bonjour, comment puis-je vous aider ?",
fontFamily = productSansFont,
fontSize = 30.sp,
modifier = Modifier.padding(top = 30.dp)
)
}
}
@Composable
fun RecordFloatingButton() {
//We create a row that we align to the bottom center of the parent box
Row(
Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.Center
) {
//Microphone floating button to manually start/stop listening
FloatingActionButton(onClick = { /*TODO*/ }, modifier = Modifier.size(70.dp)) {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_mic_24),
contentDescription = "microphone"
)
}
}
}
@Composable
fun MessageFromJarvis(text: String) {
//We create a row to contain the message and the robot image (to look like an sms)
Row(Modifier.padding(bottom = 25.dp)) {
// Adding the robot image as the sender
Image(
painter = painterResource(id = R.drawable.robot256),
contentDescription = "robot",
Modifier
.size(50.dp)
.padding(end = 10.dp)
)
// Adding the message box with the text given in the params
Box(
modifier = Modifier
.fillMaxWidth(fraction = 0.9F)
.clip(RoundedCornerShape(15.dp))
.background(color = MaterialTheme.colors.secondaryVariant)
.padding(horizontal = 10.dp, vertical = 5.dp)
) {
Text(text = text, fontFamily = productSansFont)
}
}
}
@Composable
fun MessageFromUser(text: String) {
//We create a row to contain the user message and we align the row to the right side (to look like a conversation between two people)
Row(
Modifier
.padding(bottom = 25.dp)
.fillMaxWidth(), horizontalArrangement = Arrangement.End
) {
// The message box with the text
Box(
modifier = Modifier
.fillMaxWidth(fraction = 0.8F)
.clip(RoundedCornerShape(15.dp))
.background(color = MaterialTheme.colors.secondary)
.padding(horizontal = 10.dp, vertical = 5.dp)
) {
Text(
text = text,
fontFamily = productSansFont,
color = if (!isSystemInDarkTheme()) Color.White else Color(15, 15, 15, 255)
)
}
}
}
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
fun DefaultPreview() { fun DefaultPreview() {
JarvisComposeTheme { JarvisComposeTheme {
//We create a main box with basic padding to avoid having stuff too close to every side. DisplayMainPage(rememberNavController())
Box(
Modifier
.fillMaxHeight()
.fillMaxWidth()
.padding(horizontal = 15.dp)
.padding(top = 45.dp, bottom = 10.dp)
) {
// This column regroup the base and all the conversations (everything except the footer)
Column(Modifier.padding(bottom = 80.dp)) {
Base()
messageScroll = rememberScrollState()
// This column regroup only the conversations and make them scrollable
LazyColumn(content = {
item {
// Basic interaction stuff for demo
MessageFromJarvis(text = "Salut, je suis Jarvis! \nPose moi une question et je ferais de mon mieux pour te renseigner.")
MessageFromUser(text = "Quel temps fait-il à Paris en ce moment ?")
MessageFromJarvis(text = "A Paris, il fait actuellement 10 degrés et le ciel est nuageux.")
}
})
}
// Finally we add the footer to the bottom center of the main box
Column(
Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 40.dp)
) {
RecordFloatingButton()
}
}
} }
} }

View File

@ -0,0 +1,33 @@
package ch.mathieubroillet.jarvis.android.nav
import androidx.compose.runtime.Composable
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import ch.mathieubroillet.jarvis.android.pages.DisplayMainPage
import ch.mathieubroillet.jarvis.android.pages.DisplaySettingsPage
@Composable
fun Navigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Screen.MainScreen.route) {
composable(route = Screen.MainScreen.route) {
MainScreen(navController = navController)
}
composable(route = Screen.SettingsScreen.route) {
SettingsScreen(navController = navController)
}
}
}
@Composable
fun MainScreen(navController: NavController) {
DisplayMainPage(navController)
}
@Composable
fun SettingsScreen(navController: NavController) {
DisplaySettingsPage(navController)
}

View File

@ -0,0 +1,6 @@
package ch.mathieubroillet.jarvis.android.nav
sealed class Screen(val route: String){
object MainScreen : Screen("main_screen")
object SettingsScreen : Screen("settings_screen")
}

View File

@ -0,0 +1,138 @@
package ch.mathieubroillet.jarvis.android.pages
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import ch.mathieubroillet.jarvis.android.R
import ch.mathieubroillet.jarvis.android.nav.Screen
import ch.mathieubroillet.jarvis.android.ui.theme.JarvisComposeTheme
import ch.mathieubroillet.jarvis.android.ui.theme.productSansFont
import ch.mathieubroillet.jarvis.android.utils.DefaultBox
import ch.mathieubroillet.jarvis.android.utils.IconAlertDialogTextField
import ch.mathieubroillet.jarvis.android.utils.MessageFromJarvis
import ch.mathieubroillet.jarvis.android.utils.MessageFromUser
//Draws the base of the main activity, that includes the 3-dots menu and the "hi text".
@Composable
fun Base(navController: NavController) {
Column(Modifier.padding(bottom = 25.dp)) {
Row(Modifier.align(Alignment.End)) {
IconAlertDialogTextField(
R.drawable.ic_baseline_keyboard_24,
"Demandez-moi quelque chose",
"Entrez une phrase"
)
DropDownSettingsMenu(navController)
}
Text(
text = "Bonjour, comment puis-je vous aider ?",
fontFamily = productSansFont,
fontSize = 30.sp,
modifier = Modifier.padding(top = 30.dp)
)
}
}
@Composable
fun DropDownSettingsMenu(navController: NavController) {
var expanded by remember { mutableStateOf(false) }
IconButton(onClick = { expanded = true }) {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_more_vert_24),
contentDescription = "3 dots button"
)
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
offset = DpOffset((-500).dp, 0.dp)
) {
DropdownMenuItem(onClick = { /* Handle refresh! */ }) {
Text("Effacer la conversation")
}
DropdownMenuItem(onClick = { navController.navigate(Screen.SettingsScreen.route) }) {
Text("Paramètres")
}
Divider()
DropdownMenuItem(onClick = { /* Handle send feedback! */ }) {
Text("Signaler un problème")
}
}
}
@Composable
fun StartRecordingFAB() {
//We create a row that we align to the bottom center of the parent box
Row(
Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.Center
) {
//Microphone floating button to manually start/stop listening
FloatingActionButton(onClick = { /*TODO*/ }, modifier = Modifier.size(70.dp)) {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_mic_24),
contentDescription = "microphone"
)
}
}
}
@Composable
fun DisplayMainPage(navController: NavController) {
//We create a main box with basic padding to avoid having stuff too close to every side.
DefaultBox {
// This column regroup the base and all the conversations (everything except the footer)
Column(Modifier.padding(bottom = 80.dp)) {
Base(navController)
// This column regroup only the conversations and make them scrollable
LazyColumn(content = {
item {
// Basic interaction stuff for demo
MessageFromJarvis(text = "Salut, je suis Jarvis! \nPose moi une question et je ferais de mon mieux pour te renseigner.")
MessageFromUser(text = "Quel temps fait-il à Paris en ce moment ?")
MessageFromJarvis(text = "A Paris, il fait actuellement 10 degrés et le ciel est nuageux.")
}
})
}
// Finally we add the footer to the bottom center of the main box
Column(
Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 40.dp)
) {
StartRecordingFAB()
}
}
}
@Preview(showBackground = true)
@Composable
fun MainPagePreview() {
JarvisComposeTheme {
DisplayMainPage(rememberNavController())
}
}

View File

@ -0,0 +1,57 @@
package ch.mathieubroillet.jarvis.android.pages
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import ch.mathieubroillet.jarvis.android.R
import ch.mathieubroillet.jarvis.android.nav.Screen
import ch.mathieubroillet.jarvis.android.ui.theme.JarvisComposeTheme
import ch.mathieubroillet.jarvis.android.ui.theme.productSansFont
import ch.mathieubroillet.jarvis.android.utils.DefaultBox
@Composable
fun DisplaySettingsPage(navController: NavController) {
DefaultBox {
SettingsBase(navController)
}
}
@Composable
fun SettingsBase(navController: NavController) {
Column(Modifier.padding(bottom = 25.dp)) {
Row {
IconButton(onClick = { navController.navigate(Screen.MainScreen.route) }) {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_arrow_back_24),
contentDescription = "back button"
)
}
}
Text(
text = "Paramètres",
fontFamily = productSansFont,
fontSize = 30.sp,
modifier = Modifier.padding(top = 30.dp)
)
}
}
@Preview(showBackground = true)
@Composable
fun SettingsPagePreview() {
JarvisComposeTheme {
DisplaySettingsPage(rememberNavController())
}
}

View File

@ -0,0 +1,70 @@
package ch.mathieubroillet.jarvis.android.utils
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import ch.mathieubroillet.jarvis.android.R
import ch.mathieubroillet.jarvis.android.ui.theme.productSansFont
@Composable
fun MessageFromJarvis(text: String) {
//We create a row to contain the message and the robot image (to look like an sms)
Row(Modifier.padding(bottom = 25.dp)) {
// Adding the robot image as the sender
Image(
painter = painterResource(id = R.drawable.robot256),
contentDescription = "robot",
Modifier
.size(50.dp)
.padding(end = 10.dp)
)
// Adding the message box with the text given in the params
Box(
modifier = Modifier
.fillMaxWidth(fraction = 0.9F)
.clip(RoundedCornerShape(15.dp))
.background(color = MaterialTheme.colors.secondaryVariant)
.padding(horizontal = 10.dp, vertical = 5.dp)
) {
Text(text = text, fontFamily = productSansFont)
}
}
}
@Composable
fun MessageFromUser(text: String) {
//We create a row to contain the user message and we align the row to the right side (to look like a conversation between two people)
Row(
Modifier
.padding(bottom = 25.dp)
.fillMaxWidth(), horizontalArrangement = Arrangement.End
) {
// The message box with the text
Box(
modifier = Modifier
.fillMaxWidth(fraction = 0.8F)
.clip(RoundedCornerShape(15.dp))
.background(color = MaterialTheme.colors.secondary)
.padding(horizontal = 10.dp, vertical = 5.dp)
) {
Text(
text = text,
fontFamily = productSansFont,
color = if (!isSystemInDarkTheme()) Color.White else Color(15, 15, 15, 255)
)
}
}
}

View File

@ -0,0 +1,23 @@
package ch.mathieubroillet.jarvis.android.utils
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun DefaultBox(
content: @Composable() (BoxScope.() -> Unit)
) {
Box(
Modifier
.fillMaxHeight()
.fillMaxWidth()
.padding(horizontal = 15.dp)
.padding(top = 45.dp, bottom = 10.dp)
) {
content()
}
}

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>