diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b589d56..61a9130 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index fdf8d99..9a55c2d 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 773fe0f..e7b1341 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,7 @@ + - + diff --git a/app/build.gradle b/app/build.gradle index f0c7a9b..27780a9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,8 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'kotlin-kapt' + id 'com.google.dagger.hilt.android' } android { @@ -31,24 +33,31 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = JavaVersion.VERSION_1_8.toString() } buildFeatures { compose true } composeOptions { - kotlinCompilerExtensionVersion '1.5.0' + // Note: this is strictly tied to a kotlin version, but isn't the version of kotlin exactly. + // See: https://developer.android.com/jetpack/androidx/releases/compose-kotlin + kotlinCompilerExtensionVersion '1.4.8' } packagingOptions { resources { excludes += '/META-INF/{AL2.0,LGPL2.1}' } } + buildToolsVersion '33.0.1' +} + +kotlin { + jvmToolchain(8) } dependencies { - implementation 'androidx.compose.material3:material3:1.1.1' - implementation 'androidx.core:core-ktx:1.10.1' + implementation "androidx.compose.material3:material3:1.1.1" + implementation "androidx.core:core-ktx:${kotlin_version}" // Kordophone lib implementation project(':backend') @@ -73,14 +82,23 @@ dependencies { // Jetpack Compose Integration implementation "androidx.navigation:navigation-compose:$nav_version" - implementation 'androidx.core:core-ktx:1.10.1' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' implementation 'androidx.activity:activity-compose:1.4.3' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1' implementation "androidx.compose.ui:ui:1.4.3" implementation 'androidx.compose.material:material:1.4.3' implementation "androidx.compose.foundation:foundation:1.4.3" - debugImplementation 'androidx.compose.ui:ui-tooling:1.4.3' + // Hilt (dependency injection) + implementation "com.google.dagger:hilt-android:${hilt_version}" + implementation "androidx.hilt:hilt-navigation-compose:1.0.0" + kapt "com.google.dagger:hilt-compiler:${hilt_version}" -} \ No newline at end of file + debugImplementation 'androidx.compose.ui:ui-tooling:1.4.3' +} + +// Allow references to generated code +kapt { + correctErrorTypes true +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5ad0368..b7a30fc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ android:supportsRtl="true" android:theme="@style/Theme.KordophoneDroid" android:name=".KordophoneApplication" + android:networkSecurityConfig="@xml/network_security_config" tools:targetApi="31"> Unit + viewModel: ConversationListViewModel = hiltViewModel(), + onConversationSelected: (conversationID: String) -> Unit +) { + val conversations by viewModel.conversations.collectAsState(initial = listOf()) + ConversationListView(conversations = conversations, onConversationSelected = onConversationSelected) +} + +@Composable +fun ConversationListView( + conversations: List, + onConversationSelected: (conversationID: String) -> Unit ) { - val convos = ArrayList() - for (i in 0..200) { - convos += "Test" - } val listState = rememberLazyListState() @@ -42,12 +51,12 @@ fun ConversationListScreen( } ) { LazyColumn(state = listState, modifier = Modifier.padding(it)) { - items(convos) { conversation -> + items(conversations) { conversation -> ConversationListItem( - name = "James Magahern", - id = "asdf", - isUnread = false, - onClick = { onMessageSelected("asdf") } + name = conversation.formattedDisplayName(), + id = conversation.guid, + isUnread = conversation.unreadCount > 0, + onClick = { onConversationSelected(conversation.guid) } ) } } @@ -137,5 +146,5 @@ fun ConversationListItemPreview() { @Preview @Composable fun ConversationListScreenPreview() { - ConversationListScreen(onMessageSelected = {}) + ConversationListScreen(onConversationSelected = {}) } \ No newline at end of file diff --git a/app/src/main/java/net/buzzert/kordophonedroid/ui/conversationlist/ConversationListViewModel.kt b/app/src/main/java/net/buzzert/kordophonedroid/ui/conversationlist/ConversationListViewModel.kt new file mode 100644 index 0000000..f79276a --- /dev/null +++ b/app/src/main/java/net/buzzert/kordophonedroid/ui/conversationlist/ConversationListViewModel.kt @@ -0,0 +1,24 @@ +package net.buzzert.kordophonedroid.ui.conversationlist + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch +import net.buzzert.kordophone.backend.model.Conversation +import net.buzzert.kordophone.backend.server.ChatRepository +import javax.inject.Inject + +@HiltViewModel +class ConversationListViewModel @Inject constructor( + private val repository: ChatRepository +) : ViewModel() { + val conversations: Flow> + get() = repository.conversationChanges + + init { + viewModelScope.launch { + repository.synchronize() + } + } +} \ No newline at end of file diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..5289581 --- /dev/null +++ b/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,6 @@ + + + + 192.168.1.123 + + \ No newline at end of file diff --git a/backend/build.gradle b/backend/build.gradle index b66ebea..853d470 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -27,7 +27,7 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = JavaVersion.VERSION_1_8.toString() } } @@ -48,7 +48,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.9.0' // Realm - implementation 'io.realm.kotlin:library-base:1.10.0' + implementation "io.realm.kotlin:library-base:${realm_version}" // https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.7.3', ext: 'pom' diff --git a/backend/src/main/java/net/buzzert/kordophone/backend/model/Conversation.kt b/backend/src/main/java/net/buzzert/kordophone/backend/model/Conversation.kt index d03b0b6..dbad24b 100644 --- a/backend/src/main/java/net/buzzert/kordophone/backend/model/Conversation.kt +++ b/backend/src/main/java/net/buzzert/kordophone/backend/model/Conversation.kt @@ -27,6 +27,11 @@ data class Conversation( @SerializedName("lastMessage") var lastMessage: Message?, ) { + + fun formattedDisplayName(): String { + return displayName ?: participants.joinToString(", ") + } + override fun equals(other: Any?): Boolean { if (other == null || javaClass != other.javaClass) return false @@ -39,4 +44,15 @@ data class Conversation( unreadCount == o.unreadCount ) } + + override fun hashCode(): Int { + var result = guid.hashCode() + result = 31 * result + date.hashCode() + result = 31 * result + participants.hashCode() + result = 31 * result + (displayName?.hashCode() ?: 0) + result = 31 * result + unreadCount + result = 31 * result + (lastMessagePreview?.hashCode() ?: 0) + result = 31 * result + (lastMessage?.hashCode() ?: 0) + return result + } } diff --git a/backend/src/test/java/net/buzzert/kordophone/backend/MockServer.kt b/backend/src/test/java/net/buzzert/kordophone/backend/MockServer.kt index 8f1c012..a803a15 100644 --- a/backend/src/test/java/net/buzzert/kordophone/backend/MockServer.kt +++ b/backend/src/test/java/net/buzzert/kordophone/backend/MockServer.kt @@ -30,6 +30,7 @@ import retrofit2.Response import java.util.Date import java.util.UUID +@OptIn(ExperimentalStdlibApi::class) class MockServer { val version = "Kordophone-2.0" val conversations: MutableList = mutableListOf() diff --git a/build.gradle b/build.gradle index 524cf5e..5d66039 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,8 @@ buildscript { ext { + kotlin_version = '1.8.22' + realm_version = '1.10.0' + hilt_version = '2.44' } } @@ -7,6 +10,7 @@ buildscript { plugins { id 'com.android.application' version '8.0.2' apply false id 'com.android.library' version '8.0.2' apply false - id 'org.jetbrains.kotlin.android' version '1.9.0' apply false - id 'io.realm.kotlin' version '1.10.0' apply false + id 'org.jetbrains.kotlin.android' version "${kotlin_version}" apply false + id 'io.realm.kotlin' version "${realm_version}" apply false + id 'com.google.dagger.hilt.android' version "${hilt_version}" apply false } \ No newline at end of file