WIP: Persist NoteEntities
This commit is contained in:
		
							parent
							
								
									646e2a5f1a
								
							
						
					
					
						commit
						ba0cbcef09
					
				@ -4,7 +4,7 @@
 | 
			
		||||
    <selectionStates>
 | 
			
		||||
      <SelectionState runConfigName="app">
 | 
			
		||||
        <option name="selectionMode" value="DROPDOWN" />
 | 
			
		||||
        <DropdownSelection timestamp="2025-04-29T15:21:34.999046322Z">
 | 
			
		||||
        <DropdownSelection timestamp="2025-04-29T16:02:42.921287190Z">
 | 
			
		||||
          <Target type="DEFAULT_BOOT">
 | 
			
		||||
            <handle>
 | 
			
		||||
              <DeviceId pluginId="PhysicalDevice" identifier="serial=R52N50NLGRT" />
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ plugins {
 | 
			
		||||
    alias(libs.plugins.android.application)
 | 
			
		||||
    alias(libs.plugins.kotlin.android)
 | 
			
		||||
    alias(libs.plugins.kotlin.compose)
 | 
			
		||||
    alias(libs.plugins.ksp) // NEU
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
@ -56,4 +57,9 @@ dependencies {
 | 
			
		||||
    debugImplementation(libs.androidx.ui.tooling)
 | 
			
		||||
    debugImplementation(libs.androidx.ui.test.manifest)
 | 
			
		||||
    implementation(libs.androidx.foundation)
 | 
			
		||||
    implementation(libs.androidx.room.runtime)
 | 
			
		||||
    implementation(libs.androidx.room.ktx)
 | 
			
		||||
    implementation(libs.lifecycle.livedata.ktx)
 | 
			
		||||
 | 
			
		||||
    ksp(libs.androidx.room.compiler)
 | 
			
		||||
}
 | 
			
		||||
@ -10,10 +10,15 @@ import androidx.activity.result.PickVisualMediaRequest
 | 
			
		||||
import androidx.activity.result.contract.ActivityResultContracts
 | 
			
		||||
import androidx.compose.runtime.*
 | 
			
		||||
import androidx.compose.ui.platform.LocalContext
 | 
			
		||||
import androidx.lifecycle.viewmodel.compose.viewModel
 | 
			
		||||
import come.stormborntales.notevault.data.local.AppDatabase
 | 
			
		||||
import come.stormborntales.notevault.data.model.NoteEntry
 | 
			
		||||
import come.stormborntales.notevault.data.repository.NoteRepository
 | 
			
		||||
import come.stormborntales.notevault.ui.screens.AddNoteDialog
 | 
			
		||||
import come.stormborntales.notevault.ui.screens.MainScreen
 | 
			
		||||
import come.stormborntales.notevault.ui.theme.NoteVaultTheme
 | 
			
		||||
import come.stormborntales.notevault.ui.viewmodel.NoteViewModel
 | 
			
		||||
import come.stormborntales.notevault.ui.viewmodel.NoteViewModelFactory
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import java.io.File
 | 
			
		||||
@ -22,9 +27,14 @@ import java.io.InputStream
 | 
			
		||||
class MainActivity : ComponentActivity() {
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
        val applicationContext = applicationContext
 | 
			
		||||
        val database = AppDatabase.getDatabase(applicationContext)
 | 
			
		||||
        val repository = NoteRepository(database.noteDao())
 | 
			
		||||
        val viewModelFactory = NoteViewModelFactory(repository)
 | 
			
		||||
 | 
			
		||||
        setContent {
 | 
			
		||||
            NoteVaultTheme {
 | 
			
		||||
                val viewModel: NoteViewModel = viewModel(factory = viewModelFactory)
 | 
			
		||||
                val context = LocalContext.current
 | 
			
		||||
 | 
			
		||||
                // Globale Notenliste
 | 
			
		||||
@ -62,43 +72,16 @@ class MainActivity : ComponentActivity() {
 | 
			
		||||
                    AddNoteDialog(
 | 
			
		||||
                        onDismiss = { showDialog = false },
 | 
			
		||||
                        onSave = { title, composer, year, genre, description ->
 | 
			
		||||
                            scope.launch(Dispatchers.IO) {
 | 
			
		||||
                                val copiedUris = selectedUris.mapNotNull { uri ->
 | 
			
		||||
                                    try {
 | 
			
		||||
                                        val inputStream: InputStream? = context.contentResolver.openInputStream(uri)
 | 
			
		||||
                                        val extension = MimeTypeMap.getSingleton()
 | 
			
		||||
                                            .getExtensionFromMimeType(context.contentResolver.getType(uri)) ?: "jpg" // Fallback
 | 
			
		||||
 | 
			
		||||
                                        val outputFile = File(context.filesDir, "note_${System.currentTimeMillis()}.$extension")
 | 
			
		||||
                                        inputStream?.use { input ->
 | 
			
		||||
                                            outputFile.outputStream().use { output ->
 | 
			
		||||
                                                input.copyTo(output)
 | 
			
		||||
                                            }
 | 
			
		||||
                                        }
 | 
			
		||||
                                        Uri.fromFile(outputFile)
 | 
			
		||||
                                    } catch (e: Exception) {
 | 
			
		||||
                                        e.printStackTrace()
 | 
			
		||||
                                        null
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                if (copiedUris.isNotEmpty()) {
 | 
			
		||||
                                    notes.add(
 | 
			
		||||
                                        NoteEntry(
 | 
			
		||||
                                            title = title,
 | 
			
		||||
                                            images = copiedUris,
 | 
			
		||||
                                            composer = composer,
 | 
			
		||||
                                            year = year,
 | 
			
		||||
                                            genre = genre,
 | 
			
		||||
                                            description = description
 | 
			
		||||
                                        )
 | 
			
		||||
                                    )
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                showDialog = false
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                            viewModel.addNote(
 | 
			
		||||
                                context = context,
 | 
			
		||||
                                title = title,
 | 
			
		||||
                                composer = composer,
 | 
			
		||||
                                year = year,
 | 
			
		||||
                                genre = genre,
 | 
			
		||||
                                description = description,
 | 
			
		||||
                                selectedUris = selectedUris,
 | 
			
		||||
                                onDone = { showDialog = false }
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,32 @@
 | 
			
		||||
package come.stormborntales.notevault.data.local
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import androidx.room.Database
 | 
			
		||||
import androidx.room.Room
 | 
			
		||||
import androidx.room.RoomDatabase
 | 
			
		||||
import androidx.room.TypeConverters
 | 
			
		||||
import come.stormborntales.notevault.data.local.dao.NoteDao
 | 
			
		||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
 | 
			
		||||
 | 
			
		||||
@Database(entities = [NoteEntity::class], version = 1)
 | 
			
		||||
@TypeConverters(UriListConverter::class)
 | 
			
		||||
abstract class AppDatabase : RoomDatabase() {
 | 
			
		||||
    abstract fun noteDao(): NoteDao
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        @Volatile
 | 
			
		||||
        private var INSTANCE: AppDatabase? = null
 | 
			
		||||
 | 
			
		||||
        fun getDatabase(context: Context): AppDatabase {
 | 
			
		||||
            return INSTANCE ?: synchronized(this) {
 | 
			
		||||
                val instance = Room.databaseBuilder(
 | 
			
		||||
                    context.applicationContext,
 | 
			
		||||
                    AppDatabase::class.java,
 | 
			
		||||
                    "note_database"
 | 
			
		||||
                ).build()
 | 
			
		||||
                INSTANCE = instance
 | 
			
		||||
                instance
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,11 @@
 | 
			
		||||
package come.stormborntales.notevault.data.local
 | 
			
		||||
 | 
			
		||||
import androidx.room.TypeConverter
 | 
			
		||||
 | 
			
		||||
class UriListConverter {
 | 
			
		||||
    @TypeConverter
 | 
			
		||||
    fun fromList(list: List<String>): String = list.joinToString(",")
 | 
			
		||||
 | 
			
		||||
    @TypeConverter
 | 
			
		||||
    fun toList(data: String): List<String> = if (data.isBlank()) emptyList() else data.split(",")
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,21 @@
 | 
			
		||||
package come.stormborntales.notevault.data.local.dao
 | 
			
		||||
 | 
			
		||||
import androidx.room.Dao
 | 
			
		||||
import androidx.room.Delete
 | 
			
		||||
import androidx.room.Insert
 | 
			
		||||
import androidx.room.OnConflictStrategy
 | 
			
		||||
import androidx.room.Query
 | 
			
		||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
 | 
			
		||||
@Dao
 | 
			
		||||
interface NoteDao {
 | 
			
		||||
    @Query("SELECT * FROM notes")
 | 
			
		||||
    fun getAll(): Flow<List<NoteEntity>>
 | 
			
		||||
 | 
			
		||||
    @Insert(onConflict = OnConflictStrategy.REPLACE)
 | 
			
		||||
    suspend fun insert(note: NoteEntity)
 | 
			
		||||
 | 
			
		||||
    @Delete
 | 
			
		||||
    suspend fun delete(note: NoteEntity)
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,15 @@
 | 
			
		||||
package come.stormborntales.notevault.data.local.entity
 | 
			
		||||
 | 
			
		||||
import androidx.room.Entity
 | 
			
		||||
import androidx.room.PrimaryKey
 | 
			
		||||
 | 
			
		||||
@Entity(tableName = "notes")
 | 
			
		||||
data class NoteEntity(
 | 
			
		||||
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
 | 
			
		||||
    val title: String,
 | 
			
		||||
    val images: List<String>, // oder String + TypeConverter
 | 
			
		||||
    val composer: String?,
 | 
			
		||||
    val year: Int?,
 | 
			
		||||
    val genre: String?,
 | 
			
		||||
    val description: String?
 | 
			
		||||
)
 | 
			
		||||
@ -0,0 +1,12 @@
 | 
			
		||||
package come.stormborntales.notevault.data.repository
 | 
			
		||||
 | 
			
		||||
import come.stormborntales.notevault.data.local.dao.NoteDao
 | 
			
		||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
 | 
			
		||||
class NoteRepository(private val dao: NoteDao) {
 | 
			
		||||
    val allNotes: Flow<List<NoteEntity>> = dao.getAll()
 | 
			
		||||
 | 
			
		||||
    suspend fun insert(note: NoteEntity) = dao.insert(note)
 | 
			
		||||
    suspend fun delete(note: NoteEntity) = dao.delete(note)
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,62 @@
 | 
			
		||||
package come.stormborntales.notevault.ui.viewmodel
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.webkit.MimeTypeMap
 | 
			
		||||
import androidx.lifecycle.ViewModel
 | 
			
		||||
import androidx.lifecycle.asLiveData
 | 
			
		||||
import androidx.lifecycle.viewModelScope
 | 
			
		||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
 | 
			
		||||
import come.stormborntales.notevault.data.repository.NoteRepository
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
class NoteViewModel(
 | 
			
		||||
    private val repository: NoteRepository,
 | 
			
		||||
) : ViewModel() {
 | 
			
		||||
    val notes = repository.allNotes.asLiveData()
 | 
			
		||||
 | 
			
		||||
    fun addNote(context: Context, title: String, composer: String?, year: Int?, genre: String?, description: String?, selectedUris: List<Uri>, onDone:() -> Unit) {
 | 
			
		||||
        viewModelScope.launch(Dispatchers.IO) {
 | 
			
		||||
            val copiedUris = selectedUris.mapNotNull { uri ->
 | 
			
		||||
                try {
 | 
			
		||||
                    val inputStream = context.contentResolver.openInputStream(uri)
 | 
			
		||||
                    val extension = MimeTypeMap.getSingleton()
 | 
			
		||||
                        .getExtensionFromMimeType(context.contentResolver.getType(uri)) ?: "jpg"
 | 
			
		||||
 | 
			
		||||
                    val outputFile = File(context.filesDir, "note_${System.currentTimeMillis()}.$extension")
 | 
			
		||||
                    inputStream?.use { input ->
 | 
			
		||||
                        outputFile.outputStream().use { output ->
 | 
			
		||||
                            input.copyTo(output)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    Uri.fromFile(outputFile).toString() // speichern als String
 | 
			
		||||
                } catch (e: Exception) {
 | 
			
		||||
                    e.printStackTrace()
 | 
			
		||||
                    null
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (copiedUris.isNotEmpty()) {
 | 
			
		||||
                val note = NoteEntity(
 | 
			
		||||
                    title = title,
 | 
			
		||||
                    images = copiedUris, // muss als List<String> gespeichert sein
 | 
			
		||||
                    composer = composer,
 | 
			
		||||
                    year = year,
 | 
			
		||||
                    genre = genre,
 | 
			
		||||
                    description = description
 | 
			
		||||
                )
 | 
			
		||||
                repository.insert(note)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            onDone()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun deleteNote(note: NoteEntity) {
 | 
			
		||||
        viewModelScope.launch {
 | 
			
		||||
            repository.delete(note)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,17 @@
 | 
			
		||||
package come.stormborntales.notevault.ui.viewmodel
 | 
			
		||||
 | 
			
		||||
import androidx.lifecycle.ViewModel
 | 
			
		||||
import androidx.lifecycle.ViewModelProvider
 | 
			
		||||
import come.stormborntales.notevault.data.repository.NoteRepository
 | 
			
		||||
 | 
			
		||||
class NoteViewModelFactory(
 | 
			
		||||
    private val repository: NoteRepository
 | 
			
		||||
) : ViewModelProvider.Factory {
 | 
			
		||||
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
 | 
			
		||||
        if (modelClass.isAssignableFrom(NoteViewModel::class.java)) {
 | 
			
		||||
            @Suppress("UNCHECKED_CAST")
 | 
			
		||||
            return NoteViewModel(repository) as T
 | 
			
		||||
        }
 | 
			
		||||
        throw IllegalArgumentException("Unknown ViewModel class")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -10,13 +10,17 @@ lifecycleRuntimeKtx = "2.8.7"
 | 
			
		||||
activityCompose = "1.8.0"
 | 
			
		||||
composeBom = "2024.04.01"
 | 
			
		||||
navigationCompose = "2.8.9"
 | 
			
		||||
room = "2.7.1"
 | 
			
		||||
ksp="2.1.21-RC-2.0.0"
 | 
			
		||||
 | 
			
		||||
[libraries]
 | 
			
		||||
lifecycle-livedata-ktx = "androidx.lifecycle:lifecycle-livedata-ktx:2.8.7"
 | 
			
		||||
androidx-compose-bom-v20240100 = { module = "androidx.compose:compose-bom", version.ref = "composeBomVersion" }
 | 
			
		||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
 | 
			
		||||
androidx-foundation = { module = "androidx.compose.foundation:foundation" }
 | 
			
		||||
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
 | 
			
		||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
 | 
			
		||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
 | 
			
		||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
 | 
			
		||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
 | 
			
		||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
 | 
			
		||||
@ -30,9 +34,12 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
 | 
			
		||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
 | 
			
		||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
 | 
			
		||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
 | 
			
		||||
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
 | 
			
		||||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[plugins]
 | 
			
		||||
android-application = { id = "com.android.application", version.ref = "agp" }
 | 
			
		||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
 | 
			
		||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
 | 
			
		||||
 | 
			
		||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user