Start integration with app: adds Hilt for dep injection, implements a view model
This commit is contained in:
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
<bytecodeTargetLevel target="1.8" />
|
||||
</component>
|
||||
</project>
|
||||
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.9.0" />
|
||||
<option name="version" value="1.8.22" />
|
||||
</component>
|
||||
</project>
|
||||
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<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" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
||||
@@ -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}"
|
||||
|
||||
debugImplementation 'androidx.compose.ui:ui-tooling:1.4.3'
|
||||
}
|
||||
|
||||
// Allow references to generated code
|
||||
kapt {
|
||||
correctErrorTypes true
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.KordophoneDroid"
|
||||
android:name=".KordophoneApplication"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
25
app/src/main/java/net/buzzert/kordophonedroid/AppModule.kt
Normal file
25
app/src/main/java/net/buzzert/kordophonedroid/AppModule.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package net.buzzert.kordophonedroid
|
||||
|
||||
import android.app.Application
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import net.buzzert.kordophonedroid.data.AppContainer
|
||||
import net.buzzert.kordophonedroid.data.AppContainerImpl
|
||||
|
||||
@HiltAndroidApp
|
||||
class KordophoneApplication : Application() {
|
||||
lateinit var container: AppContainer
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@ package net.buzzert.kordophonedroid
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import net.buzzert.kordophonedroid.data.AppContainerImpl
|
||||
import net.buzzert.kordophonedroid.ui.KordophoneApp
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
package net.buzzert.kordophonedroid.data
|
||||
|
||||
interface AppContainer {
|
||||
val something: String
|
||||
import androidx.lifecycle.ViewModelStore
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import net.buzzert.kordophone.backend.server.ChatRepository
|
||||
|
||||
interface AppContainer: ViewModelStoreOwner {
|
||||
val repository: ChatRepository
|
||||
}
|
||||
|
||||
class AppContainerImpl() : AppContainer {
|
||||
override val something: String
|
||||
override val repository: ChatRepository
|
||||
get() = TODO("Not yet implemented")
|
||||
|
||||
override val viewModelStore: ViewModelStore
|
||||
get() = TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ fun KordophoneApp(
|
||||
startDestination = Destination.ConversationList.route,
|
||||
) {
|
||||
composable(route = Destination.ConversationList.route) {
|
||||
ConversationListScreen(onMessageSelected = {
|
||||
ConversationListScreen(onConversationSelected = {
|
||||
navController.navigate(Destination.MessageList.createRoute(it))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ 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.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import net.buzzert.kordophone.backend.model.Conversation
|
||||
|
||||
@Composable
|
||||
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()
|
||||
|
||||
@@ -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 = {})
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
6
app/src/main/res/xml/network_security_config.xml
Normal file
6
app/src/main/res/xml/network_security_config.xml
Normal 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>
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Conversation> = mutableListOf()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user