Private
Public Access
1
0

Some progress on message list UI

This commit is contained in:
2023-07-07 00:11:13 -07:00
parent 27e3c09228
commit 16148949c8
16 changed files with 283 additions and 48 deletions

2
.gitignore vendored
View File

@@ -13,3 +13,5 @@
.externalNativeBuild
.cxx
local.properties
.idea/

2
.idea/compiler.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
<bytecodeTargetLevel target="17" />
</component>
</project>

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<runningDeviceTargetSelectedWithDropDown>
<Target>
<type value="RUNNING_DEVICE_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="$USER_HOME$/.android/avd/Pixel_3a_API_33_x86_64.avd" />
</Key>
</deviceKey>
</Target>
</runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-06-12T07:15:49.721274Z" />
</component>
</project>

1
.idea/gradle.xml generated
View File

@@ -7,6 +7,7 @@
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="Embedded JDK" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

9
.idea/kotlinScripting.xml generated Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinScriptingSettings">
<scriptDefinition className="org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate" definitionName="KotlinSettingsScript">
<order>2147483647</order>
<autoReloadConfigurations>true</autoReloadConfigurations>
</scriptDefinition>
</component>
</project>

6
.idea/kotlinc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.0" />
</component>
</project>

2
.idea/misc.xml generated
View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@@ -5,12 +5,12 @@ plugins {
android {
namespace 'net.buzzert.kordophonedroid'
compileSdk 32
compileSdk 33
defaultConfig {
applicationId "net.buzzert.kordophonedroid"
minSdk 31
targetSdk 32
targetSdk 33
versionCode 1
versionName "1.0"
@@ -37,7 +37,7 @@ android {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.1.1'
kotlinCompilerExtensionVersion '1.4.1'
}
packagingOptions {
resources {
@@ -47,6 +47,8 @@ android {
}
dependencies {
implementation 'androidx.compose.material3:material3:1.1.1'
// Navigation
def nav_version = "2.5.3"
@@ -74,6 +76,8 @@ dependencies {
implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
implementation 'androidx.compose.material:material:1.1.1'
implementation 'androidx.compose.foundation:foundation:$compose_ui_version'
debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
}

View File

@@ -17,6 +17,8 @@
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:windowSoftInputMode="adjustResize"
android:theme="@style/Theme.KordophoneDroid">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@@ -9,7 +9,8 @@ import androidx.navigation.compose.rememberNavController
import net.buzzert.kordophonedroid.data.AppContainer
import net.buzzert.kordophonedroid.ui.theme.KordophoneTheme
import net.buzzert.kordophonedroid.ui.conversationlist.ConversationListRoute
import net.buzzert.kordophonedroid.ui.conversationlist.ConversationListScreen
import net.buzzert.kordophonedroid.ui.messagelist.MessageListScreen
sealed class Destination(val route: String) {
object ConversationList : Destination("conversations")
@@ -29,14 +30,16 @@ fun KordophoneApp(
startDestination = Destination.ConversationList.route,
) {
composable(route = Destination.ConversationList.route) {
ConversationListRoute(
onNavigateToMessage = { navController.navigate(Destination.MessageList.createRoute(it)) }
)
ConversationListScreen(onMessageSelected = {
navController.navigate(Destination.MessageList.createRoute(it))
})
}
composable(Destination.MessageList.route) {
val conversationID = it.arguments?.getString("id")
Text("Conversation ID: $conversationID")
MessageListScreen(messages = listOf()) {
navController.popBackStack()
}
}
}
}

View File

@@ -1,10 +0,0 @@
package net.buzzert.kordophonedroid.ui.conversationlist
import androidx.compose.runtime.Composable
@Composable
fun ConversationListRoute(
onNavigateToMessage: (conversationID: String) -> Unit
) {
ConversationListScreen(onMessageSelected = onNavigateToMessage)
}

View File

@@ -11,10 +11,12 @@ import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Info
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ComposeCompilerApi
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -33,13 +35,13 @@ fun ConversationListScreen(
Scaffold(
topBar = {
TopAppBar(title = { Text("Conversations") }, actions = {
Button(onClick = { /*TODO*/ }) {
IconButton(onClick = { /*TODO*/ }) {
Icon(Icons.Rounded.Info, contentDescription = "Info")
}
})
}
) {
LazyColumn(state = listState) {
LazyColumn(state = listState, modifier = Modifier.padding(it)) {
items(convos) { conversation ->
ConversationListItem(
name = "James Magahern",
@@ -61,6 +63,7 @@ fun ConversationListItem(
) {
val unreadSize = 12.dp
val horizontalPadding = 8.dp
val verticalPadding = 12.dp
Row(
Modifier
@@ -81,7 +84,7 @@ fun ConversationListItem(
Spacer(Modifier.width(horizontalPadding))
Column {
Spacer(Modifier.height(horizontalPadding))
Spacer(Modifier.height(verticalPadding))
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
@@ -103,7 +106,7 @@ fun ConversationListItem(
Text("This is a test.")
Spacer(Modifier.height(8.dp))
Spacer(Modifier.height(verticalPadding))
Divider()
}
}
@@ -120,3 +123,19 @@ fun UnreadIndicator(size: Dp, modifier: Modifier = Modifier) {
)
)
}
// -
@Preview
@Composable
fun ConversationListItemPreview() {
Column(modifier = Modifier.background(MaterialTheme.colors.background)) {
ConversationListItem(name = "James Magahern", id = "asdf", isUnread = true) {}
}
}
@Preview
@Composable
fun ConversationListScreenPreview() {
ConversationListScreen(onMessageSelected = {})
}

View File

@@ -0,0 +1,212 @@
package net.buzzert.kordophonedroid.ui.messagelist
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Button
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Immutable
data class Message(
val content: String,
)
@Composable
fun MessageListScreen(
messages: List<Message>,
backAction: () -> Unit
) {
val scrollState = rememberLazyListState()
var textState by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue())
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("Messages") },
navigationIcon = {
IconButton(onClick = backAction) {
Icon(Icons.Filled.ArrowBack, null)
}
},
actions = {
})
}
) { paddingValues ->
Column(
Modifier
.fillMaxSize()
.padding(paddingValues)) {
Messages(messages = messages, modifier = Modifier.weight(1f), scrollState = scrollState)
MessageEntry(
onTextChanged = { textState = it },
textFieldValue = textState,
)
}
}
}
@Composable
fun Messages(
messages: List<Message>,
modifier: Modifier = Modifier,
scrollState: LazyListState
) {
val scope = rememberCoroutineScope()
Box(modifier = modifier) {
LazyColumn(
reverseLayout = true,
state = scrollState,
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 16.dp)
) {
for (index in messages.indices) {
val content = messages[index]
item {
MessageBubble(content)
}
}
}
}
}
private val ChatBubbleShape = RoundedCornerShape(4.dp, 20.dp, 20.dp, 20.dp)
@Composable
fun MessageBubble(message: Message) {
val backgroundBubbleColor = MaterialTheme.colors.primary
Column(modifier = Modifier.padding(end = 16.dp)) {
Surface(
color = backgroundBubbleColor,
shape = ChatBubbleShape
) {
Text(
text = message.content,
style = MaterialTheme.typography.body1,
modifier = Modifier
.padding(16.dp)
)
}
Spacer(modifier = Modifier.height(4.dp))
}
}
@Composable
private fun MessageEntry(
keyboardType: KeyboardType = KeyboardType.Text,
onTextChanged: (TextFieldValue) -> Unit,
textFieldValue: TextFieldValue,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(64.dp)
.padding(8.dp)
.background(MaterialTheme.colors.secondary)
) {
Surface {
Row(modifier = Modifier
.padding(8.dp)
.height(64.dp)
.weight(1f)
.align(Alignment.Bottom)
.imePadding()
.navigationBarsPadding()
) {
BasicTextField(
value = textFieldValue,
onValueChange = { onTextChanged(it) },
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.align(Alignment.CenterVertically)
.padding(horizontal = 8.dp),
cursorBrush = SolidColor(MaterialTheme.colors.onBackground),
textStyle = MaterialTheme.typography.body1.copy(MaterialTheme.colors.onBackground),
decorationBox = { textContent ->
if (textFieldValue.text.isEmpty()) {
Text(
text = "Message",
style = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onSurface)
)
}
textContent()
}
)
Button(onClick = { /*TODO*/ }) {
Text(text = "Send")
}
}
}
}
}
// -
@Preview
@Composable
private fun MessageListScreenPreview() {
val messages = listOf<Message>(
Message(content = "Hello"),
Message(content = "Hi there"),
)
MessageListScreen(messages = messages) {}
}

View File

@@ -1,10 +1,12 @@
buildscript {
ext {
compose_ui_version = '1.1.1'
compose_ui_version = '1.4.1'
}
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.4.2' apply false
id 'com.android.library' version '7.4.2' apply false
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
}

View File

@@ -21,3 +21,5 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true
android.nonFinalResIds=false

View File

@@ -1,6 +1,6 @@
#Sun Jun 11 18:08:06 PDT 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME