Adding Room to Compose View #1

Merged
Azea_Avenbright merged 5 commits from room_shenanigans into master 2023-10-18 00:40:08 -04:00
13 changed files with 253 additions and 86 deletions

1
.idea/gradle.xml generated
View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>

1
.idea/misc.xml generated
View file

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MarkdownSettingsMigration">

View file

@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.google.devtools.ksp'
Azea_Avenbright marked this conversation as resolved

KSP is our main tool for connecting room to compose, but we'll never see it

KSP is our main tool for connecting room to compose, but we'll never see it
}
android {
@ -27,11 +28,11 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '17'
}
buildFeatures {
compose true
@ -48,6 +49,9 @@ android {
dependencies {
def activity_version = '1.8.0'
implementation "androidx.activity:activity-ktx:$activity_version"
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
implementation 'androidx.activity:activity-compose:1.8.0'
@ -66,4 +70,58 @@ dependencies {
implementation 'androidx.room:room-common:2.5.2'
implementation 'androidx.room:room-ktx:2.5.2'
implementation 'androidx.navigation:navigation-runtime-ktx:2.7.4'
def lifecycle_version = "2.6.2"
def arch_version = "2.2.0"
def roomVersion = '2.3.0'
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// ViewModel utilities for Compose
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
// Lifecycle utilities for Compose
implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version"
//Fragment
def fragment_version = "1.6.1"
// Kotlin
implementation "androidx.fragment:fragment-ktx:$fragment_version"
// Saved state module for ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
ksp 'androidx.room:room-compiler:2.5.0'
// Annotation processor
annotationProcessor "androidx.room:room-compiler:$roomVersion"
// alternately - if using Java8, use the following instead of lifecycle-compiler
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
// optional - helpers for implementing LifecycleOwner in a Service
implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"
// optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
// optional - ReactiveStreams support for LiveData
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"
// optional - Test helpers for LiveData
testImplementation "androidx.arch.core:core-testing:$arch_version"
// optional - Test helpers for Lifecycle runtime
testImplementation "androidx.lifecycle:lifecycle-runtime-testing:$lifecycle_version"
//Runtime
implementation "androidx.compose.runtime:runtime:1.5.3"
implementation "androidx.compose.runtime:runtime-livedata:1.5.3"
implementation "androidx.compose.runtime:runtime-rxjava2:1.5.3"
}

View file

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".database.polycule.PolyculeApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"

View file

@ -1,40 +1,46 @@
package com.menagerie.ophelia
import android.app.Activity
import android.app.Application
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import com.menagerie.ophelia.database.polycule.PolyculeDatabase
import com.menagerie.ophelia.database.polycule.PolyculeDatabaseManager
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.menagerie.ophelia.database.polycule.BioListViewModel
import com.menagerie.ophelia.database.polycule.PolyculeApplication
import com.menagerie.ophelia.database.polycule.PolyculeRepository
import com.menagerie.ophelia.database.polycule.entity.Bio
import com.menagerie.ophelia.ui.theme.OpheliaTheme
class MainActivity : ComponentActivity() {
private val db by lazy { PolyculeDatabase.getDatabase(this) }
private val polyculeRepository by lazy {
PolyculeRepository(
bioDao = db.bioDao()
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
PolyculeDatabaseManager.polyculeRepository = polyculeRepository
setContent {
OpheliaTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.error
) {
Greeting("Android")
val repository = (application as PolyculeApplication).repository
Greeting("Android", repository)
}
}
}
@ -42,17 +48,40 @@ class MainActivity : ComponentActivity() {
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
fun Greeting(name: String, repository: PolyculeRepository, modifier: Modifier = Modifier) {
Log.d("Tawni", "Test")
val mBioListViewModel: BioListViewModel = viewModel(
factory = BioListViewModel.BioListViewModelFactory(repository)
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
OpheliaTheme {
Greeting("Android")
val items = mBioListViewModel.allBios.observeAsState(listOf()).value
Column(
modifier = Modifier.padding(16.dp)
) {
Text(text = "FUCKING FINAALLLLLLYY")
Spacer(modifier = Modifier.padding(16.dp))
BioList(list = items, mBioListViewModel = mBioListViewModel)
Spacer(modifier = Modifier.padding(16.dp))
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BioList(
list: List<Bio>,
mBioListViewModel: BioListViewModel
) {
Log.d("Test" ,"${list.size}")
LazyColumn() {
items(list) {bio ->
ListItem(
headlineText = {Text(text = bio.name)}
)
}
}
}

View file

@ -0,0 +1,35 @@
package com.menagerie.ophelia.database.polycule
import android.app.Application
import android.content.Context
import android.view.View
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.menagerie.ophelia.database.polycule.entity.Bio
import kotlinx.coroutines.launch
import java.lang.IllegalArgumentException
class BioListViewModel(private val repository: PolyculeRepository) : ViewModel() {
val allBios: LiveData<List<Bio>> = repository.getAllBios().asLiveData()
fun insert(bio: Bio) = viewModelScope.launch {
repository.upsertBio(bio)
}
class BioListViewModelFactory(
private val repository: PolyculeRepository) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(BioListViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return BioListViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel Class")
}
}
}

View file

@ -1,18 +0,0 @@
package com.menagerie.ophelia.database.polycule
import android.util.LruCache
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
abstract class CachedDao<K, V> (capacity: Int = 100) {
private val cache: LruCache<K, V> = LruCache(capacity)
private val cacheMutex: Mutex = Mutex()
protected suspend fun <R> withDaoCache(cacheBlock: suspend LruCache<K, V>.()->R): R {
return cacheMutex.withLock {
cacheBlock(cache)
}
}
}

View file

@ -0,0 +1,15 @@
package com.menagerie.ophelia.database.polycule
import android.app.Application
import android.util.Log
import com.menagerie.ophelia.database.polycule.entity.Bio
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
class PolyculeApplication : Application() {
private val applicationScope = CoroutineScope(SupervisorJob())
val database by lazy { PolyculeDatabase.getDatabase(this, applicationScope)}
val repository by lazy { PolyculeRepository(database.bioDao())}
}

View file

@ -1,11 +1,16 @@
package com.menagerie.ophelia.database.polycule
import android.content.Context
import android.util.Log
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import com.menagerie.ophelia.database.polycule.entity.Bio
import com.menagerie.ophelia.database.polycule.entity.BioDao
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.count
import kotlinx.coroutines.launch
@Database(
@ -23,19 +28,69 @@ abstract class PolyculeDatabase : RoomDatabase() {
@Volatile
private var INSTANCE: PolyculeDatabase? = null
fun getDatabase(context: Context): PolyculeDatabase {
fun getDatabase(
context: Context,
scope: CoroutineScope,
): PolyculeDatabase {
return INSTANCE ?: synchronized(this)
{
Log.d("huh?", "FUCK")
val instance = Room.databaseBuilder(
context.applicationContext,
PolyculeDatabase::class.java,
PolyculeDatabase::class.java,
"polycule.db"
)
.fallbackToDestructiveMigration()
.addCallback(PolyculeDatabaseCallback(scope))
.build()
INSTANCE = instance
instance
}
}
}
private class PolyculeDatabaseCallback(
private val scope: CoroutineScope
) : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
Log.d("WWW" , "FUCK")
INSTANCE?.let { database ->
scope.launch {
Log.d("WWWWWWWWW", "FUCK")
populateDatabase(database.bioDao())
}
}
}
suspend fun populateDatabase(bioDao: BioDao) {
Log.d("TAWNI", "AAAAA")
var bio = Bio(
name = "Fuck")
bioDao.insert(bio)
bio = Bio(
name = "Off")
bioDao.insert(bio)
bio = Bio(
name = "Android!")
bioDao.insert(bio)
bio = Bio(
name = "(Now")
bioDao.insert(bio)
bio = Bio(
name = "In")
bioDao.insert(bio)
bio = Bio(
name = "Compose!)")
bioDao.insert(bio)
val count = bioDao.getAllAlphabetisedBios().count()
Log.d("Count", "$count")
}
}
}

View file

@ -1,7 +1,10 @@
package com.menagerie.ophelia.database.polycule
import androidx.lifecycle.LiveData
import com.menagerie.ophelia.database.polycule.entity.Bio
import com.menagerie.ophelia.database.polycule.entity.BioDao
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.count
class PolyculeRepository(
@ -9,7 +12,20 @@ class PolyculeRepository(
) {
suspend fun upsertBio(bio: Bio): Long {
return bioDao.findOrInsert(bio)
return 0
//return bioDao.findOrInsert(bio)
}
fun getAlphabetisedBios(): Flow<List<Bio>> {
return bioDao.getAllAlphabetisedBios()
}
fun getAllBios(): Flow<List<Bio>> {
return bioDao.getAllBios()
}
suspend fun insert(bio : Bio) {
bioDao.insert(bio)
}
}

View file

@ -1,19 +1,14 @@
package com.menagerie.ophelia.database.polycule.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(
foreignKeys = [
ForeignKey(
entity = Identity::class,
parentColumns = ["id"],
childColumns = ["uniqueId"]
)
]
tableName = "bio_table",
)
data class Bio(
@PrimaryKey(autoGenerate = true) val id: Long,
/*FK*/ val uniqueId: Long
@PrimaryKey
@ColumnInfo(name = "name") val name: String,
)

View file

@ -3,43 +3,23 @@ package com.menagerie.ophelia.database.polycule.entity
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import com.menagerie.ophelia.database.polycule.CachedDao
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.count
@Dao
abstract class BioDao : CachedDao<Long, Bio>() {
abstract class BioDao {
@Insert
abstract suspend fun insert(newBio: Bio): Long
abstract suspend fun insert(newBio: Bio)
@Update
abstract suspend fun update(existingBio: Bio)
abstract suspend fun update(existingBio: Bio) : Int
@Query("SELECT * FROM Bio WHERE uniqueId = :uniqueId")
abstract suspend fun findByUniqueId(uniqueId: Long): Bio?
@Query("SELECT * FROM bio_table ORDER BY name ASC")
abstract fun getAllAlphabetisedBios(): Flow<List<Bio>>
@Transaction
open suspend fun findOrInsert(bio: Bio): Long {
return withDaoCache {
val key = bio.uniqueId
val returnVal: Bio =
get(key)
@Query("SELECT * FROM bio_table")
abstract fun getAllBios(): Flow<List<Bio>>
?: findByUniqueId(key)
?: run {
val newId: Long = insert(bio)
bio.copy(id = newId)
}
if (bio != returnVal) {
if (returnVal.id == 0L) {
insert(bio)
} else {
update(bio)
}
}
put(key, returnVal)
returnVal.id
}
}
}

View file

@ -2,4 +2,5 @@
plugins {
id 'com.android.application' version '8.1.2' apply false
id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
id 'com.google.devtools.ksp' version '1.8.10-1.0.9' apply false
}