Private
Public Access
1
0

Start integration with app: adds Hilt for dep injection, implements a view model

This commit is contained in:
2023-08-17 00:37:11 -07:00
parent 28f2bfe580
commit 4eef98aef4
17 changed files with 149 additions and 32 deletions

2
.idea/compiler.xml generated
View File

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

2
.idea/kotlinc.xml generated
View File

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

3
.idea/misc.xml generated
View File

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

View File

@@ -1,6 +1,8 @@
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'com.google.dagger.hilt.android'
} }
android { android {
@@ -31,24 +33,31 @@ android {
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = JavaVersion.VERSION_1_8.toString()
} }
buildFeatures { buildFeatures {
compose true compose true
} }
composeOptions { 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 { packagingOptions {
resources { resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}' excludes += '/META-INF/{AL2.0,LGPL2.1}'
} }
} }
buildToolsVersion '33.0.1'
}
kotlin {
jvmToolchain(8)
} }
dependencies { dependencies {
implementation 'androidx.compose.material3:material3:1.1.1' implementation "androidx.compose.material3:material3:1.1.1"
implementation 'androidx.core:core-ktx:1.10.1' implementation "androidx.core:core-ktx:${kotlin_version}"
// Kordophone lib // Kordophone lib
implementation project(':backend') implementation project(':backend')
@@ -73,14 +82,23 @@ dependencies {
// Jetpack Compose Integration // Jetpack Compose Integration
implementation "androidx.navigation:navigation-compose:$nav_version" 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.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.ui:ui:1.4.3"
implementation 'androidx.compose.material:material:1.4.3' implementation 'androidx.compose.material:material:1.4.3'
implementation "androidx.compose.foundation:foundation: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}"
} debugImplementation 'androidx.compose.ui:ui-tooling:1.4.3'
}
// Allow references to generated code
kapt {
correctErrorTypes true
}

View File

@@ -12,6 +12,7 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.KordophoneDroid" android:theme="@style/Theme.KordophoneDroid"
android:name=".KordophoneApplication" android:name=".KordophoneApplication"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="31"> tools:targetApi="31">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"

View File

@@ -0,0 +1,25 @@
package net.buzzert.kordophonedroid
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import net.buzzert.kordophone.backend.db.CachedChatDatabase
import net.buzzert.kordophone.backend.server.ChatRepository
import net.buzzert.kordophone.backend.server.RetrofitAPIClient
import java.net.URL
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Singleton
@Provides
fun provideChatRepository(): ChatRepository {
val host = "http://192.168.1.123:5738"
val client = RetrofitAPIClient(URL(host))
val apiInterface = client.getAPIInterface()
val database = CachedChatDatabase.liveDatabase()
return ChatRepository(apiInterface, database)
}
}

View File

@@ -1,9 +1,11 @@
package net.buzzert.kordophonedroid package net.buzzert.kordophonedroid
import android.app.Application import android.app.Application
import dagger.hilt.android.HiltAndroidApp
import net.buzzert.kordophonedroid.data.AppContainer import net.buzzert.kordophonedroid.data.AppContainer
import net.buzzert.kordophonedroid.data.AppContainerImpl import net.buzzert.kordophonedroid.data.AppContainerImpl
@HiltAndroidApp
class KordophoneApplication : Application() { class KordophoneApplication : Application() {
lateinit var container: AppContainer lateinit var container: AppContainer

View File

@@ -3,9 +3,11 @@ package net.buzzert.kordophonedroid
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import dagger.hilt.android.AndroidEntryPoint
import net.buzzert.kordophonedroid.data.AppContainerImpl import net.buzzert.kordophonedroid.data.AppContainerImpl
import net.buzzert.kordophonedroid.ui.KordophoneApp import net.buzzert.kordophonedroid.ui.KordophoneApp
@AndroidEntryPoint
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@@ -1,10 +1,18 @@
package net.buzzert.kordophonedroid.data package net.buzzert.kordophonedroid.data
interface AppContainer { import androidx.lifecycle.ViewModelStore
val something: String import androidx.lifecycle.ViewModelStoreOwner
import net.buzzert.kordophone.backend.server.ChatRepository
interface AppContainer: ViewModelStoreOwner {
val repository: ChatRepository
} }
class AppContainerImpl() : AppContainer { class AppContainerImpl() : AppContainer {
override val something: String override val repository: ChatRepository
get() = TODO("Not yet implemented") get() = TODO("Not yet implemented")
}
override val viewModelStore: ViewModelStore
get() = TODO("Not yet implemented")
}

View File

@@ -30,7 +30,7 @@ fun KordophoneApp(
startDestination = Destination.ConversationList.route, startDestination = Destination.ConversationList.route,
) { ) {
composable(route = Destination.ConversationList.route) { composable(route = Destination.ConversationList.route) {
ConversationListScreen(onMessageSelected = { ConversationListScreen(onConversationSelected = {
navController.navigate(Destination.MessageList.createRoute(it)) navController.navigate(Destination.MessageList.createRoute(it))
}) })
} }

View File

@@ -11,7 +11,8 @@ import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Info import androidx.compose.material.icons.rounded.Info
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.ComposeCompilerApi import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
@@ -20,15 +21,23 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import net.buzzert.kordophone.backend.model.Conversation
@Composable @Composable
fun ConversationListScreen( fun ConversationListScreen(
onMessageSelected: (conversationID: String) -> 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<Conversation>,
onConversationSelected: (conversationID: String) -> Unit
) { ) {
val convos = ArrayList<String>()
for (i in 0..200) {
convos += "Test"
}
val listState = rememberLazyListState() val listState = rememberLazyListState()
@@ -42,12 +51,12 @@ fun ConversationListScreen(
} }
) { ) {
LazyColumn(state = listState, modifier = Modifier.padding(it)) { LazyColumn(state = listState, modifier = Modifier.padding(it)) {
items(convos) { conversation -> items(conversations) { conversation ->
ConversationListItem( ConversationListItem(
name = "James Magahern", name = conversation.formattedDisplayName(),
id = "asdf", id = conversation.guid,
isUnread = false, isUnread = conversation.unreadCount > 0,
onClick = { onMessageSelected("asdf") } onClick = { onConversationSelected(conversation.guid) }
) )
} }
} }
@@ -137,5 +146,5 @@ fun ConversationListItemPreview() {
@Preview @Preview
@Composable @Composable
fun ConversationListScreenPreview() { fun ConversationListScreenPreview() {
ConversationListScreen(onMessageSelected = {}) ConversationListScreen(onConversationSelected = {})
} }

View File

@@ -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<List<Conversation>>
get() = repository.conversationChanges
init {
viewModelScope.launch {
repository.synchronize()
}
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">192.168.1.123</domain>
</domain-config>
</network-security-config>

View File

@@ -27,7 +27,7 @@ android {
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = JavaVersion.VERSION_1_8.toString()
} }
} }
@@ -48,7 +48,7 @@ dependencies {
implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.google.code.gson:gson:2.9.0'
// Realm // 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 // 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' implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.7.3', ext: 'pom'

View File

@@ -27,6 +27,11 @@ data class Conversation(
@SerializedName("lastMessage") @SerializedName("lastMessage")
var lastMessage: Message?, var lastMessage: Message?,
) { ) {
fun formattedDisplayName(): String {
return displayName ?: participants.joinToString(", ")
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other == null || javaClass != other.javaClass) return false if (other == null || javaClass != other.javaClass) return false
@@ -39,4 +44,15 @@ data class Conversation(
unreadCount == o.unreadCount 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
}
} }

View File

@@ -30,6 +30,7 @@ import retrofit2.Response
import java.util.Date import java.util.Date
import java.util.UUID import java.util.UUID
@OptIn(ExperimentalStdlibApi::class)
class MockServer { class MockServer {
val version = "Kordophone-2.0" val version = "Kordophone-2.0"
val conversations: MutableList<Conversation> = mutableListOf() val conversations: MutableList<Conversation> = mutableListOf()

View File

@@ -1,5 +1,8 @@
buildscript { buildscript {
ext { ext {
kotlin_version = '1.8.22'
realm_version = '1.10.0'
hilt_version = '2.44'
} }
} }
@@ -7,6 +10,7 @@ buildscript {
plugins { plugins {
id 'com.android.application' version '8.0.2' apply false id 'com.android.application' version '8.0.2' apply false
id 'com.android.library' 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 'org.jetbrains.kotlin.android' version "${kotlin_version}" apply false
id 'io.realm.kotlin' version '1.10.0' apply false id 'io.realm.kotlin' version "${realm_version}" apply false
id 'com.google.dagger.hilt.android' version "${hilt_version}" apply false
} }