nextNoteVault #23
@ -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(
|
||||
viewModel.addNote(
|
||||
context = context,
|
||||
title = title,
|
||||
images = copiedUris,
|
||||
composer = composer,
|
||||
year = year,
|
||||
genre = genre,
|
||||
description = description
|
||||
description = description,
|
||||
selectedUris = selectedUris,
|
||||
onDone = { showDialog = false }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
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