- Camerafunktionalität dokumentiert
- Aufräumarbeiten
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.example.snapandsolve
|
package com.example.snapandsolve
|
||||||
|
|
||||||
|
|
||||||
import MainScreen
|
import MainScreen
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
package com.example.snapandsolve.camera
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.graphics.ImageDecoder
|
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
|
||||||
import androidx.core.content.FileProvider
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.example.snapandsolve.BuildConfig
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.io.File
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This variant inherits from [AndroidViewModel] and has access to the application context
|
|
||||||
*/
|
|
||||||
class AlbumAndroidViewModel(private val appContext: Application,
|
|
||||||
private val coroutineContext: CoroutineContext
|
|
||||||
): AndroidViewModel(appContext) {
|
|
||||||
//region View State
|
|
||||||
private val _albumViewState: MutableStateFlow<AlbumViewState> = MutableStateFlow(
|
|
||||||
AlbumViewState()
|
|
||||||
)
|
|
||||||
val viewStateFlow: StateFlow<AlbumViewState>
|
|
||||||
get() = _albumViewState
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
fun onEvent(intent: Intent) = viewModelScope.launch(coroutineContext) {
|
|
||||||
when(intent) {
|
|
||||||
is Intent.OnPermissionGranted -> {
|
|
||||||
// Create an empty image file in the app's cache directory
|
|
||||||
val file = File.createTempFile(
|
|
||||||
"temp_image_file_", /* prefix */
|
|
||||||
".jpg", /* suffix */
|
|
||||||
appContext.cacheDir /* cache directory */
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create sandboxed url for this temp file - needed for the camera API
|
|
||||||
val uri = FileProvider.getUriForFile(appContext,
|
|
||||||
"${BuildConfig.APPLICATION_ID}.provider",
|
|
||||||
file
|
|
||||||
)
|
|
||||||
_albumViewState.value = _albumViewState.value.copy(tempFileUrl = uri)
|
|
||||||
}
|
|
||||||
|
|
||||||
is Intent.OnPermissionDenied -> {
|
|
||||||
// maybe log the permission denial event
|
|
||||||
println("User did not grant permission to use the camera")
|
|
||||||
}
|
|
||||||
|
|
||||||
is Intent.OnFinishPickingImages -> {
|
|
||||||
if (intent.imageUrls.isNotEmpty()) {
|
|
||||||
// Handle picked images
|
|
||||||
val newImages = mutableListOf<ImageBitmap>()
|
|
||||||
for (eachImageUrl in intent.imageUrls) {
|
|
||||||
val inputStream = appContext.contentResolver.openInputStream(eachImageUrl)
|
|
||||||
val bytes = inputStream?.readBytes()
|
|
||||||
inputStream?.close()
|
|
||||||
|
|
||||||
if (bytes != null) {
|
|
||||||
val bitmapOptions = BitmapFactory.Options()
|
|
||||||
bitmapOptions.inMutable = true
|
|
||||||
val bitmap: Bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size, bitmapOptions)
|
|
||||||
val imageBitmap = bitmap.asImageBitmap()
|
|
||||||
newImages.add(imageBitmap)
|
|
||||||
} else {
|
|
||||||
// error reading the bytes from the image url
|
|
||||||
println("The image that was picked could not be read from the device at this url: $eachImageUrl")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentViewState = _albumViewState.value
|
|
||||||
val newCopy = currentViewState.copy(
|
|
||||||
selectedPictures = (currentViewState.selectedPictures + newImages),
|
|
||||||
tempFileUrl = null
|
|
||||||
)
|
|
||||||
_albumViewState.value = newCopy
|
|
||||||
} else {
|
|
||||||
// user did not pick anything
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is Intent.OnImageSaved -> {
|
|
||||||
val tempImageUrl = _albumViewState.value.tempFileUrl
|
|
||||||
if (tempImageUrl != null) {
|
|
||||||
val source: ImageDecoder.Source = ImageDecoder.createSource(appContext.contentResolver, tempImageUrl)
|
|
||||||
|
|
||||||
val currentPictures = _albumViewState.value.selectedPictures.toMutableList()
|
|
||||||
currentPictures.add(ImageDecoder.decodeBitmap(source).asImageBitmap())
|
|
||||||
|
|
||||||
_albumViewState.value = _albumViewState.value.copy(tempFileUrl = null,
|
|
||||||
selectedPictures = currentPictures)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is Intent.OnImageSavingCanceled -> {
|
|
||||||
_albumViewState.value = _albumViewState.value.copy(tempFileUrl = null)
|
|
||||||
}
|
|
||||||
|
|
||||||
is Intent.OnFinishPickingImagesWith -> {
|
|
||||||
// unnecessary in this viewmodel variant
|
|
||||||
}
|
|
||||||
|
|
||||||
is Intent.OnPermissionGrantedWith -> {
|
|
||||||
// unnecessary in this viewmodel variant
|
|
||||||
}
|
|
||||||
|
|
||||||
is Intent.OnImageSavedWith -> {
|
|
||||||
// unnecessary in this viewmodel variant
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +1,39 @@
|
|||||||
package com.example.snapandsolve.camera
|
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
/*
|
|
||||||
Das sind die Funktionen beim Drücken der Knöpfe. Übersichtlicher wäre es sie direkt mit den
|
|
||||||
Knöpfen in der ViewModel zu platzieren. AlbumEvents als name ist vielleicht unglücklich gewählt.
|
|
||||||
*/
|
|
||||||
/**
|
/**
|
||||||
* User generated events that can be triggered from the UI.
|
* Benutzer-Aktionen für das Album/Kamera-System.
|
||||||
*/
|
*/
|
||||||
sealed class Intent {
|
sealed class Intent {
|
||||||
data object OnPermissionGranted: Intent()
|
/**
|
||||||
|
* Kamera-Permission wurde erteilt.
|
||||||
|
* @param compositionContext Android Context für Dateizugriff
|
||||||
|
*/
|
||||||
data class OnPermissionGrantedWith(val compositionContext: Context): Intent()
|
data class OnPermissionGrantedWith(val compositionContext: Context): Intent()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kamera-Permission wurde verweigert.
|
||||||
|
*/
|
||||||
data object OnPermissionDenied: Intent()
|
data object OnPermissionDenied: Intent()
|
||||||
|
|
||||||
data object OnImageSaved: Intent()
|
/**
|
||||||
|
* Kamera-Aufnahme wurde gespeichert.
|
||||||
|
* @param compositionContext Android Context für Dateizugriff
|
||||||
|
*/
|
||||||
data class OnImageSavedWith(val compositionContext: Context): Intent()
|
data class OnImageSavedWith(val compositionContext: Context): Intent()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kamera-Aufnahme wurde abgebrochen.
|
||||||
|
*/
|
||||||
data object OnImageSavingCanceled: Intent()
|
data object OnImageSavingCanceled: Intent()
|
||||||
|
|
||||||
data class OnFinishPickingImages(val imageUrls: List<Uri>): Intent()
|
/**
|
||||||
|
* Bilder aus Galerie wurden ausgewählt.
|
||||||
data class OnFinishPickingImagesWith(val compositionContext: Context, val imageUrls: List<Uri>): Intent()
|
* @param compositionContext Android Context für Dateizugriff
|
||||||
|
* @param imageUrls Liste der ausgewählten Bild-URIs
|
||||||
|
*/
|
||||||
|
data class OnFinishPickingImagesWith(
|
||||||
|
val compositionContext: Context,
|
||||||
|
val imageUrls: List<Uri>
|
||||||
|
): Intent()
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.example.snapandsolve.camera
|
package com.example.snapandsolve.camera
|
||||||
|
|
||||||
|
import Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.ImageDecoder
|
import android.graphics.ImageDecoder
|
||||||
@@ -10,25 +10,33 @@ import androidx.core.content.FileProvider
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.example.snapandsolve.BuildConfig
|
import com.example.snapandsolve.BuildConfig
|
||||||
|
import com.example.snapandsolve.camera.AlbumViewState
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class AlbumViewModel(private val coroutineContext: CoroutineContext
|
/**
|
||||||
): ViewModel() {
|
* ViewModel für Bild-Verwaltung (Kamera + Galerie).
|
||||||
|
* Verwaltet ausgewählte Bilder und temporäre Kamera-Dateien.
|
||||||
|
*/
|
||||||
|
class AlbumViewModel(private val coroutineContext: CoroutineContext) : ViewModel() {
|
||||||
|
|
||||||
//region View State
|
|
||||||
private val _albumViewState: MutableStateFlow<AlbumViewState> = MutableStateFlow(
|
private val _albumViewState: MutableStateFlow<AlbumViewState> = MutableStateFlow(
|
||||||
AlbumViewState()
|
AlbumViewState()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable State für UI-Komponenten.
|
||||||
|
*/
|
||||||
val viewStateFlow: StateFlow<AlbumViewState>
|
val viewStateFlow: StateFlow<AlbumViewState>
|
||||||
get() = _albumViewState
|
get() = _albumViewState
|
||||||
//endregion
|
|
||||||
|
|
||||||
// region Intents
|
/**
|
||||||
|
* Verarbeitet Benutzer-Aktionen.
|
||||||
|
* @param intent Die zu verarbeitende Aktion
|
||||||
|
*/
|
||||||
fun onReceive(intent: Intent) = viewModelScope.launch(coroutineContext) {
|
fun onReceive(intent: Intent) = viewModelScope.launch(coroutineContext) {
|
||||||
when (intent) {
|
when (intent) {
|
||||||
is Intent.OnPermissionGrantedWith -> {
|
is Intent.OnPermissionGrantedWith -> {
|
||||||
@@ -40,7 +48,8 @@ class AlbumViewModel(private val coroutineContext: CoroutineContext
|
|||||||
)
|
)
|
||||||
println("DEBUG: TempFile erstellt: ${tempFile.absolutePath}")
|
println("DEBUG: TempFile erstellt: ${tempFile.absolutePath}")
|
||||||
|
|
||||||
val uri = FileProvider.getUriForFile(intent.compositionContext,
|
val uri = FileProvider.getUriForFile(
|
||||||
|
intent.compositionContext,
|
||||||
"${BuildConfig.APPLICATION_ID}.provider",
|
"${BuildConfig.APPLICATION_ID}.provider",
|
||||||
tempFile
|
tempFile
|
||||||
)
|
)
|
||||||
@@ -50,13 +59,11 @@ class AlbumViewModel(private val coroutineContext: CoroutineContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
is Intent.OnPermissionDenied -> {
|
is Intent.OnPermissionDenied -> {
|
||||||
// maybe log the permission denial event
|
|
||||||
println("User did not grant permission to use the camera")
|
println("User did not grant permission to use the camera")
|
||||||
}
|
}
|
||||||
|
|
||||||
is Intent.OnFinishPickingImagesWith -> {
|
is Intent.OnFinishPickingImagesWith -> {
|
||||||
if (intent.imageUrls.isNotEmpty()) {
|
if (intent.imageUrls.isNotEmpty()) {
|
||||||
// Handle picked images
|
|
||||||
val newImages = mutableListOf<ImageBitmap>()
|
val newImages = mutableListOf<ImageBitmap>()
|
||||||
for (eachImageUrl in intent.imageUrls) {
|
for (eachImageUrl in intent.imageUrls) {
|
||||||
val inputStream = intent.compositionContext.contentResolver.openInputStream(eachImageUrl)
|
val inputStream = intent.compositionContext.contentResolver.openInputStream(eachImageUrl)
|
||||||
@@ -69,7 +76,6 @@ class AlbumViewModel(private val coroutineContext: CoroutineContext
|
|||||||
val bitmap: Bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size, bitmapOptions)
|
val bitmap: Bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size, bitmapOptions)
|
||||||
newImages.add(bitmap.asImageBitmap())
|
newImages.add(bitmap.asImageBitmap())
|
||||||
} else {
|
} else {
|
||||||
// error reading the bytes from the image url
|
|
||||||
println("The image that was picked could not be read from the device at this url: $eachImageUrl")
|
println("The image that was picked could not be read from the device at this url: $eachImageUrl")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,42 +86,36 @@ class AlbumViewModel(private val coroutineContext: CoroutineContext
|
|||||||
tempFileUrl = null
|
tempFileUrl = null
|
||||||
)
|
)
|
||||||
_albumViewState.value = newCopy
|
_albumViewState.value = newCopy
|
||||||
} else {
|
|
||||||
// user did not pick anything
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Intent.OnImageSavedWith -> {
|
is Intent.OnImageSavedWith -> {
|
||||||
val tempImageUrl = _albumViewState.value.tempFileUrl
|
val tempImageUrl = _albumViewState.value.tempFileUrl
|
||||||
if (tempImageUrl != null) {
|
if (tempImageUrl != null) {
|
||||||
val source = ImageDecoder.createSource(intent.compositionContext.contentResolver, tempImageUrl)
|
val source = ImageDecoder.createSource(
|
||||||
|
intent.compositionContext.contentResolver,
|
||||||
|
tempImageUrl
|
||||||
|
)
|
||||||
|
|
||||||
val currentPictures = _albumViewState.value.selectedPictures.toMutableList()
|
val currentPictures = _albumViewState.value.selectedPictures.toMutableList()
|
||||||
currentPictures.add(ImageDecoder.decodeBitmap(source).asImageBitmap())
|
currentPictures.add(ImageDecoder.decodeBitmap(source).asImageBitmap())
|
||||||
|
|
||||||
_albumViewState.value = _albumViewState.value.copy(tempFileUrl = null,
|
_albumViewState.value = _albumViewState.value.copy(
|
||||||
selectedPictures = currentPictures)
|
tempFileUrl = null,
|
||||||
|
selectedPictures = currentPictures
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Intent.OnImageSavingCanceled -> {
|
is Intent.OnImageSavingCanceled -> {
|
||||||
_albumViewState.value = _albumViewState.value.copy(tempFileUrl = null)
|
_albumViewState.value = _albumViewState.value.copy(tempFileUrl = null)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
is Intent.OnPermissionGranted -> {
|
|
||||||
// unnecessary in this viewmodel variant
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is Intent.OnFinishPickingImages -> {
|
/**
|
||||||
// unnecessary in this viewmodel variant
|
* Löscht alle ausgewählten Bilder aus dem State.
|
||||||
}
|
*/
|
||||||
|
|
||||||
is Intent.OnImageSaved -> {
|
|
||||||
// unnecessary in this viewmodel variant
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
fun clearSelection() {
|
fun clearSelection() {
|
||||||
_albumViewState.value = _albumViewState.value.copy(selectedPictures = emptyList())
|
_albumViewState.value = _albumViewState.value.copy(selectedPictures = emptyList())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
@file:Suppress("COMPOSE_APPLIER_CALL_MISMATCH")
|
|
||||||
|
|
||||||
package com.example.snapandsolve.ui.theme
|
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.RowScope
|
|
||||||
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.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun OverlayShell(
|
|
||||||
title: String,
|
|
||||||
footer: @Composable RowScope.() -> Unit,
|
|
||||||
content: @Composable ColumnScope.() -> Unit
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.background(Color.Black.copy(alpha = 0.25f)),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
BoxWithConstraints {
|
|
||||||
val verticalMargin = 50.dp
|
|
||||||
val maxCardHeight = maxHeight - verticalMargin * 2
|
|
||||||
|
|
||||||
Card(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth(0.9f)
|
|
||||||
.heightIn(min = 400.dp, max = maxCardHeight),
|
|
||||||
shape = RoundedCornerShape(24.dp),
|
|
||||||
colors = CardDefaults.cardColors(containerColor = WidgetColor)
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(20.dp)
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = title,
|
|
||||||
style = MaterialTheme.typography.titleLarge,
|
|
||||||
color = Color.Black
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(Modifier.height(12.dp))
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.weight(1f, fill = true)
|
|
||||||
.verticalScroll(rememberScrollState()),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
|
||||||
content = content
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(Modifier.height(12.dp))
|
|
||||||
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
content = footer
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -26,7 +26,6 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.snapandsolve.camera.AlbumViewModel
|
import com.example.snapandsolve.camera.AlbumViewModel
|
||||||
import com.example.snapandsolve.camera.AlbumViewState
|
import com.example.snapandsolve.camera.AlbumViewState
|
||||||
import com.example.snapandsolve.camera.Intent
|
|
||||||
import com.example.snapandsolve.ui.theme.composable.AppButton
|
import com.example.snapandsolve.ui.theme.composable.AppButton
|
||||||
import com.example.snapandsolve.ui.theme.composable.AppButtonStyle
|
import com.example.snapandsolve.ui.theme.composable.AppButtonStyle
|
||||||
import com.example.snapandsolve.ui.theme.composable.DialogContainer
|
import com.example.snapandsolve.ui.theme.composable.DialogContainer
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
## Intent (sealed class)
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
sealed class Intent
|
||||||
|
```
|
||||||
|
|
||||||
|
**Zweck:** Definition aller möglichen Benutzeraktionen im Album-System.
|
||||||
|
|
||||||
|
### Varianten
|
||||||
|
|
||||||
|
| Intent | Parameter | Beschreibung |
|
||||||
|
|--------|-----------|--------------|
|
||||||
|
| `OnPermissionGrantedWith` | `Context` | Kamera-Permission erteilt |
|
||||||
|
| `OnImageSavedWith` | `Context` | Kamera-Aufnahme gespeichert |
|
||||||
|
| `OnFinishPickingImagesWith` | `Context, List<Uri>` | Bilder aus Galerie ausgewählt |
|
||||||
|
| `OnPermissionDenied` | - | Permission verweigert |
|
||||||
|
| `OnImageSavingCanceled` | - | Kamera-Aufnahme abgebrochen |
|
||||||
|
|
||||||
|
**Deprecated:** `OnPermissionGranted`, `OnImageSaved`, `OnFinishPickingImages` (Varianten ohne Context-Parameter)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Album/Kamera-System Dokumentation
|
||||||
|
|
||||||
|
## AlbumViewModel
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
class AlbumViewModel(private val coroutineContext: CoroutineContext) : ViewModel()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Zweck:** Verwaltung von Bildauswahl und Kamera-Aufnahmen für Schadensmeldungen.
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
| Name | Typ | Beschreibung |
|
||||||
|
|------|-----|--------------|
|
||||||
|
| `viewStateFlow` | `StateFlow<AlbumViewState>` | Read-only State für UI-Komponenten |
|
||||||
|
|
||||||
|
### Methoden
|
||||||
|
|
||||||
|
#### `onReceive(intent: Intent)`
|
||||||
|
Verarbeitet Benutzeraktionen für Bild-Verwaltung.
|
||||||
|
|
||||||
|
**Parameter:**
|
||||||
|
- `intent: Intent` - Benutzeraktion (siehe Intent-Klasse)
|
||||||
|
|
||||||
|
**Verwendete Intents:**
|
||||||
|
- `OnPermissionGrantedWith(Context)` - Erstellt temp. Datei für Kamera
|
||||||
|
- `OnFinishPickingImagesWith(Context, List<Uri>)` - Lädt Bilder aus Galerie
|
||||||
|
- `OnImageSavedWith(Context)` - Speichert Kamera-Aufnahme
|
||||||
|
- `OnImageSavingCanceled` - Verwirft temp. Datei
|
||||||
|
- `OnPermissionDenied` - Loggt Permission-Verweigerung
|
||||||
|
|
||||||
|
**Deprecated Intents:** `OnPermissionGranted`, `OnFinishPickingImages`, `OnImageSaved` (ohne Context)
|
||||||
|
|
||||||
|
#### `clearSelection()`
|
||||||
|
Löscht alle ausgewählten Bilder aus dem State.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
## AlbumViewState
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
data class AlbumViewState(
|
||||||
|
val tempFileUrl: Uri? = null,
|
||||||
|
val selectedPictures: List<ImageBitmap> = emptyList()
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Zweck:** Immutable State-Container für Album-UI.
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
| Name | Typ | Default | Beschreibung |
|
||||||
|
|------|-----|---------|--------------|
|
||||||
|
| `tempFileUrl` | `Uri?` | `null` | Temporäre URI für Kamera-Aufnahme |
|
||||||
|
| `selectedPictures` | `List<ImageBitmap>` | `emptyList()` | Alle ausgewählten/aufgenommenen Bilder |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verwendungsbeispiel
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// Initialisierung
|
||||||
|
val albumViewModel = remember { AlbumViewModel(Dispatchers.Default) }
|
||||||
|
|
||||||
|
// State beobachten
|
||||||
|
val viewState by albumViewModel.viewStateFlow.collectAsState()
|
||||||
|
|
||||||
|
// Kamera öffnen
|
||||||
|
albumViewModel.onReceive(Intent.OnPermissionGrantedWith(context))
|
||||||
|
val cameraUri = viewState.tempFileUrl
|
||||||
|
|
||||||
|
// Bild gespeichert
|
||||||
|
albumViewModel.onReceive(Intent.OnImageSavedWith(context))
|
||||||
|
|
||||||
|
// Bilder hochladen
|
||||||
|
viewState.selectedPictures.forEach { bitmap ->
|
||||||
|
mapViewModel.uploadImageAsAttachment(bitmap)
|
||||||
|
}
|
||||||
|
albumViewModel.clearSelection()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user