Compare commits
	
		
			No commits in common. "master" and "nextNoteVault-room-integration" have entirely different histories.
		
	
	
		
			master
			...
			nextNoteVa
		
	
		
							
								
								
									
										1
									
								
								.idea/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -6,4 +6,3 @@
 | 
			
		||||
# Datasource local storage ignored files
 | 
			
		||||
/dataSources/
 | 
			
		||||
/dataSources.local.xml
 | 
			
		||||
/AndroidProjectSystem.xml
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
    <selectionStates>
 | 
			
		||||
      <SelectionState runConfigName="app">
 | 
			
		||||
        <option name="selectionMode" value="DROPDOWN" />
 | 
			
		||||
        <DropdownSelection timestamp="2025-05-03T08:34:29.354537334Z">
 | 
			
		||||
        <DropdownSelection timestamp="2025-04-29T17:33:41.575251940Z">
 | 
			
		||||
          <Target type="DEFAULT_BOOT">
 | 
			
		||||
            <handle>
 | 
			
		||||
              <DeviceId pluginId="PhysicalDevice" identifier="serial=R52N50NLGRT" />
 | 
			
		||||
 | 
			
		||||
@ -40,9 +40,6 @@
 | 
			
		||||
    <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
 | 
			
		||||
      <option name="composableFile" value="true" />
 | 
			
		||||
    </inspection_tool>
 | 
			
		||||
    <inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
 | 
			
		||||
      <option name="composableFile" value="true" />
 | 
			
		||||
    </inspection_tool>
 | 
			
		||||
    <inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
 | 
			
		||||
      <option name="composableFile" value="true" />
 | 
			
		||||
    </inspection_tool>
 | 
			
		||||
 | 
			
		||||
@ -61,6 +61,6 @@ dependencies {
 | 
			
		||||
    implementation(libs.androidx.room.ktx)
 | 
			
		||||
    implementation(libs.lifecycle.livedata.ktx)
 | 
			
		||||
    implementation(libs.compose.runtime.livedata)
 | 
			
		||||
    implementation(libs.coil.compose)
 | 
			
		||||
 | 
			
		||||
    ksp(libs.androidx.room.compiler)
 | 
			
		||||
}
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 197 KiB  | 
@ -2,59 +2,29 @@ package come.stormborntales.notevault
 | 
			
		||||
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import android.webkit.MimeTypeMap
 | 
			
		||||
import androidx.activity.ComponentActivity
 | 
			
		||||
import androidx.activity.compose.rememberLauncherForActivityResult
 | 
			
		||||
import androidx.activity.compose.setContent
 | 
			
		||||
import androidx.activity.result.PickVisualMediaRequest
 | 
			
		||||
import androidx.activity.result.contract.ActivityResultContracts
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.material.icons.Icons
 | 
			
		||||
import androidx.compose.material.icons.filled.AccountCircle
 | 
			
		||||
import androidx.compose.material.icons.filled.Menu
 | 
			
		||||
import androidx.compose.material3.DrawerValue
 | 
			
		||||
import androidx.compose.material3.DropdownMenu
 | 
			
		||||
import androidx.compose.material3.DropdownMenuItem
 | 
			
		||||
import androidx.compose.material3.ExperimentalMaterial3Api
 | 
			
		||||
import androidx.compose.material3.Icon
 | 
			
		||||
import androidx.compose.material3.IconButton
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.ModalDrawerSheet
 | 
			
		||||
import androidx.compose.material3.ModalNavigationDrawer
 | 
			
		||||
import androidx.compose.material3.NavigationDrawerItem
 | 
			
		||||
import androidx.compose.material3.NavigationDrawerItemDefaults
 | 
			
		||||
import androidx.compose.material3.Scaffold
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.material3.TextField
 | 
			
		||||
import androidx.compose.material3.TextFieldDefaults
 | 
			
		||||
import androidx.compose.material3.TopAppBar
 | 
			
		||||
import androidx.compose.material3.rememberDrawerState
 | 
			
		||||
import androidx.compose.runtime.*
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
import androidx.compose.ui.platform.LocalContext
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import androidx.lifecycle.viewmodel.compose.viewModel
 | 
			
		||||
import androidx.navigation.compose.NavHost
 | 
			
		||||
import androidx.navigation.compose.composable
 | 
			
		||||
import androidx.navigation.compose.currentBackStackEntryAsState
 | 
			
		||||
import androidx.navigation.compose.rememberNavController
 | 
			
		||||
import come.stormborntales.notevault.data.local.AppDatabase
 | 
			
		||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
 | 
			
		||||
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.screens.NotesScreen
 | 
			
		||||
import come.stormborntales.notevault.ui.screens.SettingsScreen
 | 
			
		||||
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
 | 
			
		||||
import java.io.InputStream
 | 
			
		||||
 | 
			
		||||
class MainActivity : ComponentActivity() {
 | 
			
		||||
    @OptIn(ExperimentalMaterial3Api::class)
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
        val applicationContext = applicationContext
 | 
			
		||||
@ -64,15 +34,17 @@ class MainActivity : ComponentActivity() {
 | 
			
		||||
 | 
			
		||||
        setContent {
 | 
			
		||||
            NoteVaultTheme {
 | 
			
		||||
                val navController = rememberNavController()
 | 
			
		||||
                val viewModel: NoteViewModel = viewModel(factory = viewModelFactory)
 | 
			
		||||
                val context = LocalContext.current
 | 
			
		||||
 | 
			
		||||
                // Globale Notenliste
 | 
			
		||||
                val notes = remember { mutableStateListOf<NoteEntry>() }
 | 
			
		||||
 | 
			
		||||
                // Bildauswahl + Dialog-States
 | 
			
		||||
                var selectedUris by remember { mutableStateOf<List<Uri>>(emptyList()) }
 | 
			
		||||
                var showDialog by remember { mutableStateOf(false) }
 | 
			
		||||
                var noteToEdit by remember { mutableStateOf<NoteEntity?>(null) }
 | 
			
		||||
                val drawerState = rememberDrawerState(DrawerValue.Closed)
 | 
			
		||||
                val scope = rememberCoroutineScope()
 | 
			
		||||
 | 
			
		||||
                // Launcher innerhalb von Compose
 | 
			
		||||
                val imagePickerLauncher = rememberLauncherForActivityResult(
 | 
			
		||||
                    contract = ActivityResultContracts.OpenMultipleDocuments(),
 | 
			
		||||
                    onResult = { uris ->
 | 
			
		||||
@ -83,169 +55,35 @@ class MainActivity : ComponentActivity() {
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                val openDialog: (NoteEntity?) -> Unit = { note ->
 | 
			
		||||
                    Log.d("EditNote", "NoteEntity: ${note?.title}")
 | 
			
		||||
                    noteToEdit = note
 | 
			
		||||
                    showDialog = true
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                val navItems = listOf(
 | 
			
		||||
                    "Home" to "main",
 | 
			
		||||
                    "Notes" to "notes",
 | 
			
		||||
                    "Einstellungen" to "settings"
 | 
			
		||||
                // UI anzeigen
 | 
			
		||||
                MainScreen(
 | 
			
		||||
                    viewModel = viewModel,
 | 
			
		||||
                    onAddNoteClicked = {
 | 
			
		||||
                        imagePickerLauncher.launch(
 | 
			
		||||
                            arrayOf("image/*")
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                ModalNavigationDrawer(
 | 
			
		||||
                    drawerState = drawerState,
 | 
			
		||||
                    drawerContent = {
 | 
			
		||||
                        ModalDrawerSheet {
 | 
			
		||||
                            Text("Navigation", modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleMedium)
 | 
			
		||||
                            navItems.forEach { (label, route) ->
 | 
			
		||||
                                NavigationDrawerItem(
 | 
			
		||||
                                    label = { Text(label) },
 | 
			
		||||
                                    selected = false,
 | 
			
		||||
                                    onClick = {
 | 
			
		||||
                                        navController.navigate(route) {
 | 
			
		||||
                                            popUpTo(navController.graph.startDestinationId) { saveState = true }
 | 
			
		||||
                                            launchSingleTop = true
 | 
			
		||||
                                            restoreState = true
 | 
			
		||||
                                        }
 | 
			
		||||
                                        scope.launch { drawerState.close() }
 | 
			
		||||
                                    },
 | 
			
		||||
                                    modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
 | 
			
		||||
                                )
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ) {
 | 
			
		||||
                    Scaffold(
 | 
			
		||||
                        topBar = {
 | 
			
		||||
                            var searchQuery by remember { mutableStateOf("") }
 | 
			
		||||
                            var isMenuExpanded by remember { mutableStateOf(false) }
 | 
			
		||||
 | 
			
		||||
                            val navBackStackEntry by navController.currentBackStackEntryAsState()
 | 
			
		||||
                            val currentRoute = navBackStackEntry?.destination?.route
 | 
			
		||||
 | 
			
		||||
                            TopAppBar(
 | 
			
		||||
                                title = {
 | 
			
		||||
                                    if(currentRoute == "main") {
 | 
			
		||||
                                        TextField(
 | 
			
		||||
                                            value = searchQuery,
 | 
			
		||||
                                            onValueChange = {
 | 
			
		||||
                                                searchQuery = it
 | 
			
		||||
                                                viewModel.searchQuery.value = it
 | 
			
		||||
                                            },
 | 
			
		||||
                                            placeholder = { Text("Suche...") },
 | 
			
		||||
                                            singleLine = true,
 | 
			
		||||
                                            modifier = Modifier
 | 
			
		||||
                                                .fillMaxWidth()
 | 
			
		||||
                                                .padding(end = 48.dp), // Platz für Avatar
 | 
			
		||||
                                            textStyle = MaterialTheme.typography.bodyLarge,
 | 
			
		||||
                                            colors = TextFieldDefaults.textFieldColors(
 | 
			
		||||
                                                containerColor = Color.Transparent,
 | 
			
		||||
                                                unfocusedIndicatorColor = Color.Transparent,
 | 
			
		||||
                                                focusedIndicatorColor = Color.Transparent
 | 
			
		||||
                                            )
 | 
			
		||||
                                        )
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        Text("NoteVault")
 | 
			
		||||
                                    }
 | 
			
		||||
                                },
 | 
			
		||||
                                navigationIcon = {
 | 
			
		||||
                                    IconButton(onClick = { scope.launch { drawerState.open() } }) {
 | 
			
		||||
                                        Icon(Icons.Default.Menu, contentDescription = "Menü öffnen")
 | 
			
		||||
                                    }
 | 
			
		||||
                                },
 | 
			
		||||
                                actions = {
 | 
			
		||||
                                    Box {
 | 
			
		||||
                                        IconButton(onClick = { isMenuExpanded = true }) {
 | 
			
		||||
                                            Icon(
 | 
			
		||||
                                                imageVector = Icons.Default.AccountCircle, // Avatar-Icon
 | 
			
		||||
                                                contentDescription = "Benutzerprofil"
 | 
			
		||||
                                            )
 | 
			
		||||
                                        }
 | 
			
		||||
                                        DropdownMenu(
 | 
			
		||||
                                            expanded = isMenuExpanded,
 | 
			
		||||
                                            onDismissRequest = { isMenuExpanded = false }
 | 
			
		||||
                                        ) {
 | 
			
		||||
                                            DropdownMenuItem(
 | 
			
		||||
                                                text = { Text("Profil") },
 | 
			
		||||
                                                onClick = {
 | 
			
		||||
                                                    isMenuExpanded = false
 | 
			
		||||
                                                    // TODO: Navigiere ggf. zu Profilscreen
 | 
			
		||||
                                                }
 | 
			
		||||
                                            )
 | 
			
		||||
                                            DropdownMenuItem(
 | 
			
		||||
                                                text = { Text("Abmelden") },
 | 
			
		||||
                                                onClick = {
 | 
			
		||||
                                                    isMenuExpanded = false
 | 
			
		||||
                                                    // TODO: Logout-Logik
 | 
			
		||||
                                                }
 | 
			
		||||
                                            )
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                // Dialog bei Bedarf
 | 
			
		||||
                if (showDialog) {
 | 
			
		||||
                    val context = LocalContext.current;
 | 
			
		||||
                    val scope = rememberCoroutineScope();
 | 
			
		||||
                    AddNoteDialog(
 | 
			
		||||
                        onDismiss = { showDialog = false },
 | 
			
		||||
                        onSave = { title, composer, year, genre, description ->
 | 
			
		||||
                            viewModel.addNote(
 | 
			
		||||
                                context = context,
 | 
			
		||||
                                title = title,
 | 
			
		||||
                                composer = composer,
 | 
			
		||||
                                year = year,
 | 
			
		||||
                                genre = genre,
 | 
			
		||||
                                description = description,
 | 
			
		||||
                                selectedUris = selectedUris,
 | 
			
		||||
                                onDone = { showDialog = false }
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                    ) { innerPadding ->
 | 
			
		||||
                        NavHost(
 | 
			
		||||
                            navController = navController,
 | 
			
		||||
                            startDestination = "main",
 | 
			
		||||
                            modifier = Modifier.padding(innerPadding)
 | 
			
		||||
                        ) {
 | 
			
		||||
                            composable("main") {
 | 
			
		||||
                                MainScreen(
 | 
			
		||||
                                    viewModel = viewModel,
 | 
			
		||||
                                    onAddNoteClicked = {
 | 
			
		||||
                                        imagePickerLauncher.launch(arrayOf("image/*"))
 | 
			
		||||
                                    },
 | 
			
		||||
                                    onEditNote = openDialog
 | 
			
		||||
                                )
 | 
			
		||||
                            }
 | 
			
		||||
                            composable("settings") {
 | 
			
		||||
                                SettingsScreen()
 | 
			
		||||
                            }
 | 
			
		||||
                            composable("notes") {
 | 
			
		||||
                                NotesScreen()
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (showDialog) {
 | 
			
		||||
                            val context = LocalContext.current
 | 
			
		||||
                            AddNoteDialog(
 | 
			
		||||
                                onDismiss = { showDialog = false },
 | 
			
		||||
                                onSave = { title, composer, year, genre, description ->
 | 
			
		||||
                                    if (noteToEdit == null) {
 | 
			
		||||
                                        viewModel.addNote(
 | 
			
		||||
                                            context = context,
 | 
			
		||||
                                            title = title,
 | 
			
		||||
                                            composer = composer,
 | 
			
		||||
                                            year = year,
 | 
			
		||||
                                            genre = genre,
 | 
			
		||||
                                            description = description,
 | 
			
		||||
                                            selectedUris = selectedUris,
 | 
			
		||||
                                            onDone = { showDialog = false }
 | 
			
		||||
                                        )
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        viewModel.updateNote(
 | 
			
		||||
                                            editedNote = noteToEdit!!,
 | 
			
		||||
                                            updatedTitle = title,
 | 
			
		||||
                                            updatedComposer = composer,
 | 
			
		||||
                                            updatedYear = year,
 | 
			
		||||
                                            updatedGenre = genre,
 | 
			
		||||
                                            updatedDescription = description,
 | 
			
		||||
                                            onDone = { showDialog = false }
 | 
			
		||||
                                        )
 | 
			
		||||
                                    }
 | 
			
		||||
                                },
 | 
			
		||||
                                initialTitle = noteToEdit?.title ?: "",
 | 
			
		||||
                                initialComposer = noteToEdit?.composer,
 | 
			
		||||
                                initialYear = noteToEdit?.year,
 | 
			
		||||
                                initialGenre = noteToEdit?.genre,
 | 
			
		||||
                                initialDescription = noteToEdit?.description
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -7,10 +7,9 @@ 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.NoteCollection
 | 
			
		||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
 | 
			
		||||
 | 
			
		||||
@Database(entities = [NoteEntity::class, NoteCollection::class], version = 1)
 | 
			
		||||
@Database(entities = [NoteEntity::class], version = 1)
 | 
			
		||||
@TypeConverters(UriListConverter::class)
 | 
			
		||||
abstract class AppDatabase : RoomDatabase() {
 | 
			
		||||
    abstract fun noteDao(): NoteDao
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@ import androidx.room.Delete
 | 
			
		||||
import androidx.room.Insert
 | 
			
		||||
import androidx.room.OnConflictStrategy
 | 
			
		||||
import androidx.room.Query
 | 
			
		||||
import androidx.room.Update
 | 
			
		||||
import come.stormborntales.notevault.data.local.entity.NoteEntity
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
 | 
			
		||||
@ -19,7 +18,4 @@ interface NoteDao {
 | 
			
		||||
 | 
			
		||||
    @Delete
 | 
			
		||||
    suspend fun delete(note: NoteEntity)
 | 
			
		||||
 | 
			
		||||
    @Update
 | 
			
		||||
    suspend fun update(note: NoteEntity)
 | 
			
		||||
}
 | 
			
		||||
@ -1,11 +0,0 @@
 | 
			
		||||
package come.stormborntales.notevault.data.local.entity
 | 
			
		||||
 | 
			
		||||
import androidx.room.Entity
 | 
			
		||||
import androidx.room.PrimaryKey
 | 
			
		||||
 | 
			
		||||
@Entity("note_collections")
 | 
			
		||||
data class NoteCollection(
 | 
			
		||||
    @PrimaryKey(autoGenerate = true) var id: Int = 0,
 | 
			
		||||
    var name: String,
 | 
			
		||||
    var parentId: Int? = null
 | 
			
		||||
)
 | 
			
		||||
@ -1,24 +1,15 @@
 | 
			
		||||
package come.stormborntales.notevault.data.local.entity
 | 
			
		||||
 | 
			
		||||
import androidx.room.Entity
 | 
			
		||||
import androidx.room.ForeignKey
 | 
			
		||||
import androidx.room.PrimaryKey
 | 
			
		||||
 | 
			
		||||
@Entity(tableName = "notes",
 | 
			
		||||
    foreignKeys = [ForeignKey(
 | 
			
		||||
        entity = NoteCollection::class,
 | 
			
		||||
        parentColumns = ["id"],
 | 
			
		||||
        childColumns = ["collectionId"],
 | 
			
		||||
        onDelete = ForeignKey.CASCADE
 | 
			
		||||
    )])
 | 
			
		||||
@Entity(tableName = "notes")
 | 
			
		||||
data class NoteEntity(
 | 
			
		||||
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
 | 
			
		||||
    var title: String,
 | 
			
		||||
    val title: String,
 | 
			
		||||
    val images: List<String>, // oder String + TypeConverter
 | 
			
		||||
    var composer: String?,
 | 
			
		||||
    var year: Int?,
 | 
			
		||||
    var genre: String?,
 | 
			
		||||
    var description: String?,
 | 
			
		||||
    val imagePreview: String,
 | 
			
		||||
    val collectionId: Int?
 | 
			
		||||
)
 | 
			
		||||
    val composer: String?,
 | 
			
		||||
    val year: Int?,
 | 
			
		||||
    val genre: String?,
 | 
			
		||||
    val description: String?
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,4 @@ class NoteRepository(private val dao: NoteDao) {
 | 
			
		||||
 | 
			
		||||
    suspend fun insert(note: NoteEntity) = dao.insert(note)
 | 
			
		||||
    suspend fun delete(note: NoteEntity) = dao.delete(note)
 | 
			
		||||
 | 
			
		||||
    suspend fun update(note: NoteEntity) = dao.update(note);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,21 +14,15 @@ import androidx.compose.ui.unit.dp
 | 
			
		||||
@Composable
 | 
			
		||||
fun AddNoteDialog(
 | 
			
		||||
    onDismiss: () -> Unit,
 | 
			
		||||
    onSave: (title: String, composer: String?, year: Int?, genre: String?, description: String?) -> Unit,
 | 
			
		||||
    initialTitle: String = "",
 | 
			
		||||
    initialComposer: String? = null,
 | 
			
		||||
    initialYear: Int? = null,
 | 
			
		||||
    initialGenre: String? = null,
 | 
			
		||||
    initialDescription: String? = null
 | 
			
		||||
    onSave: (title: String, composer: String?, year: Int?, genre: String?, description: String?) -> Unit
 | 
			
		||||
) {
 | 
			
		||||
    var title by remember { mutableStateOf(initialTitle) }
 | 
			
		||||
    var composer by remember { mutableStateOf(initialComposer ?: "") }
 | 
			
		||||
    var yearText by remember { mutableStateOf(initialYear?.toString() ?: "") }
 | 
			
		||||
    var genre by remember { mutableStateOf(initialGenre ?: "") }
 | 
			
		||||
    var description by remember { mutableStateOf(initialDescription ?: "") }
 | 
			
		||||
    var title by remember { mutableStateOf("") }
 | 
			
		||||
    var composer by remember { mutableStateOf("") }
 | 
			
		||||
    var yearText by remember { mutableStateOf("") }
 | 
			
		||||
    var genre by remember { mutableStateOf("") }
 | 
			
		||||
    var description by remember { mutableStateOf("") }
 | 
			
		||||
    var showTitleError by remember { mutableStateOf(false) }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    AlertDialog(
 | 
			
		||||
        onDismissRequest = onDismiss,
 | 
			
		||||
        confirmButton = {
 | 
			
		||||
 | 
			
		||||
@ -6,13 +6,10 @@ import android.graphics.BitmapFactory
 | 
			
		||||
import android.graphics.ImageDecoder
 | 
			
		||||
import android.media.Image
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import androidx.compose.foundation.Image
 | 
			
		||||
import androidx.compose.foundation.horizontalScroll
 | 
			
		||||
import androidx.compose.foundation.layout.*
 | 
			
		||||
import androidx.compose.foundation.lazy.LazyColumn
 | 
			
		||||
import androidx.compose.foundation.lazy.items
 | 
			
		||||
import androidx.compose.foundation.rememberScrollState
 | 
			
		||||
import androidx.compose.foundation.shape.RoundedCornerShape
 | 
			
		||||
import androidx.compose.material.icons.Icons
 | 
			
		||||
import androidx.compose.material.icons.filled.Add
 | 
			
		||||
@ -24,7 +21,6 @@ import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.draw.clip
 | 
			
		||||
import androidx.compose.ui.graphics.ImageBitmap
 | 
			
		||||
import androidx.compose.ui.graphics.asImageBitmap
 | 
			
		||||
import androidx.compose.ui.platform.LocalConfiguration
 | 
			
		||||
import androidx.compose.ui.platform.LocalContext
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import come.stormborntales.notevault.FullscreenImageViewerActivity
 | 
			
		||||
@ -33,8 +29,6 @@ import come.stormborntales.notevault.data.model.NoteEntry
 | 
			
		||||
import come.stormborntales.notevault.ui.viewmodel.NoteViewModel
 | 
			
		||||
import java.io.InputStream
 | 
			
		||||
import androidx.core.net.toUri
 | 
			
		||||
import androidx.lifecycle.viewmodel.compose.viewModel
 | 
			
		||||
import coil.compose.AsyncImage
 | 
			
		||||
 | 
			
		||||
fun loadImageBitmap(context: Context, uriString: String): ImageBitmap? {
 | 
			
		||||
    return try {
 | 
			
		||||
@ -46,14 +40,14 @@ fun loadImageBitmap(context: Context, uriString: String): ImageBitmap? {
 | 
			
		||||
        e.printStackTrace()
 | 
			
		||||
        null
 | 
			
		||||
    }
 | 
			
		||||
}@Composable
 | 
			
		||||
fun NoteCard(note: NoteEntity, onDeleteNote: (NoteEntity) -> Unit, onEditNote: (NoteEntity) -> Unit) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun NoteCard(note: NoteEntity) {
 | 
			
		||||
    val context = LocalContext.current
 | 
			
		||||
 | 
			
		||||
    val screenWidth = LocalConfiguration.current.screenWidthDp // Bildschirmbreite in dp
 | 
			
		||||
 | 
			
		||||
    // Dynamische Bildgröße basierend auf der Bildschirmbreite
 | 
			
		||||
    val imageSize = if (screenWidth < 400) 80.dp else 120.dp
 | 
			
		||||
    val imageBitmap = remember(note.images) {
 | 
			
		||||
        note.images.firstOrNull()?.let { loadImageBitmap(context, it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Card(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
@ -62,171 +56,81 @@ fun NoteCard(note: NoteEntity, onDeleteNote: (NoteEntity) -> Unit, onEditNote: (
 | 
			
		||||
        shape = RoundedCornerShape(16.dp),
 | 
			
		||||
        elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
 | 
			
		||||
    ) {
 | 
			
		||||
        // Responsive Layout: Überprüfen, ob der Bildschirm schmaler als 360 dp ist
 | 
			
		||||
        if (screenWidth < 400) {
 | 
			
		||||
            // Wenn der Bildschirm schmal ist, arrangiere die Elemente vertikal
 | 
			
		||||
            Row(modifier = Modifier.padding(16.dp)) {
 | 
			
		||||
                AsyncImage(
 | 
			
		||||
                    model = note.imagePreview,
 | 
			
		||||
        Row(modifier = Modifier.padding(16.dp)) {
 | 
			
		||||
            // Linkes Vorschaubild
 | 
			
		||||
            imageBitmap?.let {
 | 
			
		||||
                Image(
 | 
			
		||||
                    bitmap = it,
 | 
			
		||||
                    contentDescription = "Vorschaubild",
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .size(imageSize)
 | 
			
		||||
                        .size(120.dp)
 | 
			
		||||
                        .clip(RoundedCornerShape(12.dp))
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                Column(modifier = Modifier.padding(16.dp)) {
 | 
			
		||||
                    Text(
 | 
			
		||||
                        text = note.title,
 | 
			
		||||
                        style = MaterialTheme.typography.titleMedium
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                    note.composer?.let {
 | 
			
		||||
                        Text("von $it", style = MaterialTheme.typography.labelMedium)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Spacer(modifier = Modifier.height(4.dp))
 | 
			
		||||
 | 
			
		||||
                    note.year?.let {
 | 
			
		||||
                        Text("Jahr: $it", style = MaterialTheme.typography.bodySmall)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    note.genre?.let {
 | 
			
		||||
                        Text("Genre: $it", style = MaterialTheme.typography.bodySmall)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    note.description?.let {
 | 
			
		||||
                        Text("Beschreibung: $it", style = MaterialTheme.typography.bodySmall, maxLines = 2)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Spacer(modifier = Modifier.height(16.dp)) // Abstand zwischen Text und Buttons
 | 
			
		||||
                }
 | 
			
		||||
                Spacer(modifier = Modifier.width(16.dp))
 | 
			
		||||
            }
 | 
			
		||||
            // Buttons unter dem Text
 | 
			
		||||
            Row(
 | 
			
		||||
                horizontalArrangement = Arrangement.spacedBy(8.dp),
 | 
			
		||||
                modifier = Modifier.fillMaxWidth().padding(16.dp),
 | 
			
		||||
 | 
			
		||||
            // Rechte Info-Spalte
 | 
			
		||||
            Column(
 | 
			
		||||
                modifier = Modifier
 | 
			
		||||
                    .weight(1f)
 | 
			
		||||
                    .align(Alignment.CenterVertically)
 | 
			
		||||
            ) {
 | 
			
		||||
                OutlinedButton(
 | 
			
		||||
                    onClick = {
 | 
			
		||||
                        val uris = ArrayList<Uri>()
 | 
			
		||||
                        note.images.forEach { uris.add(it.toUri()) }
 | 
			
		||||
 | 
			
		||||
                        val intent = Intent(context, FullscreenImageViewerActivity::class.java).apply {
 | 
			
		||||
                            putParcelableArrayListExtra("imageUris", ArrayList(uris))
 | 
			
		||||
                        }
 | 
			
		||||
                        context.startActivity(intent)
 | 
			
		||||
                    },
 | 
			
		||||
                    contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
			
		||||
                ) {
 | 
			
		||||
                    Text("Anzeigen", style = MaterialTheme.typography.labelLarge)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                OutlinedButton(
 | 
			
		||||
                    onClick = {
 | 
			
		||||
                        onEditNote(note)
 | 
			
		||||
                    },
 | 
			
		||||
                    contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
			
		||||
                ) {
 | 
			
		||||
                    Text("Bearbeiten", style = MaterialTheme.typography.labelLarge)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                OutlinedButton(
 | 
			
		||||
                    onClick = {
 | 
			
		||||
                        onDeleteNote(note)
 | 
			
		||||
                    },
 | 
			
		||||
                    colors = ButtonDefaults.outlinedButtonColors(
 | 
			
		||||
                        contentColor = MaterialTheme.colorScheme.error
 | 
			
		||||
                    ),
 | 
			
		||||
                    contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
			
		||||
                ) {
 | 
			
		||||
                    Text("Löschen", style = MaterialTheme.typography.labelLarge)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // Wenn der Bildschirm breiter als 360 dp ist, arrangiere die Elemente nebeneinander
 | 
			
		||||
            Row(modifier = Modifier.padding(16.dp)) {
 | 
			
		||||
                // Linkes Vorschaubild
 | 
			
		||||
                AsyncImage(
 | 
			
		||||
                    model = note.imagePreview,
 | 
			
		||||
                    contentDescription = "Vorschaubild",
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .size(imageSize)
 | 
			
		||||
                        .clip(RoundedCornerShape(12.dp))
 | 
			
		||||
                Text(
 | 
			
		||||
                    text = note.title,
 | 
			
		||||
                    style = MaterialTheme.typography.titleMedium
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                // Rechte Info-Spalte
 | 
			
		||||
                Column(
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .weight(1f) // Damit die Spalte den verfügbaren Platz nutzt
 | 
			
		||||
                        .align(Alignment.CenterVertically)
 | 
			
		||||
                        .padding(start = 16.dp)
 | 
			
		||||
                note.composer?.let {
 | 
			
		||||
                    Text("von $it", style = MaterialTheme.typography.labelMedium)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Spacer(modifier = Modifier.height(4.dp))
 | 
			
		||||
 | 
			
		||||
                note.year?.let {
 | 
			
		||||
                    Text("Jahr: $it", style = MaterialTheme.typography.bodySmall)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                note.genre?.let {
 | 
			
		||||
                    Text("Genre: $it", style = MaterialTheme.typography.bodySmall)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                note.description?.let {
 | 
			
		||||
                    Text("Beschreibung: $it", style = MaterialTheme.typography.bodySmall, maxLines = 2)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Spacer(modifier = Modifier.height(8.dp))
 | 
			
		||||
 | 
			
		||||
                Row(
 | 
			
		||||
                    horizontalArrangement = Arrangement.spacedBy(8.dp)
 | 
			
		||||
                ) {
 | 
			
		||||
                    Text(
 | 
			
		||||
                        text = note.title,
 | 
			
		||||
                        style = MaterialTheme.typography.titleMedium
 | 
			
		||||
                    )
 | 
			
		||||
                    OutlinedButton(
 | 
			
		||||
                        onClick = {
 | 
			
		||||
                            val uris = ArrayList<Uri>();
 | 
			
		||||
                            note.images.forEach { uris.add(it.toUri()) }
 | 
			
		||||
 | 
			
		||||
                    note.composer?.let {
 | 
			
		||||
                        Text("von $it", style = MaterialTheme.typography.labelMedium)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Spacer(modifier = Modifier.height(4.dp))
 | 
			
		||||
 | 
			
		||||
                    note.year?.let {
 | 
			
		||||
                        Text("Jahr: $it", style = MaterialTheme.typography.bodySmall)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    note.genre?.let {
 | 
			
		||||
                        Text("Genre: $it", style = MaterialTheme.typography.bodySmall)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    note.description?.let {
 | 
			
		||||
                        Text("Beschreibung: $it", style = MaterialTheme.typography.bodySmall, maxLines = 2)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Spacer(modifier = Modifier.height(8.dp))
 | 
			
		||||
 | 
			
		||||
                    // Buttons in einer Row anordnen
 | 
			
		||||
                    Row(
 | 
			
		||||
                        horizontalArrangement = Arrangement.spacedBy(8.dp),
 | 
			
		||||
                        modifier = Modifier.fillMaxWidth() // Stellt sicher, dass die Row den gesamten verfügbaren Platz einnimmt
 | 
			
		||||
                            val intent = Intent(context, FullscreenImageViewerActivity::class.java).apply {
 | 
			
		||||
                                putParcelableArrayListExtra("imageUris", ArrayList(uris))
 | 
			
		||||
                            }
 | 
			
		||||
                            context.startActivity(intent)
 | 
			
		||||
                        },
 | 
			
		||||
                        contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
			
		||||
                    ) {
 | 
			
		||||
                        OutlinedButton(
 | 
			
		||||
                            onClick = {
 | 
			
		||||
                                val uris = ArrayList<Uri>()
 | 
			
		||||
                                note.images.forEach { uris.add(it.toUri()) }
 | 
			
		||||
 | 
			
		||||
                                val intent = Intent(context, FullscreenImageViewerActivity::class.java).apply {
 | 
			
		||||
                                    putParcelableArrayListExtra("imageUris", ArrayList(uris))
 | 
			
		||||
                                }
 | 
			
		||||
                                context.startActivity(intent)
 | 
			
		||||
                            },
 | 
			
		||||
                            contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
			
		||||
                        ) {
 | 
			
		||||
                            Text("Anzeigen", style = MaterialTheme.typography.labelLarge)
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        OutlinedButton(
 | 
			
		||||
                            onClick = {
 | 
			
		||||
                                onEditNote(note)
 | 
			
		||||
                            },
 | 
			
		||||
                            contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
			
		||||
                        ) {
 | 
			
		||||
                            Text("Bearbeiten", style = MaterialTheme.typography.labelLarge)
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        OutlinedButton(
 | 
			
		||||
                            onClick = {
 | 
			
		||||
                                onDeleteNote(note)
 | 
			
		||||
                            },
 | 
			
		||||
                            colors = ButtonDefaults.outlinedButtonColors(
 | 
			
		||||
                                contentColor = MaterialTheme.colorScheme.error
 | 
			
		||||
                            ),
 | 
			
		||||
                            contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
			
		||||
                        ) {
 | 
			
		||||
                            Text("Löschen", style = MaterialTheme.typography.labelLarge)
 | 
			
		||||
                        }
 | 
			
		||||
                        Text("Anzeigen", style = MaterialTheme.typography.labelLarge)
 | 
			
		||||
                    }
 | 
			
		||||
                    OutlinedButton(
 | 
			
		||||
                        onClick = { /* TODO */ },
 | 
			
		||||
                        contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
			
		||||
                    ) {
 | 
			
		||||
                        Text("Bearbeiten", style = MaterialTheme.typography.labelLarge)
 | 
			
		||||
                    }
 | 
			
		||||
                    OutlinedButton(
 | 
			
		||||
                        onClick = { /* TODO */ },
 | 
			
		||||
                        colors = ButtonDefaults.outlinedButtonColors(
 | 
			
		||||
                            contentColor = MaterialTheme.colorScheme.error
 | 
			
		||||
                        ),
 | 
			
		||||
                        contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp)
 | 
			
		||||
                    ) {
 | 
			
		||||
                        Text("Löschen", style = MaterialTheme.typography.labelLarge)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -239,11 +143,15 @@ fun NoteCard(note: NoteEntity, onDeleteNote: (NoteEntity) -> Unit, onEditNote: (
 | 
			
		||||
@Composable
 | 
			
		||||
fun MainScreen(
 | 
			
		||||
    onAddNoteClicked: () -> Unit, // Übergib hier deine Logik für den Import
 | 
			
		||||
    viewModel: NoteViewModel,
 | 
			
		||||
    onEditNote: (NoteEntity) -> Unit
 | 
			
		||||
    viewModel: NoteViewModel
 | 
			
		||||
) {
 | 
			
		||||
    val notes by viewModel.filteredNotes.collectAsState(initial = emptyList())
 | 
			
		||||
    val notes by viewModel.notes.observeAsState(emptyList())
 | 
			
		||||
    Scaffold(
 | 
			
		||||
        topBar = {
 | 
			
		||||
            TopAppBar(
 | 
			
		||||
                title = { Text("Meine Noten") }
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        floatingActionButton = {
 | 
			
		||||
            FloatingActionButton(
 | 
			
		||||
                onClick = onAddNoteClicked
 | 
			
		||||
@ -260,9 +168,7 @@ fun MainScreen(
 | 
			
		||||
                .padding(16.dp)
 | 
			
		||||
        ) {
 | 
			
		||||
            items(notes) { note ->
 | 
			
		||||
                NoteCard(note = note, onDeleteNote = { noteEntity ->
 | 
			
		||||
                    viewModel.deleteNote(noteEntity)
 | 
			
		||||
                }, onEditNote = onEditNote)
 | 
			
		||||
                NoteCard(note = note)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,24 +0,0 @@
 | 
			
		||||
package come.stormborntales.notevault.ui.screens
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
import androidx.compose.foundation.layout.Spacer
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
			
		||||
import androidx.compose.foundation.layout.height
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.material3.Divider
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.OutlinedButton
 | 
			
		||||
import androidx.compose.material3.Switch
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun NotesScreen() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,79 +0,0 @@
 | 
			
		||||
package come.stormborntales.notevault.ui.screens
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
import androidx.compose.foundation.layout.Spacer
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
			
		||||
import androidx.compose.foundation.layout.height
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.material3.Divider
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.OutlinedButton
 | 
			
		||||
import androidx.compose.material3.Switch
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun SettingsScreen() {
 | 
			
		||||
    Column(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .fillMaxSize()
 | 
			
		||||
            .padding(16.dp)
 | 
			
		||||
    ) {
 | 
			
		||||
        Text(
 | 
			
		||||
            text = "Einstellungen",
 | 
			
		||||
            style = MaterialTheme.typography.headlineMedium,
 | 
			
		||||
            modifier = Modifier.padding(bottom = 16.dp)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        Divider()
 | 
			
		||||
 | 
			
		||||
        Spacer(modifier = Modifier.height(16.dp))
 | 
			
		||||
 | 
			
		||||
        // Beispielhafte Einstellung
 | 
			
		||||
        Row(
 | 
			
		||||
            verticalAlignment = Alignment.CenterVertically,
 | 
			
		||||
            modifier = Modifier.fillMaxWidth()
 | 
			
		||||
        ) {
 | 
			
		||||
            Text(
 | 
			
		||||
                text = "Dark Mode",
 | 
			
		||||
                modifier = Modifier.weight(1f),
 | 
			
		||||
                style = MaterialTheme.typography.bodyLarge
 | 
			
		||||
            )
 | 
			
		||||
            Switch(
 | 
			
		||||
                checked = false,
 | 
			
		||||
                onCheckedChange = { /* TODO: Dark Mode aktivieren */ }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Spacer(modifier = Modifier.height(16.dp))
 | 
			
		||||
 | 
			
		||||
        Row(
 | 
			
		||||
            verticalAlignment = Alignment.CenterVertically,
 | 
			
		||||
            modifier = Modifier.fillMaxWidth()
 | 
			
		||||
        ) {
 | 
			
		||||
            Text(
 | 
			
		||||
                text = "Benachrichtigungen",
 | 
			
		||||
                modifier = Modifier.weight(1f),
 | 
			
		||||
                style = MaterialTheme.typography.bodyLarge
 | 
			
		||||
            )
 | 
			
		||||
            Switch(
 | 
			
		||||
                checked = true,
 | 
			
		||||
                onCheckedChange = { /* TODO: Benachrichtigungseinstellungen */ }
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Spacer(modifier = Modifier.height(32.dp))
 | 
			
		||||
 | 
			
		||||
        OutlinedButton(
 | 
			
		||||
            onClick = { /* TODO: Impressum anzeigen */ },
 | 
			
		||||
            modifier = Modifier.fillMaxWidth()
 | 
			
		||||
        ) {
 | 
			
		||||
            Text("Impressum")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,8 +1,6 @@
 | 
			
		||||
package come.stormborntales.notevault.ui.viewmodel
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.graphics.Bitmap
 | 
			
		||||
import android.graphics.BitmapFactory
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import android.webkit.MimeTypeMap
 | 
			
		||||
@ -14,52 +12,11 @@ import come.stormborntales.notevault.data.repository.NoteRepository
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import java.io.File
 | 
			
		||||
import androidx.core.graphics.scale
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.MutableStateFlow
 | 
			
		||||
import kotlinx.coroutines.flow.SharingStarted
 | 
			
		||||
import kotlinx.coroutines.flow.StateFlow
 | 
			
		||||
import kotlinx.coroutines.flow.combine
 | 
			
		||||
import kotlinx.coroutines.flow.stateIn
 | 
			
		||||
 | 
			
		||||
class NoteViewModel(
 | 
			
		||||
    private val repository: NoteRepository,
 | 
			
		||||
) : ViewModel() {
 | 
			
		||||
    // Sucheingabe als StateFlow
 | 
			
		||||
    val searchQuery = MutableStateFlow("")
 | 
			
		||||
 | 
			
		||||
    // Alle Notizen als Flow aus der Datenbank (NICHT blockierend!)
 | 
			
		||||
    private val allNotes: Flow<List<NoteEntity>> = repository.allNotes
 | 
			
		||||
 | 
			
		||||
    // Gefilterte Notizen basierend auf Sucheingabe
 | 
			
		||||
    val filteredNotes: StateFlow<List<NoteEntity>> = combine(searchQuery, allNotes) { query, notes ->
 | 
			
		||||
        if (query.isBlank()) {
 | 
			
		||||
            notes
 | 
			
		||||
        } else {
 | 
			
		||||
            notes.filter {
 | 
			
		||||
                it.title.contains(query, ignoreCase = true) ||
 | 
			
		||||
                        it.description?.contains(query, ignoreCase = true) == true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    fun createPreviewImage(context: Context, uri: Uri): File? {
 | 
			
		||||
       return try {
 | 
			
		||||
           val inputStream = context.contentResolver.openInputStream(uri)
 | 
			
		||||
           val originalBitmap = BitmapFactory.decodeStream(inputStream) ?: return null
 | 
			
		||||
 | 
			
		||||
           val previewBitmap = originalBitmap.scale(128, 128, false)
 | 
			
		||||
           val previewFile = File(context.filesDir, "preview_${System.currentTimeMillis()}.jpg")
 | 
			
		||||
           previewFile.outputStream().use { out ->
 | 
			
		||||
               previewBitmap.compress(Bitmap.CompressFormat.JPEG, 75, out)
 | 
			
		||||
           }
 | 
			
		||||
           previewFile
 | 
			
		||||
       } catch (e: Exception) {
 | 
			
		||||
           e.printStackTrace()
 | 
			
		||||
           null
 | 
			
		||||
       }
 | 
			
		||||
    }
 | 
			
		||||
    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) {
 | 
			
		||||
@ -84,17 +41,13 @@ class NoteViewModel(
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (copiedUris.isNotEmpty()) {
 | 
			
		||||
                val preview_image_path = createPreviewImage(context, uri = selectedUris[0])
 | 
			
		||||
 | 
			
		||||
                val note = NoteEntity(
 | 
			
		||||
                    title = title,
 | 
			
		||||
                    images = copiedUris, // muss als List<String> gespeichert sein
 | 
			
		||||
                    composer = composer,
 | 
			
		||||
                    year = year,
 | 
			
		||||
                    genre = genre,
 | 
			
		||||
                    description = description,
 | 
			
		||||
                    imagePreview = preview_image_path.toString(),
 | 
			
		||||
                    collectionId = null
 | 
			
		||||
                    description = description
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                repository.insert(note)
 | 
			
		||||
@ -109,26 +62,4 @@ class NoteViewModel(
 | 
			
		||||
            repository.delete(note)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun updateNote(
 | 
			
		||||
        editedNote: NoteEntity,
 | 
			
		||||
        updatedTitle: String,
 | 
			
		||||
        updatedComposer: String?,
 | 
			
		||||
        updatedYear: Int?,
 | 
			
		||||
        updatedGenre: String?,
 | 
			
		||||
        updatedDescription: String?,
 | 
			
		||||
        onDone: () -> Unit
 | 
			
		||||
    ) {
 | 
			
		||||
        viewModelScope.launch {
 | 
			
		||||
            editedNote.title = updatedTitle
 | 
			
		||||
            editedNote.year = updatedYear;
 | 
			
		||||
            editedNote.composer = updatedComposer
 | 
			
		||||
            editedNote.genre = updatedGenre
 | 
			
		||||
            editedNote.description = updatedDescription
 | 
			
		||||
 | 
			
		||||
            repository.update(editedNote)
 | 
			
		||||
 | 
			
		||||
            onDone()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <background android:drawable="@mipmap/ic_launcher_background"/>
 | 
			
		||||
    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
 | 
			
		||||
    <background android:drawable="@drawable/ic_launcher_background"/>
 | 
			
		||||
    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
 | 
			
		||||
    <monochrome android:drawable="@drawable/ic_launcher_foreground"/>
 | 
			
		||||
</adaptive-icon>
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <background android:drawable="@mipmap/ic_launcher_background"/>
 | 
			
		||||
    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
 | 
			
		||||
    <background android:drawable="@drawable/ic_launcher_background"/>
 | 
			
		||||
    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
 | 
			
		||||
    <monochrome android:drawable="@drawable/ic_launcher_foreground"/>
 | 
			
		||||
</adaptive-icon>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 44 B  | 
| 
		 Before Width: | Height: | Size: 8.6 KiB  | 
| 
		 Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 2.8 KiB  | 
| 
		 Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 982 B  | 
| 
		 Before Width: | Height: | Size: 44 B  | 
| 
		 Before Width: | Height: | Size: 4.6 KiB  | 
| 
		 Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 1.7 KiB  | 
| 
		 Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 1.9 KiB  | 
| 
		 Before Width: | Height: | Size: 46 B  | 
| 
		 Before Width: | Height: | Size: 14 KiB  | 
| 
		 Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 3.8 KiB  | 
| 
		 Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 2.8 KiB  | 
| 
		 Before Width: | Height: | Size: 52 B  | 
| 
		 Before Width: | Height: | Size: 28 KiB  | 
| 
		 Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 5.8 KiB  | 
| 
		 Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 3.8 KiB  | 
| 
		 Before Width: | Height: | Size: 52 B  | 
| 
		 Before Width: | Height: | Size: 50 KiB  | 
| 
		 Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 7.6 KiB  | 
@ -15,7 +15,6 @@ ksp="2.1.21-RC-2.0.0"
 | 
			
		||||
compose = "1.6.0" # oder was immer deine aktuelle Compose-Version ist
 | 
			
		||||
 | 
			
		||||
[libraries]
 | 
			
		||||
coil-compose = { group = "io.coil-kt", name = "coil-compose", version = "2.5.0" } # oder aktueller
 | 
			
		||||
compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "compose" }
 | 
			
		||||
lifecycle-livedata-ktx = "androidx.lifecycle:lifecycle-livedata-ktx:2.8.7"
 | 
			
		||||
androidx-compose-bom-v20240100 = { module = "androidx.compose:compose-bom", version.ref = "composeBomVersion" }
 | 
			
		||||
 | 
			
		||||