From 7c8ad4aee491b0478b1347c8228ef3e54be69bc8 Mon Sep 17 00:00:00 2001 From: IanRDavies Date: Wed, 16 Feb 2022 18:00:59 +0000 Subject: [PATCH] Android compose navigation (#316) * initial rough ideas * refactor and put in high level navigation * refactor Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> --- apps/android/.idea/codeStyles/Project.xml | 117 ++++++++++++++++++ .../.idea/codeStyles/codeStyleConfig.xml | 5 + .../.idea/deploymentTargetDropDown.xml | 2 +- apps/android/.idea/misc.xml | 2 + apps/android/app/build.gradle | 1 + .../java/chat/simplex/app/MainActivity.kt | 29 +++-- .../main/java/chat/simplex/app/MainPage.kt | 10 ++ .../chat/simplex/app/views/TerminalView.kt | 51 ++++++-- .../chat/simplex/app/views/WelcomeView.kt | 48 +++++++ .../app/views/usersettings/SettingsView.kt | 61 +++++++++ .../app/src/main/res/drawable/logo.png | Bin 0 -> 19731 bytes 11 files changed, 306 insertions(+), 20 deletions(-) create mode 100644 apps/android/.idea/codeStyles/Project.xml create mode 100644 apps/android/.idea/codeStyles/codeStyleConfig.xml create mode 100644 apps/android/app/src/main/java/chat/simplex/app/MainPage.kt create mode 100644 apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt create mode 100644 apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt create mode 100644 apps/android/app/src/main/res/drawable/logo.png diff --git a/apps/android/.idea/codeStyles/Project.xml b/apps/android/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..4bec4ea8ae --- /dev/null +++ b/apps/android/.idea/codeStyles/Project.xml @@ -0,0 +1,117 @@ + + + + + + \ No newline at end of file diff --git a/apps/android/.idea/codeStyles/codeStyleConfig.xml b/apps/android/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000000..a55e7a179b --- /dev/null +++ b/apps/android/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/apps/android/.idea/deploymentTargetDropDown.xml b/apps/android/.idea/deploymentTargetDropDown.xml index 230c77f89f..57b651efa0 100644 --- a/apps/android/.idea/deploymentTargetDropDown.xml +++ b/apps/android/.idea/deploymentTargetDropDown.xml @@ -12,6 +12,6 @@ - + \ No newline at end of file diff --git a/apps/android/.idea/misc.xml b/apps/android/.idea/misc.xml index 1c2eb53046..f0d8957869 100644 --- a/apps/android/.idea/misc.xml +++ b/apps/android/.idea/misc.xml @@ -7,6 +7,8 @@ + + diff --git a/apps/android/app/build.gradle b/apps/android/app/build.gradle index 0f2c5d0297..082294a40e 100644 --- a/apps/android/app/build.gradle +++ b/apps/android/app/build.gradle @@ -73,4 +73,5 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" + implementation "androidx.navigation:navigation-compose:2.4.1" } diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt index 2a62c745fb..dfd150c0d2 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt @@ -8,11 +8,9 @@ import androidx.activity.viewModels import androidx.compose.runtime.Composable import chat.simplex.app.ui.theme.SimpleXTheme import androidx.lifecycle.AndroidViewModel -import chat.simplex.app.model.* -import chat.simplex.app.views.TerminalView -import kotlinx.serialization.* -import kotlinx.serialization.json.* -import kotlinx.serialization.modules.* +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController class MainActivity: ComponentActivity() { @@ -21,7 +19,7 @@ class MainActivity: ComponentActivity() { super.onCreate(savedInstanceState) setContent { SimpleXTheme { - MainPage(viewModel) + Navigation(viewModel = viewModel) } } } @@ -32,6 +30,21 @@ class SimplexViewModel(application: Application) : AndroidViewModel(application) } @Composable -fun MainPage(vm: SimplexViewModel) { - TerminalView(vm.chatModel) +fun Navigation(viewModel: SimplexViewModel) { + val navController = rememberNavController() + + NavHost(navController=navController, startDestination=Pages.Home.route){ + composable(route=Pages.Home.route){ + MainPage(vm = viewModel) + } +// composable(route=Pages.Welcome.route){ +// WelcomeView(vm.) +// } + } +} + +sealed class Pages(val route: String) { + object Home : Pages("home") + object Terminal : Pages("terminal") + object Welcome : Pages("welcome") } diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainPage.kt b/apps/android/app/src/main/java/chat/simplex/app/MainPage.kt new file mode 100644 index 0000000000..6a596e81e8 --- /dev/null +++ b/apps/android/app/src/main/java/chat/simplex/app/MainPage.kt @@ -0,0 +1,10 @@ +package chat.simplex.app + +import androidx.compose.runtime.Composable +import chat.simplex.app.views.TerminalPage + +@Composable +fun MainPage(vm: SimplexViewModel) { + + TerminalPage(vm.chatModel) +} \ No newline at end of file diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt index 05f990dcf7..a129d75b96 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt @@ -3,20 +3,41 @@ package chat.simplex.app.views import androidx.compose.foundation.layout.Column import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.text.ClickableText import androidx.compose.material.Text +import androidx.compose.material.Button import androidx.compose.runtime.Composable +import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview -import chat.simplex.app.model.CC -import chat.simplex.app.model.ChatModel -import chat.simplex.app.model.TerminalItem +import androidx.navigation.* +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import chat.simplex.app.model.* import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.SendMsgView @Composable -fun TerminalView(chatModel: ChatModel) { +fun TerminalPage(chatModel: ChatModel) { + val navController = rememberNavController() + NavHost(navController = navController, startDestination = "terminalView"){ + composable("terminalView") { TerminalView(chatModel, navController) } + composable( + "details" + "/{_details}", + arguments=listOf( + navArgument("_details"){ + type = NavType.StringType + } + ) + ) { entry -> DetailView( entry.arguments?.getString("_details"), navController)} + } +} + +@Composable +fun TerminalView(chatModel: ChatModel, navController: NavController) { Column { - TerminalLog(chatModel.terminalItems) + TerminalLog(chatModel.terminalItems, navController) SendMsgView(sendMessage = { cmd -> chatModel.controller.sendCmd(CC.Console(cmd)) }) @@ -24,18 +45,26 @@ fun TerminalView(chatModel: ChatModel) { } @Composable -fun TerminalLog(terminalItems: List) { +fun TerminalLog(terminalItems: List, navController: NavController) { LazyColumn { items(terminalItems) { item -> - Text(item.label) + ClickableText( + AnnotatedString(item.label), + onClick={navController.navigate("details/${item.details}")} + ) } } } -@Preview @Composable -fun PreviewSendMsgView() { - SimpleXTheme { - TerminalView(chatModel = ChatModel.sampleData) +fun DetailView(details: String?, navController: NavController){ + Column { + Text("$details") + + Button( + onClick={navController.popBackStack()} + ) + {Text("Back")} } } + diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt new file mode 100644 index 0000000000..e0ed85466f --- /dev/null +++ b/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt @@ -0,0 +1,48 @@ +package chat.simplex.app.views + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.Image +import androidx.compose.ui.res.painterResource +import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import chat.simplex.app.R +import androidx.compose.material.Text +import androidx.compose.material.TextField +import androidx.compose.material.Button +import androidx.compose.ui.unit.dp +import androidx.compose.ui.Modifier +import androidx.navigation.NavController + +@Composable +fun WelcomeView(createUser: (data: String) -> String, navController: NavController) { + Column { + Image( + painter=painterResource(R.drawable.logo), contentDescription = "Simplex Logo", + ) + Text("You control your chat!") + Text("The messaging and application platform protecting your privacy and security.") + Spacer(Modifier.height(8.dp)) + Text("We don't store any of your contacts or messages (once delivered) on the servers.") + Spacer(Modifier.height(24.dp)) + CreateProfilePanel(createUser, navController) + } +} + +@Composable +fun CreateProfilePanel(createUser: (data: String) -> String, navController: NavController) { + var displayName by remember { mutableStateOf("") } + var fullName by remember { mutableStateOf("") } + + Column { + Text("Create profile") + Text("Your profile is stored on your device and shared only with your contacts.") + Text("Display Name") + TextField(value = displayName, onValueChange = { displayName = it }, modifier = Modifier.height(30.dp)) + Text("Full Name (Optional)") + TextField(value = fullName, onValueChange = { fullName = it }, modifier = Modifier.height(30.dp)) + Button(onClick={ + createUser("{\"displayName\": $displayName, \"fullName\": $fullName}") + navController.navigate("home") { popUpTo("home") { inclusive = true }} + }) { Text("Create")} + } +} \ No newline at end of file diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt new file mode 100644 index 0000000000..35c54c5f69 --- /dev/null +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt @@ -0,0 +1,61 @@ +package chat.simplex.app.views.usersettings + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import chat.simplex.app.model.Profile + +//@Preview(showBackground = true) +@Composable +fun SettingsView(profile: Profile) { + Column() { + Text("Your Settings") + Spacer(Modifier.height(4.dp)) + Text("YOU", style= MaterialTheme.typography.h4) + Button( + onClick = { println(profile.displayName) } + ) { + Text(profile.displayName) + } + Button( + onClick = { println(profile.hashCode()) } + ) { + Text("Your SimpleX contact address", style= MaterialTheme.typography.body1) + } + Spacer(Modifier.height(10.dp)) + Text("HELP", style= MaterialTheme.typography.h4) + Button( + onClick = { println("navigate to help") } + ) { + Text("How to use SimpleX Chat") + } + Button( + onClick = { println("start help chat") } + ) { + Text("Get help & advice via chat") + } + Button( + onClick = { println("navigate to email") } + ) { + Text("Ask questions via email") + } + Spacer(Modifier.height(10.dp)) + Text("DEVELOP", style= MaterialTheme.typography.h4) + Button( + onClick = { println("navigate to console") } + ) { + Text("Chat console") + } + Button( + onClick = { println("navigate to github") } + ) { + Text("Install SimpleX for terminal") + } + } +} + diff --git a/apps/android/app/src/main/res/drawable/logo.png b/apps/android/app/src/main/res/drawable/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7f25ea17d0894bc6192f012c9b16a697fcb9fc7a GIT binary patch literal 19731 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