From ea5d7109d44ca86105d6813105d78def21e1d329 Mon Sep 17 00:00:00 2001 From: SaiD Date: Thu, 4 Dec 2025 13:10:49 +0530 Subject: [PATCH] updated features --- ...otlin-compiler-7349953870880591348.salive} | 0 app/build.gradle.kts | 1 + .../java/com/example/livingai/MainActivity.kt | 16 +- .../livingai/data/local/CSVDataSource.kt | 9 +- .../example/livingai/data/ml/AIModelImpl.kt | 83 ++++++- .../java/com/example/livingai/di/AppModule.kt | 36 ++- .../com/example/livingai/domain/ml/AIModel.kt | 1 + .../pages/addprofile/AddProfileScreen.kt | 14 +- .../pages/addprofile/AddProfileViewModel.kt | 76 +++++- .../livingai/pages/camera/CameraScreen.kt | 77 ++++-- .../livingai/pages/camera/CameraViewModel.kt | 125 ++++++---- .../pages/camera/VideoRecordScreen.kt | 2 +- .../livingai/pages/camera/ViewVideoScreen.kt | 131 +++++++---- .../pages/components/CameraPreview.kt | 15 +- .../pages/components/CommonScaffold.kt | 28 ++- .../pages/components/LabeledDropdown.kt | 5 +- .../pages/components/LivingAIBottomBar.kt | 15 +- .../example/livingai/pages/home/HomeScreen.kt | 21 +- .../livingai/pages/listings/ListingsScreen.kt | 183 +++++++++++++-- .../pages/listings/ListingsViewModel.kt | 63 ++++- .../livingai/pages/navigation/NavGraph.kt | 26 +- .../livingai/pages/ratings/RatingScreen.kt | 222 ++++++++++-------- .../livingai/pages/settings/SettingsScreen.kt | 14 +- .../pages/settings/SettingsViewModel.kt | 15 +- .../livingai/utils/CoroutineDispatchers.kt | 16 ++ .../example/livingai/utils/LocaleHelper.kt | 24 ++ .../livingai/utils/ScreenOrientation.kt | 20 ++ .../livingai/utils/SilhouetteManager.kt | 77 ++++++ .../main/res/drawable/angleview_silhoutte.png | Bin 0 -> 6087 bytes app/src/main/res/drawable/back_silhoutte.png | Bin 0 -> 5005 bytes app/src/main/res/drawable/front_silhoutte.png | Bin 0 -> 3772 bytes app/src/main/res/drawable/left_silhoutte.png | Bin 0 -> 27312 bytes .../main/res/drawable/leftangle_silhoutte.png | Bin 0 -> 48193 bytes app/src/main/res/drawable/right_silhoutte.png | Bin 0 -> 6115 bytes .../res/drawable/rightangle_silhoutte.png | Bin 0 -> 33974 bytes 35 files changed, 1024 insertions(+), 291 deletions(-) rename .kotlin/sessions/{kotlin-compiler-1933245961721594071.salive => kotlin-compiler-7349953870880591348.salive} (100%) create mode 100644 app/src/main/java/com/example/livingai/utils/CoroutineDispatchers.kt create mode 100644 app/src/main/java/com/example/livingai/utils/LocaleHelper.kt create mode 100644 app/src/main/java/com/example/livingai/utils/ScreenOrientation.kt create mode 100644 app/src/main/java/com/example/livingai/utils/SilhouetteManager.kt create mode 100644 app/src/main/res/drawable/angleview_silhoutte.png create mode 100644 app/src/main/res/drawable/back_silhoutte.png create mode 100644 app/src/main/res/drawable/front_silhoutte.png create mode 100644 app/src/main/res/drawable/left_silhoutte.png create mode 100644 app/src/main/res/drawable/leftangle_silhoutte.png create mode 100644 app/src/main/res/drawable/right_silhoutte.png create mode 100644 app/src/main/res/drawable/rightangle_silhoutte.png diff --git a/.kotlin/sessions/kotlin-compiler-1933245961721594071.salive b/.kotlin/sessions/kotlin-compiler-7349953870880591348.salive similarity index 100% rename from .kotlin/sessions/kotlin-compiler-1933245961721594071.salive rename to .kotlin/sessions/kotlin-compiler-7349953870880591348.salive diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5938e78..02e9809 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -86,6 +86,7 @@ dependencies { // Coil implementation("io.coil-kt:coil-compose:2.5.0") implementation("io.coil-kt:coil-video:2.5.0") + implementation("io.coil-kt:coil-svg:2.5.0") // Paging implementation("androidx.paging:paging-runtime:3.2.1") diff --git a/app/src/main/java/com/example/livingai/MainActivity.kt b/app/src/main/java/com/example/livingai/MainActivity.kt index f992230..a17ba23 100644 --- a/app/src/main/java/com/example/livingai/MainActivity.kt +++ b/app/src/main/java/com/example/livingai/MainActivity.kt @@ -8,6 +8,7 @@ import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb @@ -15,11 +16,12 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat import com.example.livingai.pages.home.HomeViewModel import com.example.livingai.pages.navigation.NavGraph +import com.example.livingai.pages.navigation.Route import com.example.livingai.ui.theme.LivingAITheme import org.koin.androidx.viewmodel.ext.android.viewModel class MainActivity : ComponentActivity() { - val viewModel by viewModel() + private val viewModel by viewModel() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -29,6 +31,10 @@ class MainActivity : ComponentActivity() { viewModel.splashCondition.value } } + + // The splash screen is shown until the start destination is determined. + // If there's a delay, the splash screen will cover it. + setContent { LivingAITheme { enableEdgeToEdge( @@ -43,7 +49,13 @@ class MainActivity : ComponentActivity() { ) Box(modifier = Modifier.background(color = MaterialTheme.colorScheme.background)) { val startDestination = viewModel.startDestination.value - NavGraph(startDestination = startDestination) + // Ensure startDestination is not null before rendering NavGraph + if (startDestination != null) { + NavGraph(startDestination = startDestination) + } else { + // Optional: Show a loading indicator if startDestination is null + // for an extended period, though splash screen should handle it. + } } } } diff --git a/app/src/main/java/com/example/livingai/data/local/CSVDataSource.kt b/app/src/main/java/com/example/livingai/data/local/CSVDataSource.kt index 69ac26e..7eab25c 100644 --- a/app/src/main/java/com/example/livingai/data/local/CSVDataSource.kt +++ b/app/src/main/java/com/example/livingai/data/local/CSVDataSource.kt @@ -23,7 +23,8 @@ import java.io.* class CSVDataSource( private val context: Context, - private val fileName: String + private val fileName: String, + private val dispatchers: com.example.livingai.utils.CoroutineDispatchers ) : DataSource { private val folderName = "LivingAI" @@ -32,7 +33,7 @@ class CSVDataSource( private var cachedUri: Uri? = null - private suspend fun getCsvUri(): Uri = withContext(Dispatchers.IO) { + private suspend fun getCsvUri(): Uri = withContext(dispatchers.io) { cachedUri?.let { return@withContext it } val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { @@ -105,7 +106,7 @@ class CSVDataSource( private suspend fun readAllLines(): List> = mutex.withLock { val uri = getCsvUri() - return withContext(Dispatchers.IO) { + return withContext(dispatchers.io) { try { context.contentResolver.openInputStream(uri)?.use { input -> val reader = CSVReader(InputStreamReader(input)) @@ -123,7 +124,7 @@ class CSVDataSource( private suspend fun writeAllLines(lines: List>) = mutex.withLock { val uri = getCsvUri() - withContext(Dispatchers.IO) { + withContext(dispatchers.io) { try { context.contentResolver.openOutputStream(uri, "wt")?.use { out -> val writer = CSVWriter(OutputStreamWriter(out)) diff --git a/app/src/main/java/com/example/livingai/data/ml/AIModelImpl.kt b/app/src/main/java/com/example/livingai/data/ml/AIModelImpl.kt index 1f9fdbf..5f2c462 100644 --- a/app/src/main/java/com/example/livingai/data/ml/AIModelImpl.kt +++ b/app/src/main/java/com/example/livingai/data/ml/AIModelImpl.kt @@ -1,11 +1,90 @@ package com.example.livingai.data.ml import android.graphics.Bitmap +import android.graphics.Color import com.example.livingai.domain.ml.AIModel +import com.google.mlkit.vision.common.InputImage +import com.google.mlkit.vision.segmentation.subject.SubjectSegmentation +import com.google.mlkit.vision.segmentation.subject.SubjectSegmenterOptions +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.math.max + +// Define a constant color for the segmentation mask +private const val MASK_COLOR = 0x5500FF00 // Semi-transparent green class AIModelImpl : AIModel { + + private val segmenter by lazy { + val options = SubjectSegmenterOptions.Builder() + .enableForegroundBitmap() + .build() + SubjectSegmentation.getClient(options) + } + override fun deriveInference(bitmap: Bitmap): String { - // Placeholder for actual inference logic return "Inference Result" } -} \ No newline at end of file + + override suspend fun segmentImage(bitmap: Bitmap): Pair? { + return suspendCancellableCoroutine { cont -> + val image = InputImage.fromBitmap(bitmap, 0) + segmenter.process(image) + .addOnSuccessListener { result -> + val foregroundBitmap = result.foregroundBitmap + if (foregroundBitmap != null) { + val colorizedMask = createColorizedMask(foregroundBitmap) + val booleanMask = createBooleanMask(foregroundBitmap) + cont.resume(Pair(colorizedMask, booleanMask)) + } else { + cont.resume(null) + } + } + .addOnFailureListener { e -> + cont.resumeWithException(e) + } + } + } + + private fun createColorizedMask(maskBitmap: Bitmap): Bitmap { + val width = maskBitmap.width + val height = maskBitmap.height + val pixels = IntArray(width * height) + maskBitmap.getPixels(pixels, 0, width, 0, 0, width, height) + + for (i in pixels.indices) { + if (Color.alpha(pixels[i]) > 0) { + pixels[i] = MASK_COLOR + } + } + + return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888) + } + + private fun createBooleanMask(bitmap: Bitmap): BooleanArray { + val w = bitmap.width + val h = bitmap.height + val mask = BooleanArray(w * h) + val pixels = IntArray(w * h) + bitmap.getPixels(pixels, 0, w, 0, 0, w, h) + for (i in pixels.indices) { + val alpha = Color.alpha(pixels[i]) + mask[i] = alpha > 0 // Assuming foreground bitmap has transparent background + } + return mask + } + + fun jaccardIndex(maskA: BooleanArray, maskB: BooleanArray): Double { + val len = max(maskA.size, maskB.size) + var inter = 0 + var union = 0 + for (i in 0 until len) { + val a = if (i < maskA.size) maskA[i] else false + val b = if (i < maskB.size) maskB[i] else false + if (a || b) union++ + if (a && b) inter++ + } + return if (union == 0) 0.0 else inter.toDouble() / union.toDouble() + } +} diff --git a/app/src/main/java/com/example/livingai/di/AppModule.kt b/app/src/main/java/com/example/livingai/di/AppModule.kt index ff63050..cf1885c 100644 --- a/app/src/main/java/com/example/livingai/di/AppModule.kt +++ b/app/src/main/java/com/example/livingai/di/AppModule.kt @@ -4,6 +4,8 @@ import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.preferencesDataStore +import coil.ImageLoader +import coil.decode.SvgDecoder import com.example.livingai.data.local.CSVDataSource import com.example.livingai.data.manager.LocalUserManagerImpl import com.example.livingai.data.ml.AIModelImpl @@ -42,6 +44,9 @@ import com.example.livingai.pages.onboarding.OnBoardingViewModel import com.example.livingai.pages.ratings.RatingViewModel import com.example.livingai.pages.settings.SettingsViewModel import com.example.livingai.utils.Constants +import com.example.livingai.utils.CoroutineDispatchers +import com.example.livingai.utils.DefaultCoroutineDispatchers +import com.example.livingai.utils.SilhouetteManager import org.koin.android.ext.koin.androidContext import org.koin.core.module.dsl.viewModel import org.koin.dsl.module @@ -59,14 +64,35 @@ val appModule = module { ) } + // Coroutine dispatchers (for testability) + single { DefaultCoroutineDispatchers() } + // Data Source single { CSVDataSource( context = androidContext(), - fileName = Constants.ANIMAL_DATA_FILENAME + fileName = Constants.ANIMAL_DATA_FILENAME, + dispatchers = get() ) } + // Coil ImageLoader singleton + single { + ImageLoader.Builder(androidContext()) + .components { + add(SvgDecoder.Factory()) + } + .build() + } + + // Initialize silhouettes once + single(createdAtStart = true) { + val ctx: Context = androidContext() + // Names expected to be provided as drawable resource names like "front_silhoutte" + val names = listOf("front_silhouette", "side_silhouette", "rear_silhouette") + SilhouetteManager.initialize(ctx, names) + } + // ML Model single { AIModelImpl() } @@ -104,10 +130,12 @@ val appModule = module { // ViewModels viewModel { HomeViewModel(get()) } viewModel { OnBoardingViewModel(get(), get()) } - viewModel { AddProfileViewModel(get()) } + viewModel { (savedStateHandle: androidx.lifecycle.SavedStateHandle?) -> + AddProfileViewModel(get(), get(), get(), androidContext(), savedStateHandle) + } viewModel { ListingsViewModel(get()) } - viewModel { SettingsViewModel(get()) } + viewModel { SettingsViewModel(get(), androidContext()) } viewModel { RatingViewModel(get(), get(), get(), get()) } - viewModel { CameraViewModel(get()) } + viewModel { CameraViewModel(get(), get()) } viewModel { VideoViewModel() } } diff --git a/app/src/main/java/com/example/livingai/domain/ml/AIModel.kt b/app/src/main/java/com/example/livingai/domain/ml/AIModel.kt index 5c1f336..ee66295 100644 --- a/app/src/main/java/com/example/livingai/domain/ml/AIModel.kt +++ b/app/src/main/java/com/example/livingai/domain/ml/AIModel.kt @@ -4,4 +4,5 @@ import android.graphics.Bitmap interface AIModel { fun deriveInference(bitmap: Bitmap): String + suspend fun segmentImage(bitmap: Bitmap): Pair? } \ No newline at end of file diff --git a/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileScreen.kt b/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileScreen.kt index da8409f..b14a9e0 100644 --- a/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileScreen.kt +++ b/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier @@ -49,6 +50,12 @@ fun AddProfileScreen( onTakeVideo: () -> Unit ) { val context = LocalContext.current + + // If opened for edit, attempt to load existing animal details + LaunchedEffect(Unit) { + val existing = viewModel.savedStateHandle?.get("animalId") + if (existing != null) viewModel.loadAnimal(existing) + } val speciesList = stringArrayResource(id = R.array.species_list).toList() val breedList = stringArrayResource(id = R.array.cow_breed_list).toList() @@ -59,10 +66,9 @@ fun AddProfileScreen( stringResource(R.string.option_none) ) - val silhouette = Constants.silhouetteList.associate { item -> + val silhouette = Constants.silhouetteList.associateWith { item -> val resId = context.resources.getIdentifier("label_${item}", "string", context.packageName) - - item to if (resId != 0) resId else R.string.default_orientation_label + if (resId != 0) resId else R.string.default_orientation_label } // Use ViewModel state @@ -172,7 +178,7 @@ fun AddProfileScreen( // Video Button item { VideoThumbnailButton( - videoSource = videoUri, // Removed videoThumbnail fallback as it's not in VM + videoSource = videoUri, onClick = onTakeVideo ) } diff --git a/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileViewModel.kt b/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileViewModel.kt index d5ebed8..3b43c20 100644 --- a/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileViewModel.kt +++ b/app/src/main/java/com/example/livingai/pages/addprofile/AddProfileViewModel.kt @@ -1,21 +1,32 @@ package com.example.livingai.pages.addprofile +import android.content.Context +import android.net.Uri +import android.provider.OpenableColumns import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.livingai.domain.model.AnimalDetails +import com.example.livingai.domain.usecases.GetAnimalDetails import com.example.livingai.domain.usecases.ProfileEntry.ProfileEntryUseCase +import com.example.livingai.utils.CoroutineDispatchers import com.example.livingai.utils.IdGenerator import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext class AddProfileViewModel( - private val profileEntryUseCase: ProfileEntryUseCase + private val profileEntryUseCase: ProfileEntryUseCase, + private val getAnimalDetails: GetAnimalDetails, + private val dispatchers: CoroutineDispatchers, + private val context: Context, + val savedStateHandle: SavedStateHandle? = null ) : ViewModel() { - + private val _animalDetails = mutableStateOf(null) val animalDetails: State = _animalDetails @@ -36,7 +47,7 @@ class AddProfileViewModel( private val _videoUri = mutableStateOf(null) val videoUri: State = _videoUri - fun loadAnimalDetails(animalId: String?) { + fun loadAnimal(animalId: String?) { if (animalId == null) { val newId = IdGenerator.generateAnimalId() _currentAnimalId.value = newId @@ -70,15 +81,22 @@ class AddProfileViewModel( // Populate photos photos.clear() - details.images.forEach { path -> - // path: .../{id}_{orientation}.jpg - val filename = path.substringAfterLast('/') - val nameWithoutExt = filename.substringBeforeLast('.') - val parts = nameWithoutExt.split('_') - if (parts.size >= 2) { - val orientation = parts.last() - photos[orientation] = path - } + // Process images on IO thread as it may involve DB queries + withContext(dispatchers.io) { + val photoMap = mutableMapOf() + details.images.forEach { path -> + val uri = Uri.parse(path) + val filename = getFileName(uri) ?: path.substringAfterLast('/') + val nameWithoutExt = filename.substringBeforeLast('.') + val parts = nameWithoutExt.split('_') + if (parts.size >= 2) { + val orientation = parts.last() + photoMap[orientation] = path + } + } + withContext(dispatchers.main) { + photoMap.forEach { (k, v) -> photos[k] = v } + } } _videoUri.value = details.video.ifBlank { null } } @@ -86,6 +104,33 @@ class AddProfileViewModel( } } + private fun getFileName(uri: Uri): String? { + var result: String? = null + if (uri.scheme == "content") { + val cursor = context.contentResolver.query(uri, null, null, null, null) + try { + if (cursor != null && cursor.moveToFirst()) { + val index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) + if (index >= 0) { + result = cursor.getString(index) + } + } + } catch (e: Exception) { + e.printStackTrace() + } finally { + cursor?.close() + } + } + if (result == null) { + result = uri.path + val cut = result?.lastIndexOf('/') + if (cut != null && cut != -1) { + result = result?.substring(cut + 1) + } + } + return result + } + fun addPhoto(orientation: String, uri: String) { photos[orientation] = uri } @@ -115,4 +160,11 @@ class AddProfileViewModel( profileEntryUseCase.setAnimalDetails(details) } } + + init { + // Try to auto-load when editing via saved state handle + val animalId: String? = savedStateHandle?.get("animalId") + // Call loadAnimal unconditionally to ensure ID generation for new profiles + loadAnimal(animalId) + } } diff --git a/app/src/main/java/com/example/livingai/pages/camera/CameraScreen.kt b/app/src/main/java/com/example/livingai/pages/camera/CameraScreen.kt index a5ecead..44c9f55 100644 --- a/app/src/main/java/com/example/livingai/pages/camera/CameraScreen.kt +++ b/app/src/main/java/com/example/livingai/pages/camera/CameraScreen.kt @@ -1,9 +1,11 @@ package com.example.livingai.pages.camera +import android.content.pm.ActivityInfo import androidx.camera.core.ImageCapture import androidx.camera.core.ImageCaptureException import androidx.camera.core.ImageProxy import androidx.camera.view.LifecycleCameraController +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row @@ -25,14 +27,18 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat import androidx.navigation.NavController -import org.koin.androidx.compose.koinViewModel import com.example.livingai.pages.components.CameraPreview import com.example.livingai.pages.components.PermissionWrapper import com.example.livingai.pages.navigation.Route +import com.example.livingai.utils.SetScreenOrientation +import com.example.livingai.utils.SilhouetteManager +import org.koin.androidx.compose.koinViewModel @Composable fun CameraScreen( @@ -41,6 +47,12 @@ fun CameraScreen( orientation: String? = null, animalId: String ) { + val orientationLock = when (orientation) { + "front", "back" -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + else -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + } + SetScreenOrientation(orientationLock) + PermissionWrapper { val state by viewModel.state.collectAsState() val context = LocalContext.current @@ -99,29 +111,66 @@ fun CameraScreen( .fillMaxSize() .padding(paddingValues), ) { - CameraPreview( - modifier = Modifier.fillMaxSize(), - controller = controller, - onFrame = { bitmap -> - if (state.isAutoCaptureOn) { - viewModel.onEvent(CameraEvent.FrameReceived(bitmap)) + Box(modifier = Modifier.fillMaxSize()) { + CameraPreview( + modifier = Modifier.fillMaxSize(), + controller = controller, + onFrame = { bitmap, rotation -> + if (orientation != null) { + viewModel.onEvent(CameraEvent.FrameReceived(bitmap, rotation)) + } + } + ) + + // Overlay silhouette if orientation provided + if (state.orientation != null) { + val silhouetteName = "${state.orientation}_silhouette" + val bmp = SilhouetteManager.getOriginal(silhouetteName) + if (bmp != null) { + Image( + bitmap = bmp.asImageBitmap(), + contentDescription = "overlay", + modifier = Modifier.matchParentSize(), + contentScale = ContentScale.Crop, + alpha = 0.5f + ) } } - ) + + // Overlay Segmentation Mask + state.segmentationMask?.let { mask -> + Image( + bitmap = mask.asImageBitmap(), + contentDescription = "segmentation overlay", + modifier = Modifier.matchParentSize(), + contentScale = ContentScale.FillBounds, + alpha = 0.5f + ) + } + } + // Top Bar with Inference Result and Auto Capture Switch Row( modifier = Modifier .fillMaxWidth() .align(Alignment.TopCenter) .padding(16.dp), - horizontalArrangement = Arrangement.End, + horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { - Text("Auto Capture") - Switch( - checked = state.isAutoCaptureOn, - onCheckedChange = { viewModel.onEvent(CameraEvent.ToggleAutoCapture) } - ) + if (state.inferenceResult != null) { + Text(text = state.inferenceResult!!, color = androidx.compose.ui.graphics.Color.White) + } else { + Box(modifier = Modifier.weight(1f)) + } + + Row(verticalAlignment = Alignment.CenterVertically) { + Text("Auto Capture", color = androidx.compose.ui.graphics.Color.White) + Switch( + checked = state.isAutoCaptureOn, + onCheckedChange = { viewModel.onEvent(CameraEvent.ToggleAutoCapture) } + ) + } } } } diff --git a/app/src/main/java/com/example/livingai/pages/camera/CameraViewModel.kt b/app/src/main/java/com/example/livingai/pages/camera/CameraViewModel.kt index 9f84f94..4477a30 100644 --- a/app/src/main/java/com/example/livingai/pages/camera/CameraViewModel.kt +++ b/app/src/main/java/com/example/livingai/pages/camera/CameraViewModel.kt @@ -1,87 +1,126 @@ package com.example.livingai.pages.camera import android.graphics.Bitmap +import android.graphics.Matrix import android.net.Uri import androidx.camera.core.ImageProxy import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.example.livingai.data.ml.AIModelImpl +import com.example.livingai.domain.ml.AIModel import com.example.livingai.domain.repository.CameraRepository +import com.example.livingai.utils.SilhouetteManager import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock class CameraViewModel( - private val cameraRepository: CameraRepository + private val cameraRepository: CameraRepository, + private val aiModel: AIModel ) : ViewModel() { - private val _state = MutableStateFlow(CameraState()) + private val _state = MutableStateFlow(CameraUiState()) val state = _state.asStateFlow() - private var currentAnimalId: String = "" - private var currentOrientation: String? = null + // mutex to prevent parallel captures + private val captureMutex = Mutex() fun onEvent(event: CameraEvent) { when (event) { - is CameraEvent.CaptureImage -> { - // Signal UI to capture? Or handle if passed image - } - is CameraEvent.ImageCaptured -> { - saveImage(event.imageProxy) - } - is CameraEvent.FrameReceived -> { - processFrame(event.bitmap) - } - is CameraEvent.ToggleAutoCapture -> { - _state.value = _state.value.copy(isAutoCaptureOn = !_state.value.isAutoCaptureOn) - } - is CameraEvent.ClearCapturedImage -> { - _state.value = _state.value.copy(capturedImage = null, capturedImageUri = null) - } - is CameraEvent.SetContext -> { - currentAnimalId = event.animalId - currentOrientation = event.orientation - } + is CameraEvent.CaptureImage -> captureImage() + is CameraEvent.ImageCaptured -> handleImageProxy(event.imageProxy) + is CameraEvent.FrameReceived -> handleFrame(event.bitmap, event.rotationDegrees) + is CameraEvent.ToggleAutoCapture -> toggleAutoCapture() + is CameraEvent.ClearCapturedImage -> clearCaptured() + is CameraEvent.SetContext -> setContext(event.animalId, event.orientation) } } - private fun saveImage(imageProxy: ImageProxy) { + private fun setContext(animalId: String, orientation: String?) { + _state.value = _state.value.copy(animalId = animalId, orientation = orientation) + } + + private fun toggleAutoCapture() { + _state.value = _state.value.copy(isAutoCaptureOn = !_state.value.isAutoCaptureOn) + } + + private fun clearCaptured() { + _state.value = _state.value.copy(capturedImage = null, capturedImageUri = null, segmentationMask = null) + } + + private fun captureImage() { + // UI should handle capture and send ImageCaptured event + } + + private fun handleImageProxy(proxy: ImageProxy) { + // convert to bitmap and then save via repository viewModelScope.launch { - try { - val bitmap = cameraRepository.captureImage(imageProxy) - val uriString = cameraRepository.saveImage(bitmap, currentAnimalId, currentOrientation) - _state.value = _state.value.copy(capturedImage = bitmap, capturedImageUri = Uri.parse(uriString)) - } catch (e: Exception) { - // Handle error - imageProxy.close() // Ensure closed on error if repository didn't - } + val bitmap = cameraRepository.captureImage(proxy) + val animalId = _state.value.animalId ?: "unknown" + val uriString = cameraRepository.saveImage(bitmap, animalId, _state.value.orientation) + _state.value = _state.value.copy(capturedImageUri = Uri.parse(uriString)) } } - private fun processFrame(bitmap: Bitmap) { + private fun handleFrame(bitmap: Bitmap, rotationDegrees: Int) { + val orientation = _state.value.orientation ?: return + viewModelScope.launch { - try { - val result = cameraRepository.processFrame(bitmap) - _state.value = _state.value.copy(inferenceResult = result) - } catch (e: Exception) { - // Handle error + if (captureMutex.tryLock()) { + try { + val result = aiModel.segmentImage(bitmap) + if (result != null) { + val (maskBitmap, maskBool) = result + + // Rotate the mask to match the display orientation + val rotatedMask = if (rotationDegrees != 0) { + val matrix = Matrix().apply { postRotate(rotationDegrees.toFloat()) } + Bitmap.createBitmap(maskBitmap, 0, 0, maskBitmap.width, maskBitmap.height, matrix, true) + } else { + maskBitmap + } + + _state.value = _state.value.copy(segmentationMask = rotatedMask) + + val savedMask = SilhouetteManager.getBitmask("${orientation}_silhouette") + if (savedMask != null) { + if (maskBool.size == savedMask.size) { + val jaccard = (aiModel as AIModelImpl).jaccardIndex(maskBool, savedMask) + + if (_state.value.isAutoCaptureOn && jaccard > 0.5) { + val animalId = _state.value.animalId ?: "unknown" + val uriString = cameraRepository.saveImage(bitmap, animalId, orientation) + _state.value = _state.value.copy(capturedImageUri = Uri.parse(uriString)) + } + + _state.value = _state.value.copy(inferenceResult = "Jaccard: %.2f".format(jaccard)) + } + } + } + } finally { + captureMutex.unlock() + } } } } } -data class CameraState( - val isLoading: Boolean = false, - val isCameraReady: Boolean = false, +data class CameraUiState( + val animalId: String? = null, + val orientation: String? = null, val capturedImage: Any? = null, val capturedImageUri: Uri? = null, val inferenceResult: String? = null, - val isAutoCaptureOn: Boolean = false + val isAutoCaptureOn: Boolean = false, + val segmentationMask: Bitmap? = null ) sealed class CameraEvent { object CaptureImage : CameraEvent() data class ImageCaptured(val imageProxy: ImageProxy) : CameraEvent() - data class FrameReceived(val bitmap: Bitmap) : CameraEvent() + data class FrameReceived(val bitmap: Bitmap, val rotationDegrees: Int) : CameraEvent() object ToggleAutoCapture : CameraEvent() object ClearCapturedImage : CameraEvent() data class SetContext(val animalId: String, val orientation: String?) : CameraEvent() diff --git a/app/src/main/java/com/example/livingai/pages/camera/VideoRecordScreen.kt b/app/src/main/java/com/example/livingai/pages/camera/VideoRecordScreen.kt index eb18e58..9c1b369 100644 --- a/app/src/main/java/com/example/livingai/pages/camera/VideoRecordScreen.kt +++ b/app/src/main/java/com/example/livingai/pages/camera/VideoRecordScreen.kt @@ -135,7 +135,7 @@ fun VideoRecordScreen( CameraPreview( modifier = Modifier.fillMaxSize(), controller = controller, - onFrame = {} + onFrame = { _, _ -> } ) } } diff --git a/app/src/main/java/com/example/livingai/pages/camera/ViewVideoScreen.kt b/app/src/main/java/com/example/livingai/pages/camera/ViewVideoScreen.kt index b78e93d..c25ccd2 100644 --- a/app/src/main/java/com/example/livingai/pages/camera/ViewVideoScreen.kt +++ b/app/src/main/java/com/example/livingai/pages/camera/ViewVideoScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -13,16 +14,20 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Pause import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -32,6 +37,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.example.livingai.ui.theme.LivingAITheme +import kotlinx.coroutines.delay @Composable fun ViewVideoScreen( @@ -43,9 +49,22 @@ fun ViewVideoScreen( ) { var isPlaying by remember { mutableStateOf(false) } var videoView: VideoView? by remember { mutableStateOf(null) } - // Keep track if we have sought to the first frame to avoid resetting on recomposition repeatedly if not needed, - // though VideoView state management in Compose can be tricky. var isPrepared by remember { mutableStateOf(false) } + var currentPosition by remember { mutableIntStateOf(0) } + var duration by remember { mutableIntStateOf(0) } + + // Polling for video progress + LaunchedEffect(isPlaying, isPrepared) { + if (isPlaying && isPrepared) { + while (true) { + videoView?.let { + currentPosition = it.currentPosition + if (duration == 0) duration = it.duration + } + delay(100) + } + } + } LivingAITheme { Scaffold { padding -> @@ -57,7 +76,7 @@ fun ViewVideoScreen( setOnPreparedListener { mp -> mp.isLooping = true isPrepared = true - // Seek to 1ms to show thumbnail (first frame) + duration = mp.duration seekTo(1) } setOnCompletionListener { @@ -75,21 +94,18 @@ fun ViewVideoScreen( } } }, - modifier = Modifier - .fillMaxSize() - .clickable { - isPlaying = !isPlaying - } + modifier = Modifier.fillMaxSize() ) - // Play Button Overlay (Only show when paused) - if (!isPlaying) { - Box( - modifier = Modifier - .fillMaxSize() - .clickable { isPlaying = true }, // Clicking anywhere while paused starts play - contentAlignment = Alignment.Center - ) { + // Controls Overlay + Box( + modifier = Modifier + .fillMaxSize() + .background(if (!isPlaying) Color.Black.copy(alpha = 0.3f) else Color.Transparent) + .clickable { isPlaying = !isPlaying }, + contentAlignment = Alignment.Center + ) { + if (!isPlaying) { Icon( imageVector = Icons.Default.PlayArrow, contentDescription = "Play", @@ -100,41 +116,66 @@ fun ViewVideoScreen( .padding(8.dp) ) } - } else { - // Invisible clickable box to pause - Box( - modifier = Modifier - .fillMaxSize() - .clickable { isPlaying = false }, - contentAlignment = Alignment.Center - ) { - // No icon, just allows pausing + } + + // Bottom Control Bar + Column( + modifier = Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth() + .background(Color.Black.copy(alpha = 0.6f)) + .padding(16.dp) + ) { + // Progress Bar + if (duration > 0) { + LinearProgressIndicator( + progress = { if (duration > 0) currentPosition.toFloat() / duration.toFloat() else 0f }, + modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), + ) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = formatTime(currentPosition), + color = Color.White, + style = MaterialTheme.typography.bodySmall + ) + Text( + text = formatTime(duration), + color = Color.White, + style = MaterialTheme.typography.bodySmall + ) + } + } + + if (shouldAllowRetake) { + Row( + modifier = Modifier.fillMaxWidth().padding(top = 8.dp), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + Button(onClick = onRetake) { + Text("Retake") + } + Button(onClick = onAccept) { + Text("Accept") + } + } } } - if (shouldAllowRetake) { - Row( - modifier = Modifier - .fillMaxWidth() - .align(Alignment.BottomCenter) - .padding(16.dp), - horizontalArrangement = Arrangement.SpaceEvenly - ) { - Button(onClick = onRetake) { - Text("Retake") - } - Button(onClick = onAccept) { - Text("Accept") - } - } - } else { + if (!shouldAllowRetake) { IconButton( onClick = onBack, modifier = Modifier .align(Alignment.TopStart) .padding(16.dp) ) { - Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + tint = Color.White + ) } } } @@ -147,3 +188,9 @@ fun ViewVideoScreen( } } } + +fun formatTime(millis: Int): String { + val seconds = (millis / 1000) % 60 + val minutes = (millis / (1000 * 60)) % 60 + return String.format("%02d:%02d", minutes, seconds) +} diff --git a/app/src/main/java/com/example/livingai/pages/components/CameraPreview.kt b/app/src/main/java/com/example/livingai/pages/components/CameraPreview.kt index af6b568..65b029e 100644 --- a/app/src/main/java/com/example/livingai/pages/components/CameraPreview.kt +++ b/app/src/main/java/com/example/livingai/pages/components/CameraPreview.kt @@ -18,7 +18,7 @@ import java.util.concurrent.Executors fun CameraPreview( modifier: Modifier = Modifier, controller: LifecycleCameraController? = null, - onFrame: (Bitmap) -> Unit + onFrame: (Bitmap, Int) -> Unit ) { val context = LocalContext.current val lifecycleOwner = LocalLifecycleOwner.current @@ -29,26 +29,19 @@ fun CameraPreview( LaunchedEffect(cameraController) { cameraController.setImageAnalysisAnalyzer(cameraExecutor) { imageProxy -> val bitmap = imageProxy.toBitmap() - onFrame(bitmap) + val rotationDegrees = imageProxy.imageInfo.rotationDegrees + onFrame(bitmap, rotationDegrees) imageProxy.close() } } - // Ensure default setup if it was created internally, or even if passed externally we might want to enforce defaults - // But typically caller configures it if they pass it. - // However, for this component to work as a preview + analysis, we should ensure analysis is enabled. - if (controller == null) { - // Only set defaults if we created it LaunchedEffect(cameraController) { cameraController.setEnabledUseCases(LifecycleCameraController.IMAGE_ANALYSIS or LifecycleCameraController.IMAGE_CAPTURE or LifecycleCameraController.VIDEO_CAPTURE) cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA cameraController.bindToLifecycle(lifecycleOwner) } } else { - // If passed externally, we still need to bind it if not already bound? - // Usually the caller binds it. But let's be safe or assume caller does it. - // Actually, let's bind it here to be sure, as this component represents the "Active Camera". LaunchedEffect(cameraController, lifecycleOwner) { cameraController.bindToLifecycle(lifecycleOwner) } @@ -69,4 +62,4 @@ fun CameraPreview( // Cleanup if needed } ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/livingai/pages/components/CommonScaffold.kt b/app/src/main/java/com/example/livingai/pages/components/CommonScaffold.kt index 1a83277..8ec1377 100644 --- a/app/src/main/java/com/example/livingai/pages/components/CommonScaffold.kt +++ b/app/src/main/java/com/example/livingai/pages/components/CommonScaffold.kt @@ -6,10 +6,13 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Build +import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.List +import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -26,20 +29,22 @@ import com.example.livingai.pages.navigation.Route fun CommonScaffold( navController: NavController, title: String, + showFab: Boolean = false, + onFabClick: () -> Unit = {}, content: @Composable (PaddingValues) -> Unit ) { val bottomNavItems = listOf( BottomBarItem( - route = Route.OnBoardingScreen, - icon = Icons.Default.Build, - name = "Nav", - notifications = 1, + route = Route.SettingsScreen, + icon = Icons.Default.Settings, + name = "Settings", + notifications = 0, ), BottomBarItem( route = Route.HomeScreen, icon = Icons.Default.Home, name = "Home", - notifications = 5, + notifications = 0, ), BottomBarItem( route = Route.ListingsScreen, @@ -74,6 +79,17 @@ fun CommonScaffold( navController = navController, onClick = { navController.navigate(it.route) } ) + }, + floatingActionButton = { + if (showFab) { + FloatingActionButton( + onClick = onFabClick, + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary + ) { + Icon(Icons.Default.Add, contentDescription = "Add Profile") + } + } } ) { innerPadding -> Box(modifier = Modifier.fillMaxSize()) { diff --git a/app/src/main/java/com/example/livingai/pages/components/LabeledDropdown.kt b/app/src/main/java/com/example/livingai/pages/components/LabeledDropdown.kt index bc3bd75..90fd141 100644 --- a/app/src/main/java/com/example/livingai/pages/components/LabeledDropdown.kt +++ b/app/src/main/java/com/example/livingai/pages/components/LabeledDropdown.kt @@ -26,11 +26,12 @@ fun LabeledDropdown( @StringRes labelRes: Int, options: List, selected: String?, - onSelected: (String) -> Unit + onSelected: (String) -> Unit, + modifier: Modifier = Modifier ) { var expanded by remember { mutableStateOf(false) } - Column { + Column(modifier = modifier) { ExposedDropdownMenuBox( expanded = expanded, onExpandedChange = { expanded = !expanded } diff --git a/app/src/main/java/com/example/livingai/pages/components/LivingAIBottomBar.kt b/app/src/main/java/com/example/livingai/pages/components/LivingAIBottomBar.kt index 53f827e..b34cb7f 100644 --- a/app/src/main/java/com/example/livingai/pages/components/LivingAIBottomBar.kt +++ b/app/src/main/java/com/example/livingai/pages/components/LivingAIBottomBar.kt @@ -15,12 +15,15 @@ import androidx.compose.material3.NavigationBarItemDefaults import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.NavController +import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController +import androidx.navigation.toRoute import com.example.livingai.pages.commons.Dimentions import com.example.livingai.pages.navigation.Route import com.example.livingai.ui.theme.LivingAITheme @@ -32,8 +35,7 @@ fun LivingAIBottomBar( onClick: (BottomBarItem) -> Unit, modifier: Modifier = Modifier ) { - val backStackEntry = navController.currentBackStackEntryAsState() - val currentRoute = backStackEntry.value?.destination?.route + val backStackEntry by navController.currentBackStackEntryAsState() NavigationBar( modifier = modifier, @@ -42,9 +44,14 @@ fun LivingAIBottomBar( tonalElevation = Dimentions.BOTTOM_BAR_ELEVATION ) { navItems.forEach { item -> - val sel = ((currentRoute != null) && (item.route::class == currentRoute::class)) + val selected = backStackEntry?.destination?.hierarchy?.any { + // This is a safer check. It compares the string route pattern. + // It's less ideal than type-safe checking but avoids deserialization crashes. + it.route == item.route::class.simpleName + } == true + NavigationBarItem( - selected = sel, + selected = selected, onClick = { onClick(item) }, colors = NavigationBarItemDefaults.colors( selectedIconColor = MaterialTheme.colorScheme.onSecondaryContainer, diff --git a/app/src/main/java/com/example/livingai/pages/home/HomeScreen.kt b/app/src/main/java/com/example/livingai/pages/home/HomeScreen.kt index ab60202..870e1ed 100644 --- a/app/src/main/java/com/example/livingai/pages/home/HomeScreen.kt +++ b/app/src/main/java/com/example/livingai/pages/home/HomeScreen.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -23,12 +22,18 @@ import androidx.compose.ui.text.font.FontWeight import androidx.navigation.NavController import com.example.livingai.R import com.example.livingai.pages.commons.Dimentions +import com.example.livingai.pages.components.CommonScaffold import com.example.livingai.pages.navigation.Route @OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeScreen(navController: NavController) { - Scaffold { innerPadding -> + CommonScaffold( + navController = navController, + title = stringResource(id = R.string.app_name), + showFab = false, + onFabClick = { } + ) { innerPadding -> Column( modifier = Modifier .fillMaxSize() @@ -49,18 +54,16 @@ fun HomeScreen(navController: NavController) { ) Spacer(modifier = Modifier.height(Dimentions.MEDIUM_PADDING)) - HomeButton( - text = stringResource(id = R.string.top_bar_add_profile), - onClick = { navController.navigate(Route.AddProfileScreen()) } - ) HomeButton( text = stringResource(id = R.string.top_bar_listings), onClick = { navController.navigate(Route.ListingsScreen) } ) + HomeButton( - text = stringResource(id = R.string.top_bar_settings), - onClick = { navController.navigate(Route.SettingsScreen) } + text = stringResource(id = R.string.top_bar_add_profile), + onClick = { navController.navigate(Route.AddProfileScreen()) } ) + } } } @@ -78,4 +81,4 @@ fun HomeButton( ) { Text(text = text) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/livingai/pages/listings/ListingsScreen.kt b/app/src/main/java/com/example/livingai/pages/listings/ListingsScreen.kt index 2e03230..f8eadf6 100644 --- a/app/src/main/java/com/example/livingai/pages/listings/ListingsScreen.kt +++ b/app/src/main/java/com/example/livingai/pages/listings/ListingsScreen.kt @@ -1,15 +1,38 @@ package com.example.livingai.pages.listings import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +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.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp import androidx.navigation.NavController +import androidx.paging.LoadState import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import com.example.livingai.R @@ -17,7 +40,9 @@ import com.example.livingai.domain.model.AnimalProfile import com.example.livingai.pages.commons.Dimentions import com.example.livingai.pages.components.AnimalProfileCard import com.example.livingai.pages.components.CommonScaffold +import com.example.livingai.pages.components.LabeledDropdown import com.example.livingai.pages.navigation.Route +import kotlinx.coroutines.flow.collectLatest import org.koin.androidx.compose.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @@ -27,29 +52,159 @@ fun ListingsScreen( viewModel: ListingsViewModel = koinViewModel() ) { val animalProfiles: LazyPagingItems = viewModel.animalProfiles.collectAsLazyPagingItems() + val searchQuery by viewModel.searchQuery.collectAsState() + val selectedSpecies by viewModel.selectedSpecies.collectAsState() + val selectedBreed by viewModel.selectedBreed.collectAsState() + val minAge by viewModel.minAge.collectAsState() + val maxAge by viewModel.maxAge.collectAsState() + + val speciesList = stringArrayResource(id = R.array.species_list).toList() + val breedList = stringArrayResource(id = R.array.cow_breed_list).toList() + + // Listen for events from the ViewModel to refresh the list + LaunchedEffect(Unit) { + viewModel.events.collectLatest { event -> + when (event) { + is ListingsEvent.Refresh -> { + animalProfiles.refresh() + } + } + } + } + + // Listen for results from the AddProfileScreen + val savedStateHandle = navController.currentBackStackEntry?.savedStateHandle + LaunchedEffect(savedStateHandle) { + if (savedStateHandle?.get("refresh_listings") == true) { + animalProfiles.refresh() + savedStateHandle.remove("refresh_listings") + } + } CommonScaffold( navController = navController, - title = stringResource(R.string.top_bar_listings) + title = stringResource(R.string.top_bar_listings), + showFab = true, + onFabClick = { navController.navigate(Route.AddProfileScreen()) } ) { innerPadding -> - LazyColumn( + Column( modifier = Modifier .fillMaxSize() - .padding(innerPadding), - contentPadding = PaddingValues(Dimentions.SMALL_PADDING_TEXT), - verticalArrangement = Arrangement.spacedBy(Dimentions.SMALL_PADDING_TEXT) + .padding(innerPadding) + .padding(Dimentions.SMALL_PADDING_TEXT) ) { - items(count = animalProfiles.itemCount) { index -> - val item = animalProfiles[index] - item?.let { profile -> - AnimalProfileCard( - animalProfile = profile, - onEdit = { - navController.navigate(Route.AddProfileScreen(animalId = profile.animalId, loadEntry = true)) + // Search Bar + OutlinedTextField( + value = searchQuery, + onValueChange = { viewModel.searchQuery.value = it }, + label = { Text("Search") }, + leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) }, + modifier = Modifier + .fillMaxWidth() + .padding(bottom = Dimentions.SMALL_PADDING_TEXT), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), + singleLine = true + ) + + // Filters + Column( + verticalArrangement = Arrangement.spacedBy(Dimentions.SMALL_PADDING_INPUT), + modifier = Modifier.padding(bottom = Dimentions.MEDIUM_PADDING) + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(Dimentions.SMALL_PADDING_INPUT), + modifier = Modifier.fillMaxWidth() + ) { + // Species Filter + LabeledDropdown( + labelRes = R.string.label_species, + options = listOf("All") + speciesList, + selected = selectedSpecies ?: "All", + onSelected = { + viewModel.selectedSpecies.value = if (it == "All") null else it }, - onRate = { navController.navigate(Route.RatingScreen(animalId = profile.animalId)) }, - onDelete = { viewModel.deleteAnimalProfile(profile.animalId) } + modifier = Modifier.weight(1f) ) + + // Breed Filter + LabeledDropdown( + labelRes = R.string.label_breed, + options = listOf("All") + breedList, + selected = selectedBreed ?: "All", + onSelected = { + viewModel.selectedBreed.value = if (it == "All") null else it + }, + modifier = Modifier.weight(1f) + ) + } + + Row( + horizontalArrangement = Arrangement.spacedBy(Dimentions.SMALL_PADDING_INPUT), + modifier = Modifier.fillMaxWidth() + ) { + OutlinedTextField( + value = minAge, + onValueChange = { viewModel.minAge.value = it }, + label = { Text("Min Age") }, + modifier = Modifier.weight(1f), + keyboardOptions = KeyboardOptions(keyboardType = androidx.compose.ui.text.input.KeyboardType.Number) + ) + OutlinedTextField( + value = maxAge, + onValueChange = { viewModel.maxAge.value = it }, + label = { Text("Max Age") }, + modifier = Modifier.weight(1f), + keyboardOptions = KeyboardOptions(keyboardType = androidx.compose.ui.text.input.KeyboardType.Number) + ) + } + } + + // Handle loading and empty states + when (animalProfiles.loadState.refresh) { + is LoadState.Loading -> { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } + } + is LoadState.NotLoading -> { + if (animalProfiles.itemCount == 0) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Icon(Icons.Default.Search, contentDescription = null, modifier = Modifier.size(64.dp), tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)) + Text(text = "No results found", style = MaterialTheme.typography.bodyLarge) + } + } + } else { + LazyColumn( + contentPadding = PaddingValues(bottom = Dimentions.SMALL_PADDING_TEXT), + verticalArrangement = Arrangement.spacedBy(Dimentions.SMALL_PADDING_TEXT) + ) { + items(count = animalProfiles.itemCount) { index -> + val item = animalProfiles[index] + item?.let { profile -> + AnimalProfileCard( + animalProfile = profile, + onEdit = { + navController.navigate(Route.AddProfileScreen(animalId = profile.animalId, loadEntry = true)) + }, + onRate = { navController.navigate(Route.RatingScreen(animalId = profile.animalId)) }, + onDelete = { viewModel.deleteAnimalProfile(profile.animalId) } + ) + } + } + } + } + } + is LoadState.Error -> { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text(text = "Error loading data", color = MaterialTheme.colorScheme.error) + } } } } diff --git a/app/src/main/java/com/example/livingai/pages/listings/ListingsViewModel.kt b/app/src/main/java/com/example/livingai/pages/listings/ListingsViewModel.kt index 50c1a6a..f1e53e9 100644 --- a/app/src/main/java/com/example/livingai/pages/listings/ListingsViewModel.kt +++ b/app/src/main/java/com/example/livingai/pages/listings/ListingsViewModel.kt @@ -3,18 +3,79 @@ package com.example.livingai.pages.listings import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.cachedIn +import androidx.paging.filter +import com.example.livingai.domain.model.AnimalProfile import com.example.livingai.domain.usecases.ProfileListing.ProfileListingUseCase +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch +sealed class ListingsEvent { + object Refresh : ListingsEvent() +} + class ListingsViewModel( private val profileListingUseCase: ProfileListingUseCase ) : ViewModel() { - val animalProfiles = profileListingUseCase.getAnimalProfiles().cachedIn(viewModelScope) + var searchQuery = MutableStateFlow("") + var selectedSpecies = MutableStateFlow(null) + var selectedBreed = MutableStateFlow(null) + var minAge = MutableStateFlow("") + var maxAge = MutableStateFlow("") + + private val _events = Channel() + val events = _events.receiveAsFlow() + + private val filters = combine( + searchQuery, + selectedSpecies, + selectedBreed, + minAge, + maxAge + ) { query, species, breed, minAgeStr, maxAgeStr -> + FilterState(query, species, breed, minAgeStr, maxAgeStr) + } + + @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) + val animalProfiles: Flow> = filters.flatMapLatest { filter -> + profileListingUseCase.getAnimalProfiles().map { pagingData -> + val min = filter.minAge.toIntOrNull() ?: 0 + val max = filter.maxAge.toIntOrNull() ?: Int.MAX_VALUE + + pagingData.filter { profile: AnimalProfile -> + val matchesQuery = profile.name.contains(filter.query, ignoreCase = true) || + profile.breed.contains(filter.query, ignoreCase = true) + + val matchesSpecies = filter.species == null || profile.species.equals(filter.species, ignoreCase = true) + val matchesBreed = filter.breed == null || profile.breed.equals(filter.breed, ignoreCase = true) + + val matchesAge = profile.age in min..max + + matchesQuery && matchesSpecies && matchesBreed && matchesAge + } + } + }.cachedIn(viewModelScope) fun deleteAnimalProfile(animalId: String) { viewModelScope.launch { profileListingUseCase.deleteAnimalProfile(animalId) + _events.send(ListingsEvent.Refresh) } } + + private data class FilterState( + val query: String, + val species: String?, + val breed: String?, + val minAge: String, + val maxAge: String + ) } diff --git a/app/src/main/java/com/example/livingai/pages/navigation/NavGraph.kt b/app/src/main/java/com/example/livingai/pages/navigation/NavGraph.kt index 10f5d04..e586682 100644 --- a/app/src/main/java/com/example/livingai/pages/navigation/NavGraph.kt +++ b/app/src/main/java/com/example/livingai/pages/navigation/NavGraph.kt @@ -19,6 +19,7 @@ import com.example.livingai.pages.listings.ListingsScreen import com.example.livingai.pages.onboarding.OnBoardingScreen import com.example.livingai.pages.onboarding.OnBoardingViewModel import com.example.livingai.pages.ratings.RatingScreen +import com.example.livingai.pages.ratings.RatingViewModel import com.example.livingai.pages.settings.SettingsScreen import org.koin.androidx.compose.koinViewModel @@ -56,12 +57,9 @@ fun NavGraph( val route: Route.AddProfileScreen = backStackEntry.toRoute() val viewModel: AddProfileViewModel = koinViewModel() val currentId by viewModel.currentAnimalId + val videoUri by viewModel.videoUri - LaunchedEffect(route.animalId, route.loadEntry) { - if (route.loadEntry) { - viewModel.loadAnimalDetails(route.animalId) - } - } + // Note: initialization is handled in ViewModel init block using SavedStateHandle // Handle new media from saved state handle val newImageUri = backStackEntry.savedStateHandle.get("newImageUri") @@ -89,8 +87,7 @@ fun NavGraph( viewModel = viewModel, onSave = { viewModel.saveAnimalDetails() - navController.previousBackStackEntry?.savedStateHandle?.set("refresh_listings", true) - navController.popBackStack() + navController.popBackStack(Route.HomeScreen, inclusive = false) }, onCancel = { navController.popBackStack() }, onTakePhoto = { orientation -> @@ -107,12 +104,23 @@ fun NavGraph( navController.navigate(Route.CameraScreen(orientation = orientation, animalId = currentId ?: "unknown")) } }, - onTakeVideo = { navController.navigate(Route.VideoRecordScreen(animalId = currentId ?: "unknown")) } + onTakeVideo = { + if (videoUri != null) { + navController.navigate(Route.ViewVideoScreen( + videoUri = videoUri!!, + shouldAllowRetake = true, + animalId = currentId ?: "unknown" + )) + } else { + navController.navigate(Route.VideoRecordScreen(animalId = currentId ?: "unknown")) + } + } ) } composable { - RatingScreen() + val viewModel: RatingViewModel = koinViewModel() + RatingScreen(viewModel = viewModel, navController = navController) } composable { diff --git a/app/src/main/java/com/example/livingai/pages/ratings/RatingScreen.kt b/app/src/main/java/com/example/livingai/pages/ratings/RatingScreen.kt index e5c6b2c..967ccba 100644 --- a/app/src/main/java/com/example/livingai/pages/ratings/RatingScreen.kt +++ b/app/src/main/java/com/example/livingai/pages/ratings/RatingScreen.kt @@ -1,14 +1,14 @@ package com.example.livingai.pages.ratings import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box 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.foundation.lazy.LazyColumn import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField @@ -17,126 +17,144 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.navigation.NavController import com.example.livingai.R +import com.example.livingai.domain.model.AnimalRating import com.example.livingai.pages.commons.Dimentions -import com.example.livingai.pages.components.ImageThumbnailButton import com.example.livingai.pages.components.RatingScale -import org.koin.androidx.compose.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable fun RatingScreen( - navController: NavController? = null, // Nullable for preview or if passed from NavGraph - viewModel: RatingViewModel = koinViewModel() + viewModel: RatingViewModel, + navController: NavController? = null ) { val ratingState by viewModel.ratingState.collectAsState() - val animalImages by viewModel.animalImages.collectAsState() - Scaffold( - topBar = { - // You might want a TopAppBar here, reusing CommonScaffold or similar - Text( - text = stringResource(id = R.string.top_bar_ratings), - modifier = Modifier.padding(Dimentions.MEDIUM_PADDING) - ) + if (ratingState == null) { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator() } - ) { innerPadding -> - ratingState?.let { rating -> - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(horizontal = Dimentions.SMALL_PADDING_TEXT), - verticalArrangement = Arrangement.spacedBy(Dimentions.SMALL_PADDING) - ) { - // 1. Image Thumbnail - item { - if (animalImages.isNotEmpty()) { - ImageThumbnailButton( - image = animalImages.firstOrNull(), - onClick = { /* TODO: Handle click */ } - ) + return + } + + var rating = ratingState!! + + // Define rating fields declaratively to avoid repeated code + val ratingFields = remember { + listOf( + RatingField( + labelRes = R.string.label_overall_rating, + getter = { r -> r.overallRating }, + updater = { r, v -> r.copy(overallRating = v) } + ), + RatingField( + labelRes = R.string.label_health_rating, + getter = { r -> r.healthRating }, + updater = { r, v -> r.copy(healthRating = v) } + ), + RatingField( + labelRes = R.string.label_breed_rating, + getter = { r -> r.breedRating }, + updater = { r, v -> r.copy(breedRating = v) } + ), + + RatingField(R.string.label_stature, { r -> r.stature }, { r, v -> r.copy(stature = v) }), + RatingField(R.string.label_chest_width, { r -> r.chestWidth }, { r, v -> r.copy(chestWidth = v) }), + RatingField(R.string.label_body_depth, { r -> r.bodyDepth }, { r, v -> r.copy(bodyDepth = v) }), + RatingField(R.string.label_angularity, { r -> r.angularity }, { r, v -> r.copy(angularity = v) }), + RatingField(R.string.label_rump_angle, { r -> r.rumpAngle }, { r, v -> r.copy(rumpAngle = v) }), + RatingField(R.string.label_rump_width, { r -> r.rumpWidth }, { r, v -> r.copy(rumpWidth = v) }), + RatingField(R.string.label_rear_leg_set, { r -> r.rearLegSet }, { r, v -> r.copy(rearLegSet = v) }), + RatingField(R.string.label_rear_leg_rear_view, { r -> r.rearLegRearView }, { r, v -> r.copy(rearLegRearView = v) }), + RatingField(R.string.label_foot_angle, { r -> r.footAngle }, { r, v -> r.copy(footAngle = v) }), + + RatingField(R.string.label_fore_udder_attachment, { r -> r.foreUdderAttachment }, { r, v -> r.copy(foreUdderAttachment = v) }), + RatingField(R.string.label_rear_udder_height, { r -> r.rearUdderHeight }, { r, v -> r.copy(rearUdderHeight = v) }), + RatingField(R.string.label_central_ligament, { r -> r.centralLigament }, { r, v -> r.copy(centralLigament = v) }), + RatingField(R.string.label_udder_depth, { r -> r.udderDepth }, { r, v -> r.copy(udderDepth = v) }), + RatingField(R.string.label_front_teat_position, { r -> r.frontTeatPosition }, { r, v -> r.copy(frontTeatPosition = v) }), + RatingField(R.string.label_teat_length, { r -> r.teatLength }, { r, v -> r.copy(teatLength = v) }), + RatingField(R.string.label_rear_teat_position, { r -> r.rearTeatPosition }, { r, v -> r.copy(rearTeatPosition = v) }), + + RatingField(R.string.label_locomotion, { r -> r.locomotion }, { r, v -> r.copy(locomotion = v) }), + RatingField(R.string.label_body_condition_score, { r -> r.bodyConditionScore }, { r, v -> r.copy(bodyConditionScore = v) }), + RatingField(R.string.label_hock_development, { r -> r.hockDevelopment }, { r, v -> r.copy(hockDevelopment = v) }), + RatingField(R.string.label_bone_structure, { r -> r.boneStructure }, { r, v -> r.copy(boneStructure = v) }), + RatingField(R.string.label_rear_udder_width, { r -> r.rearUdderWidth }, { r, v -> r.copy(rearUdderWidth = v) }), + RatingField(R.string.label_teat_thickness, { r -> r.teatThickness }, { r, v -> r.copy(teatThickness = v) }), + RatingField(R.string.label_muscularity, { r -> r.muscularity }, { r, v -> r.copy(muscularity = v) }) + ) + } + + Scaffold { paddingValues -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + ) { + item { + OutlinedTextField( + value = rating.bodyConditionComments, + onValueChange = { + rating = rating.copy(bodyConditionComments = it) + viewModel.onRatingChange(rating) + }, + label = { Text(stringResource(id = R.string.label_rating_comments)) }, + modifier = Modifier.fillMaxWidth(), + minLines = 3 + ) + } + + items(ratingFields.size) { idx -> + val field = ratingFields[idx] + RatingScale( + label = field.labelRes, + value = field.getter(rating), + maxValue = 10, + onValueChange = { newVal -> + rating = field.updater(rating, newVal) + viewModel.onRatingChange(rating) } - } + ) + } - // 2. Description / Comments Box - item { - OutlinedTextField( - value = rating.bodyConditionComments, - onValueChange = { viewModel.onRatingChange(rating.copy(bodyConditionComments = it)) }, - label = { Text(stringResource(id = R.string.label_rating_comments)) }, - modifier = Modifier.fillMaxWidth(), - minLines = 3 - ) - } - - // 3. Rating Components - item { RatingScale(R.string.label_overall_rating, rating.overallRating, 10) { viewModel.onRatingChange(rating.copy(overallRating = it)) } } - item { RatingScale(R.string.label_health_rating, rating.healthRating, 10) { viewModel.onRatingChange(rating.copy(healthRating = it)) } } - item { RatingScale(R.string.label_breed_rating, rating.breedRating, 10) { viewModel.onRatingChange(rating.copy(breedRating = it)) } } - - // Physical Attributes - item { RatingScale(R.string.label_stature, rating.stature, 10) { viewModel.onRatingChange(rating.copy(stature = it)) } } - item { RatingScale(R.string.label_chest_width, rating.chestWidth, 10) { viewModel.onRatingChange(rating.copy(chestWidth = it)) } } - item { RatingScale(R.string.label_body_depth, rating.bodyDepth, 10) { viewModel.onRatingChange(rating.copy(bodyDepth = it)) } } - item { RatingScale(R.string.label_angularity, rating.angularity, 10) { viewModel.onRatingChange(rating.copy(angularity = it)) } } - item { RatingScale(R.string.label_rump_angle, rating.rumpAngle, 10) { viewModel.onRatingChange(rating.copy(rumpAngle = it)) } } - item { RatingScale(R.string.label_rump_width, rating.rumpWidth, 10) { viewModel.onRatingChange(rating.copy(rumpWidth = it)) } } - item { RatingScale(R.string.label_rear_leg_set, rating.rearLegSet, 10) { viewModel.onRatingChange(rating.copy(rearLegSet = it)) } } - item { RatingScale(R.string.label_rear_leg_rear_view, rating.rearLegRearView, 10) { viewModel.onRatingChange(rating.copy(rearLegRearView = it)) } } - item { RatingScale(R.string.label_foot_angle, rating.footAngle, 10) { viewModel.onRatingChange(rating.copy(footAngle = it)) } } - - // Udder Attributes - item { RatingScale(R.string.label_fore_udder_attachment, rating.foreUdderAttachment, 10) { viewModel.onRatingChange(rating.copy(foreUdderAttachment = it)) } } - item { RatingScale(R.string.label_rear_udder_height, rating.rearUdderHeight, 10) { viewModel.onRatingChange(rating.copy(rearUdderHeight = it)) } } - item { RatingScale(R.string.label_central_ligament, rating.centralLigament, 10) { viewModel.onRatingChange(rating.copy(centralLigament = it)) } } - item { RatingScale(R.string.label_udder_depth, rating.udderDepth, 10) { viewModel.onRatingChange(rating.copy(udderDepth = it)) } } - item { RatingScale(R.string.label_front_teat_position, rating.frontTeatPosition, 10) { viewModel.onRatingChange(rating.copy(frontTeatPosition = it)) } } - item { RatingScale(R.string.label_teat_length, rating.teatLength, 10) { viewModel.onRatingChange(rating.copy(teatLength = it)) } } - item { RatingScale(R.string.label_rear_teat_position, rating.rearTeatPosition, 10) { viewModel.onRatingChange(rating.copy(rearTeatPosition = it)) } } - item { RatingScale(R.string.label_rear_udder_width, rating.rearUdderWidth, 10) { viewModel.onRatingChange(rating.copy(rearUdderWidth = it)) } } - item { RatingScale(R.string.label_teat_thickness, rating.teatThickness, 10) { viewModel.onRatingChange(rating.copy(teatThickness = it)) } } - - // Other Attributes - item { RatingScale(R.string.label_locomotion, rating.locomotion, 10) { viewModel.onRatingChange(rating.copy(locomotion = it)) } } - item { RatingScale(R.string.label_body_condition_score, rating.bodyConditionScore, 10) { viewModel.onRatingChange(rating.copy(bodyConditionScore = it)) } } - item { RatingScale(R.string.label_hock_development, rating.hockDevelopment, 10) { viewModel.onRatingChange(rating.copy(hockDevelopment = it)) } } - item { RatingScale(R.string.label_bone_structure, rating.boneStructure, 10) { viewModel.onRatingChange(rating.copy(boneStructure = it)) } } - item { RatingScale(R.string.label_muscularity, rating.muscularity, 10) { viewModel.onRatingChange(rating.copy(muscularity = it)) } } - - // 4. Buttons - item { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = Dimentions.MEDIUM_PADDING), - horizontalArrangement = Arrangement.spacedBy(Dimentions.MEDIUM_PADDING) + item { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = Dimentions.MEDIUM_PADDING), + horizontalArrangement = Arrangement.spacedBy(Dimentions.MEDIUM_PADDING), + verticalAlignment = Alignment.CenterVertically + ) { + OutlinedButton( + onClick = { navController?.popBackStack() }, + modifier = Modifier.weight(1f) ) { - OutlinedButton( - onClick = { navController?.popBackStack() }, - modifier = Modifier.weight(1f) - ) { - Text(text = stringResource(id = R.string.btn_cancel)) - } - Button( - onClick = { - viewModel.saveRatings() - navController?.popBackStack() - }, - modifier = Modifier.weight(1f) - ) { - Text(text = stringResource(id = R.string.btn_save_ratings)) - } + Text(text = stringResource(id = R.string.btn_cancel)) + } + + Button( + onClick = { + viewModel.saveRatings() + navController?.popBackStack() + }, + modifier = Modifier.weight(1f) + ) { + Text(text = stringResource(id = R.string.btn_save_ratings)) } - } - - item { - Spacer(modifier = Modifier.height(Dimentions.LARGE_PADDING)) } } } } } + +private data class RatingField( + val labelRes: Int, + val getter: (AnimalRating) -> Int, + val updater: (AnimalRating, Int) -> AnimalRating +) diff --git a/app/src/main/java/com/example/livingai/pages/settings/SettingsScreen.kt b/app/src/main/java/com/example/livingai/pages/settings/SettingsScreen.kt index a0b16a3..de5edd0 100644 --- a/app/src/main/java/com/example/livingai/pages/settings/SettingsScreen.kt +++ b/app/src/main/java/com/example/livingai/pages/settings/SettingsScreen.kt @@ -1,5 +1,6 @@ package com.example.livingai.pages.settings +import android.app.Activity import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -14,13 +15,14 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource import androidx.navigation.NavController import com.example.livingai.R import com.example.livingai.pages.commons.Dimentions import com.example.livingai.pages.components.CommonScaffold -import com.example.livingai.pages.components.RadioGroup +import com.example.livingai.pages.components.LabeledDropdown import org.koin.androidx.compose.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @@ -30,6 +32,8 @@ fun SettingsScreen( viewModel: SettingsViewModel = koinViewModel() ) { val settings by viewModel.settings.collectAsState() + val context = LocalContext.current + val languageEntries = stringArrayResource(id = R.array.language_entries) val languageValues = stringArrayResource(id = R.array.language_values) val languageMap = languageEntries.zip(languageValues).toMap() @@ -47,13 +51,17 @@ fun SettingsScreen( .padding(it) .padding(Dimentions.SMALL_PADDING_TEXT) ) { - RadioGroup( - titleRes = R.string.language, + LabeledDropdown( + labelRes = R.string.language, options = languageEntries.toList(), selected = selectedLanguageEntry, onSelected = { selectedEntry -> val selectedValue = languageMap[selectedEntry] ?: languageValues.first() viewModel.saveSettings(settings.copy(language = selectedValue)) + + // Restart activity to apply language change + val activity = context as? Activity + activity?.recreate() } ) diff --git a/app/src/main/java/com/example/livingai/pages/settings/SettingsViewModel.kt b/app/src/main/java/com/example/livingai/pages/settings/SettingsViewModel.kt index d8999e7..5474798 100644 --- a/app/src/main/java/com/example/livingai/pages/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/example/livingai/pages/settings/SettingsViewModel.kt @@ -1,22 +1,31 @@ package com.example.livingai.pages.settings +import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.livingai.data.local.model.SettingsData import com.example.livingai.domain.repository.SettingsRepository +import com.example.livingai.utils.LocaleHelper import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -class SettingsViewModel(private val settingsRepository: SettingsRepository) : ViewModel() { +class SettingsViewModel( + private val settingsRepository: SettingsRepository, + private val appContext: Context +) : ViewModel() { private val _settings = MutableStateFlow(SettingsData("en", false)) val settings = _settings.asStateFlow() init { getSettings() + // apply locale initially + settingsRepository.getSettings().onEach { + LocaleHelper.applyLocale(appContext, it.language) + }.launchIn(viewModelScope) } private fun getSettings() { @@ -28,6 +37,8 @@ class SettingsViewModel(private val settingsRepository: SettingsRepository) : Vi fun saveSettings(settings: SettingsData) { viewModelScope.launch { settingsRepository.saveSettings(settings) + // apply locale immediately + LocaleHelper.applyLocale(appContext, settings.language) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/livingai/utils/CoroutineDispatchers.kt b/app/src/main/java/com/example/livingai/utils/CoroutineDispatchers.kt new file mode 100644 index 0000000..79ba290 --- /dev/null +++ b/app/src/main/java/com/example/livingai/utils/CoroutineDispatchers.kt @@ -0,0 +1,16 @@ +package com.example.livingai.utils + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +interface CoroutineDispatchers { + val io: CoroutineDispatcher + val main: CoroutineDispatcher + val default: CoroutineDispatcher +} + +class DefaultCoroutineDispatchers : CoroutineDispatchers { + override val io = Dispatchers.IO + override val main = Dispatchers.Main + override val default = Dispatchers.Default +} diff --git a/app/src/main/java/com/example/livingai/utils/LocaleHelper.kt b/app/src/main/java/com/example/livingai/utils/LocaleHelper.kt new file mode 100644 index 0000000..927ceb4 --- /dev/null +++ b/app/src/main/java/com/example/livingai/utils/LocaleHelper.kt @@ -0,0 +1,24 @@ +package com.example.livingai.utils + +import android.content.Context +import android.content.res.Configuration +import android.os.Build +import java.util.Locale + +object LocaleHelper { + fun applyLocale(context: Context, language: String): Context { + val locale = Locale(language) + Locale.setDefault(locale) + + val config = Configuration(context.resources.configuration) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + config.setLocale(locale) + return context.createConfigurationContext(config) + } else { + config.locale = locale + @Suppress("DEPRECATION") + context.resources.updateConfiguration(config, context.resources.displayMetrics) + return context + } + } +} diff --git a/app/src/main/java/com/example/livingai/utils/ScreenOrientation.kt b/app/src/main/java/com/example/livingai/utils/ScreenOrientation.kt new file mode 100644 index 0000000..371e891 --- /dev/null +++ b/app/src/main/java/com/example/livingai/utils/ScreenOrientation.kt @@ -0,0 +1,20 @@ +package com.example.livingai.utils + +import android.app.Activity +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.platform.LocalContext + +@Composable +fun SetScreenOrientation(orientation: Int) { + val context = LocalContext.current + DisposableEffect(Unit) { + val activity = context as Activity + val originalOrientation = activity.requestedOrientation + activity.requestedOrientation = orientation + onDispose { + // restore original orientation when view disappears + activity.requestedOrientation = originalOrientation + } + } +} diff --git a/app/src/main/java/com/example/livingai/utils/SilhouetteManager.kt b/app/src/main/java/com/example/livingai/utils/SilhouetteManager.kt new file mode 100644 index 0000000..edf049f --- /dev/null +++ b/app/src/main/java/com/example/livingai/utils/SilhouetteManager.kt @@ -0,0 +1,77 @@ +package com.example.livingai.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import androidx.annotation.DrawableRes +import androidx.core.graphics.createBitmap +import java.util.concurrent.ConcurrentHashMap + +object SilhouetteManager { + private val originals = ConcurrentHashMap() + private val inverted = ConcurrentHashMap() + private val bitmasks = ConcurrentHashMap() + + fun initialize(context: Context, names: List) { + names.forEach { name -> + val resId = context.resources.getIdentifier(name, "drawable", context.packageName) + if (resId != 0) { + val bmp = BitmapFactory.decodeResource(context.resources, resId) + originals[name] = bmp + val inv = invertBitmap(bmp) + inverted[name] = inv + bitmasks[name] = createBitmask(inv) + } + } + } + + fun getOriginal(name: String): Bitmap? = originals[name] + fun getInverted(name: String): Bitmap? = inverted[name] + fun getBitmask(name: String): BooleanArray? = bitmasks[name] + + private fun invertBitmap(src: Bitmap): Bitmap { + val bmOut = Bitmap.createBitmap(src.width, src.height, src.config ?: Bitmap.Config.ARGB_8888) + val canvas = Canvas(bmOut) + val paint = Paint() + val colorMatrix = android.graphics.ColorMatrix( + floatArrayOf( + -1f, 0f, 0f, 0f, 255f, + 0f, -1f, 0f, 0f, 255f, + 0f, 0f, -1f, 0f, 255f, + 0f, 0f, 0f, 1f, 0f + ) + ) + paint.colorFilter = android.graphics.ColorMatrixColorFilter(colorMatrix) + canvas.drawBitmap(src, 0f, 0f, paint) + return bmOut + } + + private fun createBitmask(bitmap: Bitmap): BooleanArray { + val w = bitmap.width + val h = bitmap.height + val mask = BooleanArray(w * h) + val pixels = IntArray(w * h) + bitmap.getPixels(pixels, 0, w, 0, 0, w, h) + for (i in pixels.indices) { + val c = pixels[i] + val alpha = Color.alpha(c) + // simple threshold: non-transparent and not near-white (assuming inverted is black on transparent/white) + // The inverted logic makes black -> white, white -> black. + // Wait, if original is black silhouette on transparent: + // Inverted: black becomes white, transparent becomes... white? + // Let's check invertBitmap logic. + // Matrix: R' = 255 - R, G' = 255 - G, B' = 255 - B, A' = A. + // If original is Black (0,0,0,255) -> White (255,255,255,255). + // If original is Transparent (0,0,0,0) -> Transparent (0,0,0,0). + + // So "Inverted" means we have White silhouette on Transparent. + // We want bitmask where the silhouette is. + // So we look for non-transparent pixels. + mask[i] = alpha > 16 + } + return mask + } +} diff --git a/app/src/main/res/drawable/angleview_silhoutte.png b/app/src/main/res/drawable/angleview_silhoutte.png new file mode 100644 index 0000000000000000000000000000000000000000..b58faefa319f068081a933928564f0cd9337aa07 GIT binary patch literal 6087 zcmY*-cT`hfuyqoG1OyF&(jgE5`O%B?UX@Z$5nc!^cIWQD)rb%n+B!y9kcA>ZU}*L}@Cbx6rbUo)E?jEh z1%Y7*QY(!Z2Xtk~zsup=sWxoLlNqL-{}i&Fm+BIkKqC|pJj{_bFEcaecx>#mVOJYE z;JG5Q+Xw~2?(ku;_aLYbGr@-Q9rVA$+@_T|MiK?!Saksi3?opTkog)zOfkvox#vhf zat#8%rH56Z_=;FJT+aybd@*QSjJg5XQbBtO9578Vi?%O+u(9RA-1V!s*VPf>AP6_O z>P_(n3qfLZTrAIh z9bn(tab@ao9?tZk!a;DRkZqplYEfFNMec%~d)ZU%!IOB)2VtkZ4q-9bup0#f)E{m8 zShDhJWP(O)UTw8KDwD2IIDb)eDGdOM8Vn5qza23+G~Y6-ZHhFSrDbgRhO&y|sjm@O z+&=r7l2bNl<-;KP=aLZ!EY9<+qI5{bhN(Z0OCfR9$$en|W4U+J!+Iar7SH;eDNVXC z#%R+wL&;D$rN+VJ1smSfPHJYlPKRO3`1VtDd|wO*I7%e>pm$9j(&kb1+i^_Px(HUB4aAS3Jn7*U{A$}bd0-eWb^*@a}|rjS|%C#D0F%rzb*&oIBq`A2)q zFwaavLKgQA)`(ZApHt1)4ws%AGw7?lr`R&#LRH#k&afWOZ5DhvHe+u&8G=&<5;3&r z@#gM)7E9ZgsA1xZql2WLe9sPV)@!_Z-8Or9a#@CJ<)_P~P1-nh4uCkai}0l=Ls8RC z+Fq3SPKW)OpR0bPP5nhzB{`*GUT7wQ@L!5RC;S%_!W}g{eSb5)%^9HH&*Q=!SEpa` zICb|35F3W$Y3vpRoK#Ta(IWS~r2Hgra4Y0bl#+p;NLdaR^xpJ}db#JKTk&~E#X8jw z6tX(oB&{)9a>h31^zp>IEpg~qfTLqxY^KoqRCc zM-H!6<_(Ezazyd0jiRo(_15(^)PLsn|FTuBxIwsQ*WZjz3!9##xAnk zPZ8w0;9Za0_9lPa4jgtJF(Am_{@H>+trh+U@qDJ}TW`;~bHXpJCDgV5M_ z)}lo?^2K5Y@jhcF@Mf8W-ha@!P;XW0kga8hv}tk;53=qkXKs|z&#r}p+rnHq?jU@6Ks?bmy7FaGKa@6;x+mm5J~ zUW9P|87M9YAWG1>R?xE{napz#I+kHt9l;N-p`^&XuK#D>|GlqWm=9R1eFYyKu=xV#Q2!@^D8EFSQc-tT3i|Lt;n zC}b7Z`y)L?AL^{KrS$r!*jfe0lO=HZch)Eb@_;s<_rX>rhaeToZEW#%u(dB;UbFy0 zZBM-nK(z|0T0Ns=*rG%=^OYW=05lva%sLVrQ7@Y$~+ku9&v8pJ&^a3GhAz(EM z>taBV7S_UN=#+OpgOYtBdZlvFbNf%3jb`ZxH(*ySaNOa~fi$oQr|>h{4NQB}K|=rtRa2zKH3-t38Ue`GA$iB(YhOkYu*N zX(7}~^M9;n{}z2YC7zxDSe(RHs31spm9pAlfIX*1o^(>2vRJ7j zdUZ$?o{YN*kj%rGE=P}W5x3cx6{EPvHL-IAK{Y%u z;m!pP;A3xxupLm)I7^T->HWig8&V+2MSg$o;xHXqB|PvXY$iPv#XWX^mskD5rq**s z=Y>|9ROrup7&w{#b!QbLrIVnvaS2S8@nZYodZvW5L5;y0#mhdd*|G*7#Gk>y%N{SjC~JP-)IZiN=K_1fXtQE01L+Lk;L zNV|BiP$ML%{cR~xhK`I=0G=o$=o4WWeoBsi%i1^etg#7}URlNBqYl(2!SnY8mKbi8 zh&(0)Ro+;sZ#B;W5FM(5r>h8WWkicSjy#$&6FsT#Pi1~7t zdpm>i_qoH!y-wHOvqO{z^|kQLJ`iQnBV(u5rF%okj9dLr1*xhyv576`F2wewAZ2j3 zQm#WCHB^~>q549)KAY^{!LWuaLD1IMxBjlu4A33NTbECS;Mfc#%z~lHjJ+B|UIEML zIJcv>FB*}g1Zbg?oIlrb;+g{>%eor*(NS1IJ;m%##25}qs`ks!$3Xd;iPajmlSugL z^S3iZjbl9iZW)kkWzQ4c(Q?bxF(``s1Or^(khKJwc#s;yJ<#Irqd|aLx^o#Dt;-}_7OE=Hf;y4~3 zdBfvIaU-%lI!5XcW|GXrx#wo^Zw}aWNIqs2?%63ING|irNYBqr02_FJZ1AU_#hSB;oi4}GxalrVre#d|y zb0+0Ml$1aUcOmo2(!kRp_Z;ACa$gG6M}i(;c*s;uSyDd}`_qnC=UDCdNKV~NKW}v0 zt&u*WTlI!G^gvcj5J4kj^2UUTE3AWS6|=!LZw_l0{oQ z|E*c5Ai=p&FBvxp#ergRss}aEcEx$Q5?Lp6ziZw)E9NN%&iTGQ9J#87dZ0*LQv}jf zakgP@O~n;^@~BOvpD(kfFXa`iVa=_-PSeAXTU5zsZ|d{ajhL?Y#cc!gIxMY&8l)ah z-46=NV8-HHXh`l0v55U(AIIsrbguJG&Vi8XkTStzxgl3!C7-SEsmP%Z^fTG@*W6!) zuPx~XjZTMtnLJozRLXv*u0Gf5DRJwNyYbFKDCcxh?7g@TcxF#Lw2us>dAWd$2`awds*BiV$*4C}kP&`emMb1`t$L*6iT2y7`#tQP$s)6mx^8|c z%9Rx2I`aB^Yb3ws3#5hd>QW}TZ1`dfyu&51u;St4&Br~BcY#7{7rhhP_O=-*?UtIh zq*b6@&U5j%cDr=-)e3B0E!&b68$d|5FUY;cEA%`93ZJco@2j4rzI(H4`=tG8s2e4@ z^Dw7S%u*VV5z?Jpvvn}tLGiyonbEWfccIpFC41sDd6z91lF)TfCubC#^5RWi*t%-x z(?NWgg1`5#>{(|fb91Au$iF#-3A$b#i=ze0S_@3vd!^=Bz%|e|!S(<~lvL*VXKYi7 zjd|8s-e`7Gr%MQ>o|stv@mbFxMi0nCy$@bej9}Eleg8CTLg;d2n(D_rD`~e4;YL-+ znFH0LR>ddQ?>x|uZ@p=34OLzuZHzA{qT3-?95|Vx?SY34iswV1L zy;1>`)!cEs7P3*5ht4}jw1^Sd35lDm=3oai(l?}UoYHZtS$@lTcLe)>r{Pyy5>~C0 z6FX2WAlqMAR>_aWdOSBn8IYd2P#Nus7Nbn3187se+I;_`>iKcW@4Fu1ee}uE7e>lj zAmu+5@Wzm`6*$anwZZ)3aD_SJL#lH7VOY(X>&k@^T9t%!2m6 zmiDB^X35+E2*ZIyJSF zW5rMFe7c(Updv?lD&-h7NLk}u2`sU)r4mgDxZs*EN^67f+Ti(}R)P9$s zC2hX?{CWX@DVc0G>VT90m{{jN2JTT%73j4nA`{O-wy8ANf& zg5BtfFzFLY+JuQIFMclY{Gj5lk5Vdf#C3gOucED<7E?*j2}b|6#bV`{;~82OkMBRI zAUr}dO^l;euAjR+rPQW+*i6HKYc&$LZqKUv0250%ifc!=T{3N7DcsO4PNzQ^+7FTs zrId`7$Xnn6>f=u^r_IX^TGZnWIq0@rzMaC)Q|Ss`&{;@*kpT>2FgjT;Ua2IaMZFg; zSYH)5zE+gEreW74MQI2E%Y^5_4q#?aoZ>5I+Q}Dv$9Dg=_N}*#ebU{lUsrNvXe9Ac zk$)P6V*{>~GBmDU@tXO1bAl3(Nau}wvHZj|rb$3-CG$M>6EDdqa&iL-*>u%Tkb&AH zsU|Q|@RwK`QbPy4I^5WN22UE&KhhqYTOIxSAC3g1NmHeL7p_sVXRVG>tD6333?|T}kyN$}e$Bq_oy}Y}#8De^mWUdz7Iu`23(}L~VuD zWSz6oh|oviqWph?v{Yq6&axN^!iM61SrRdhQj`_0`Uf=|`Q*yknO2suQiCXA)L0^0|;O_}+yl)`#{tLmK7duxfw6OD;)@ z9+qd1H#!%TA^&T$@643*Nmx+EoR>?l)VDOgp0$2TUShS;ST|5Cy458c^hp`tg(t+i zfk4DHoTSWSW^}~W)~Bq0V?viF_H$KDD}Z@bE6BMSlZF+p#b5QZK2DFXdrdiXonIt( z&?s)ijXIBiycGJ5s45M#b!q#-4g-Q&eO)`07Y>hZ z4+qWvE!X=v2ski^l{^;%=M9RLVDTGjzMS}A<10tpG641 znq0{_c0J79G_7C;;$~ibdc4)w9`)t>Ep0JyfT&aax@w@$d?0$Y@y_1H@r1fJE32|G z2(0!cs&*B2Fc@ZlzK3i%G0{8DZO4qccALoeY&N$BSjyYz0hg-R@1n73d@4`W+SwZ6 zae=>64DxL!tmBgJ-f(Kq-aXwmKR7tU`WlGHZCr%3uTt*vWRp+!fBycyim)dmM_x|t znK~E6cy52wcwoDYopR2#mQ_gg@qYZy?NY>m@#uCo(Dp9E?b#f_y;K1OeeKj^mbFo_ zkyR4-UZV5+;=oZpweFM8U8(P#4&lj%{&|-PHmde=$v_83$Y%yJQUbl_W=AGg#F>fh zi%UUYa|;KIEZZD)k@P;UM_G{ZetYMPmmv@fL~#7G?5|j~+`TU04 z5-|eI>00p;eyh&CdA|z!B3qL}@q=6g-=NfxA56gb=@>4n0NRj}XWTU1YA?T@pObbk zvqTZv&xoKBCkDOEm6>WVtnKCWe5inC-+Y~*r6!W{!;}jW2=vpy!^DTSO8Z=jWR(xE z_p6J$_exgr&G24*tWZihCPt?6bW!TUR~Hwjt%?$t$o>VhP#Iuc_K4> z`@E6mtAPD|8Js#B@Slr3x-D&FT}c2rUhbQ5Kpy;5J9cpo{yPT{tijM33JO{L(r;kz z6gHuyCh>M4{c-{<&7BVS7m7)@3@S!ir39`AHvMRocdBo>bS$18I_Ue{{h2LoMkOOv z%UU@F0?7g7Fj*N()y(rMl*~}&LnYvP=dE8wmfUO=>1mOUx mAX>hWo&f@%{6E$wYO8Rq)1!nd%fMtFbX&(*yHe9W{Qm)DN^so( literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/back_silhoutte.png b/app/src/main/res/drawable/back_silhoutte.png new file mode 100644 index 0000000000000000000000000000000000000000..1b629ca940c8cc98d479fdeacf00490c882579cf GIT binary patch literal 5005 zcmeHrc{mhY^#9B-WtZ$*-V#FA875neB}F0002Hk)fUi zT@nBQAe`wOUE^)GPM{0s07F|O0Kn1ncK}KLr7i#f_bnqmZL3h=cCKl>oB=QCN7-@q zwKXubD@*q33k83EbH>N|JZTpYlC0p{y|K2f12Tb9hM2@VosSku%NhB#Gn)NZuV}&~ zIVH5EuCD2FODN^DhEF{+iOb8gTfG0yY*Ovr$yYMcf0|TgdA7fa+&tWFJ{tb=<;!Y~ zb2P~Q+J7nXZYU!-${7rRnp5ro0gLx$4UP_N@6wYY*KO;7?Ce+SrR`i<)BR7|4XG9;_N}@EeCb{t!SF?ZcyhRiN_=DGzCs z>xAEuQFZ&zDV3-+vON&VRq7rb4j52Y<0`~7}Q}9uB0r=I%o}lC!}xW;kgr) zz1i-6Y*3laqIO61Y*bujUuWn&dI4Htsfi6=;`6J_TQPu#aPJB{SdT{;SOAXcz0U#U ziZuav051axQSmyG076tM9in%cn?4%?AXZBaT~VHZ z5uV+lnb6Dv;?A_lz;}vCd(9q>$S6%OZWbj&!rAHp-UXyY12lqKo>of zd(tu2lb$Q&fzeL^Wj30XJ6$gr_)z^P&n|l;P7aXS_QW1jwKG*&+AgA%!ly-*gM}}I z3H1eIh)mEaGI0;qo>lvg{1(2FYuq z!Sf>7KS!;$t^-W-Z)Bf=xGkAxgZd}b=Ro9=(Ez)OrvLh_eqRS9AJnt@6`5fl8rT7Z zGPepVn#vT@i{${xoK_0~!cQiK%PUmrY~IsH+|No7j|%BawsjwA8}eeH8Ps2X;oQuU zH84I0U%{OqYis8(!Q*C@rmMC<*v;&O)7dmUvIl*r1UC6iN~1d=D{20IgFDVQ_bsSJ zAD;Icc0QSm&2)NGIn;oPD4*0*GUEnM)Du+6$ELfIIV!?>;i8r7&pttF{IaNVd zw+jF*agO(Oyyj;+9)Mg?NO0CYIBlK=q?!bqzo`T$2NPW=k8FVlj)OXAVp81QuU7R0 zN%FCIpHz*pLwQw^3aVeCRRa2as#;Z-!6Uy@{#oV8S?mDWcv(BV3Z(EoB-MwrKw>gR zj(-0~N7L^&2c#-iPzWaJso(KsrhJ56&4f!<Qw*R|DYfO&0p2PfnbBi0 zs+rt#lO8(G&YQ-l&F)~U$O#9C?(Pw<@v!8z0&K1{2n&xq&lLQwGj|R$&UnJNJ?Q*A z5bVd-nB{4XWH+sH63rD`;$9Ky$8|#+&1QEcFFmrOn_-k2@7OIU)bZihF-RX zN=W(!g%s`0kPJifT}$254yS3FL|5s0??j7*QY$<0$_6$9Q=?>_?(naHF-Ig8kq748 zAo9_}3!s}k(vKviJspX{R=umO+RXW{UV$4;+qxQ4@zODFqXGGvWnre74yK)&r2(!= zq8v~$&hzg7QOtltqu8w+f%&1=(S`A@aT=KF=ZbFM&cn{HS6o*c$ftElYqs25iks4G z$iO#h=Zx8LGyNHsfh!!9sM5>5GebGR%?OxYU5bg41ezuicxK;B`OA&Y3z65RT==5h zI;C3b9)}b?Ni7y_%)s7WO?2iFGj7ZIuWuaJeK{#ukn%eq$^gl70qzF#Hs-jPc=L%4 zhM17FUfloBY(j=8i=2oU$LR8>Bv47F5JMwhrid=%Aj~I(M-j;|o^zGH{=OSB>N%le z6IEAVCY-v)Q&ZEMbrPjQMfA>1XSV>QO(KUr9)}Wx7HTGu{QjDAkYd%smks-X<~|9M zs29Gcvz6y~txBidbtJaMhO>f`%q94(Rh{S(gm_1y6gyulKQ}Njw z;u?_H@&$H5>}AC|h%Q0jJ{2#hLpSPqPk;s0E$K*;E+Lo!dX(pP)$pOm7i*x6;9U2J zQ4@2ST!C6qy9PAm0Jk-lxlL5tDD*{w5w7kQ2K4;Hun??DLHR+wDmfIA)rYy`>_TCi zP0NM!T6iV02v*ihVXJ%fanpd41RsS@+Vol`oLfsN2U61!?w_ig zK|9L9o49lbmdnk=XAczru>qcJu-MpbvL$bAo#%Y|k^MDMh5X>wDR6EvNSA@~^;g1= z4XxW~Gx*XqJUe#A93ZHEnZtnDm)wg6A9 z)_|)v+$C8_0S`mjm_Lxhwtvp++Cs97lB!?vLViAH>$~v?apZK^PmKF9fnhy-OwpLZ zljfT_3{GgiPLZ0J)gMxNW!sd;dYU8O97h@DrxmIqa<g(`uic3 zD8HLsF~A?7MkC5y( zoT^pGeA8rO*s>2_M#(DL3SEL%rAi^o)9{&QR1^Mrs6RV3R`v6ckgIe6C!;sg+vOzX z=AdtnqBR4hm^*jbjV51-`ci$Xc#F#dWI!H|6KjmiH3Q78|5~jv_UB}bc4R+&*Ap?a zXEs*mbIhZxS=k|%OPLg1;(%*=Hi!k~R?O^@*nc=0{}7g;?EAiIA&ipG^TsHsWWUB6 zlS&uQ=2#ehzkM;XA!)~8$~{+ov%C$s&_PHkc@{!NY^)1P>&k6=0BCY+jO(i{{b;s! zyUB-PyO&H$tr#e{*yowb3g>l01ZBM6N#f!`!@)g842Ltb9+adm6G#>`e31&Np_*<O?2ZT1t~ssw^rEg&2W0ggmZmFq56&Jhm11*a;K7xv_=<>%m$r_Kz!91=>7f3kdv6eU#m-iZl zMe>db2^AYVV^4$M_e8o5|FSwkr&MJO&mKLL+N*x>wsQKH;}jx@?Vt3}(;n?mlfh1D zUwz5DTH8t*pRX2I1?OBGHb#L9jLLmy;ws=I<9T0WcD@Y>2P{9@yj)((_Cxhrb7!Y@ z)M)OIWMj`;n!6-POB9n=(fVb&>63dUeEL{SQ&#)T2_+s(%G(qs0)n-v$svn8$E)fZ zpWE6&rX61mBeR6tpKUyu=P}trg2PRN57>f@mckr@GAKW9a$E7;02Z+JLKfbfZX87P zAnyc?K*;%bu*>WE8LW$zxfe9gzWpI9!tem$TP4I68ct<{K5cJAvyFrwd^?2neE17+ zVTU6dmOB)HWDrY0{U8b4PxO1y`hCgEOP_O05Q{Tyn*C@(z9_N`8kvN46|0&~F+pK7 zzE`e5Solp`JOpXB)=k>F=#-E2ZNJ*U5g`aP%8$+V<1!tG5ee*ecxCge)K;ASXbV*R z8c)~v*q|^RfN}$eFkRsjp041cs9aZba1M%L8{si46o%m{F6$yuYhE&3ce#YUhsoXm zEUTn4VZgHw(#<|s-nNW5_~zzuG*_Xd+sgT{yQ~jz)?T)py&Q*X%fr9z1+VP;g8EFp;fwiK4z z1Q?HvJt^M!p%d+%DP&veC`)d4qf}n-@T(`-6o|>PEfmGlDnriFQk{>OXFdJ;WyxpP zt%kOZH@e%z982$3%G(p)rig!`dpx^l^o^Y|XkK z#$TQp^sWK4=#B2Uoh@~+Jk0`S{<@-mqN-i^c53B?yd3T)Q}Fu9GxK7D0$9&?#!L2Y z|Gt7}{*C{rX)Bd6j668Gf<@`0^9&C1etF>me?(HPi89<&i-v_AMD7l?FLQYLvn>(1@)w5StiVW4>3D~IJc&~I~7;$~x4)X`7 zJNAGdAZn>!^IX>BrHR%xpSzz$fY*L~Iy!<%^@z^T<8;V)#qxbGqhm z9U#AZZm-^BXHgaaut6Gk3`c@Sh6BcwWfEsD)ok|#h<2MCbEU-8Mh2Y}<4!6Iqq z0Jw1}ldv28W64biXmFJ-FUoYI1;GG9ws|KPGhN0c);!h+0{;KUk8jNhdJU_rYb(?L P*8oPh&Gf2t9=-S<;&fAe literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/front_silhoutte.png b/app/src/main/res/drawable/front_silhoutte.png new file mode 100644 index 0000000000000000000000000000000000000000..6c4290680a1083ca526976b4c8391b235634a389 GIT binary patch literal 3772 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 J%F?pF@jn1{YQ6vf literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/right_silhoutte.png b/app/src/main/res/drawable/right_silhoutte.png new file mode 100644 index 0000000000000000000000000000000000000000..e974e7a49a0c01215331009d84ca12ff30f20d32 GIT binary patch literal 6115 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

ipEC1ydNy=SN3Vs!5NnF^OO+(w%FGC4y3E?ZL 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 z z#|FH;dfuTUr6B^7F$t?Yl)77;D&izLCN2iwcWbQd@_qSG`A*+Nt?hKcpYH<026rxe zd)|FVl>@y9^Y3)27Ln6xpG+0GT__%Gf3kVgSzU@v^5ox*s=E~_w{7^-{dXKU{Oc`7 zi`MX*IH`t|WB}$u^xRX>WkS-yI^mzZOw0Z~UUcPS(3~dUy5k)Avmcv2)ai8L!z#I7 zp6Du(nH{f0d|0 zckDy1b-(jt=oAt?X1~BtkJWT_-kgl|kW=;@L-HTDEz0I!lEs*J{SXWqX2*_x!JCt5 zakT7FzuWJ(;CO9jPM91NY&3d(*p*V%Q#z{iK6y{d*wvD7?hg)MPjVdYY>w{y&NPf% zYjBb1FLgmH+iYmU?SfEWsk8D-_O6pYM<_d!0|Zy^q2KqmPu0XYW%L&xdETPo$d`cJ zUWWAP5y&FbTjV-2Fsatg=Jj++zV_;io;882W}9M&Z>wh<1Y>2tc6@jk)`GdtSb4hO zxWlC6(aoqu4k5Y8^r|FiQKl2`ciWo3%E(N1%)UZ;+=ssx{5xIotszHKiQ%bwR#N@f z)Ad;chI17=k>8xa--9!4lzqJz@@Q+)dsh0l3;Y`f>keG&*2ZzMWKHd7?;dKv2fG&B zEL=I7`gLXBSZHV_M$#$1^~7Bkbj0o?#Fmamqa!f8r5us4?5Vrc%EVqp%3*$cD$Zu+ z@BSCO5;>FMN~w=AACu3ARuzVtSMbgz+(fTt;syOwT^wFR)k5Q=Ft}P0Of7mRnmcHD za|AECQ_P0;PB*;3if;j~TIDyolg(j}gWNuFJ!VsB5T2uQr+3*C;w z=P)S3N=I-Qia&mx@x$Ol=T4<+VX`rabOSr>?N!v*v`g)#DUHc5L!Q{43c4fkeIuQH z!>F~!XC$kn@qnc;XAz_*pKY;?&)FKp#bmf?2BV3YlRauYh!wv4keH=a-zK>|^xz=< z4%7LL!5C&M3ZfhG3(x403xct__a6g()ms(xxVyde@1Xd!$Q-*GoY4-eV#E$3B_!F+GrjGI1 zx8L#HSMT2>G1u?^a~W+Bt%5TfxBMnhFK-Ur6*|u8ZqxQM(J@$ZV6vw*V%pX<=g&27 zkIjuPS@&Jt0V0}+JO8O|pi{qXAoq9pX4lwPJ!?LlaASI71e%hkRzMNAi|+C+?;f{x zD(_RHPa@>F?s5-4@M7*mXj9b(ttvNSYygPH5Pu*vu6gEorUUu(jL2{Yk z`keS5J95sx`Ni~Ur@%TsE3Vgvj;Sbj;!k7QXResG{TZ$5mE8FyuayQr$CJzC(+(7N zoU-z$&t5y%(lK!B+F|y7#i8C(S!Hd{XjYQUvzN4^RR%M<1@mp(C1cBl>^g;AGmhVIJhA)fF0|2c*ZTZJe#H%MDc#@HXCJ$HycCvdyuIv)T`Gt0 zyYCp*bPJh?xj@C4a8b2vjE=|Xr2K(5My_#85Y-*fs#cFqb{IkD)&414e@9aH%VhZ& z+b71ghRMpHoXod>PJ1c`s%ZTfJMn(4$PSNF>z=F?^M2yPq0T4wl5;5A$iE=I@SVQb zK5X-&7IaL4(hbI1WlF#x3D89U)x+CY|I{`iY!LxiV50YR`RZ3 zya;ymzA?-=?w;IsIN~;fjZoxpVoIXrD>=i(PEI&0uxX^8JbI`OS&z7g>OlOCtjE%M)>r>K$0L58o~@Yq*Zq z$cqj2-0h?)*0Sh_S6_ZRo_gdGEB%j&^;~~aheH#&waaHDd@OPNQC)NvPUo2$PY!>t zA<6hnKyu`<6Ba?PVp+P?c{ndi?Q~QQb3o`!lNBr6+g8l&mJS;aa~Mqj6|eL6o}FG@ zZwzLzmh{znz@=$f_1dC=;f@1(T4g=&_jmL`m*uhUxX7P1=OKpGDq?)rs|E9h3X)Cr zE<&w>q|E_H+RV|Tj!YzV%IvLH{9LHv|N3<@OXJfS&9Aplu&wu-b9t4QU{Itnkz~Tw zU0rScHJ zpLJv@&aQZhv-~V>G?wIb9 zp~H+7722^&fIaJC?M z?o3zpb(ru(Y*rWuWj3@GIRlTq%X3?CdbiJ$Z9X@Oak=%Jg{f#!S7{$>D#!?nI+XWP|l_G*HWO-Z$}ofEZ7U-hfL?I+P?EOes&e=U}Lj8>Z22CdRg+tHC?j6k6}P`bB4z z#ha0Q0ytZ_`%^nt^qDFzH#p4MaS_j$mVk5v<)V0CoGc|vQNGBJDvQf@nc1w#u)J9tFWaAH!bn+$4pFew$<2{e^X|Q7wk#W6h|9Ox3yD| z&4*2|Ra3#;K3~7(b`GORv@2@Rh?)+#Ouw z`qyb}c4v!z>oP{P@NtLP7t(kD8F|c78aZjU=eiE4B?2Ly4QAyIXv(qV7aIy#&hEob zyer^esp>eGiP;}Axr5Bd2`xrbd|Pp60iQ}ke7a%(<*>;<1GeTrj4fN%^~_JHE)KN` z;n!K{>!12w5USr;{M~<`B)}76KVv)1e^?KONm?HY9P9IZl^yxsv98_@8@ueqQ`=J( z%ZA*qmUA}#i^?^6mzmZy@eQ?ex*gpp;w z*FB^1dA+`W!1ssm^UL!*FLU4bIoG+)b*^)r_xpXOB@Z0w(G6D7u$1~}C6C(8^dMw^ zk_Atp+M>ueC+b70C^1an40K1os-47}%QtY4X534$OV_b`IOvjr#82ynM!G1lpt4T@ zj%~@`ZjK)FJsI8iYW?1@5-eR$7nk~_*n+9{N<$6QZ+$n{O!?gZnfcbsAU3O|yVC+= z@U%!1^*#!!u_1IS3JL;-loloanMWEfOL9IzHaR2wA0SnhE74tfw#J_mB4*C?xFG5b z(>N9rqzy8*f?N$147L~V(y_;>Ti#{vVy;I}Nc^YcOPZodVrFOh1y3=)5X(Ndj0{8~FV3v?uAzv$BD1NXcwr6Y28Rjmp7P=M*t3 z)wQCj0gZY2XhFKGYJ_#zzY4|S|Kh5P0J$mgeS;)bnHNv&klV$9uBg2z%t($<&;{%l ztv=J{Ibv~0&&=7u4rj*M2JjZBbpr!m*H4~{9w8Asil}e+JDx(^@148#eOXJ|rB~LJ``g8roq*q45OZU@? zk0T5R=*BVzC-ozybg#S%UH&&YqR{&fsaa&|*D4@x3!4Riv$5 zJYX?zGZ}jUCyAzjiSA9kW!Pq;{IKehhzgDqwk9d8#hnOYg8CXgDof|s<%Trm+)s&~ zK@u~@WK{r(CdCz`Wol-!>_)1$Or>4GMXw&$jKnHB>r}ZK@XBJP70;yzwCb>Hm8x^~ zxGRIH7(bmI3J(o~sTZ)>!@gF}a@WbIpUohU+cKS2F?*r$*yhs9w4&qDbQqQ1ERFgKw^ zO+0Gzf3(L}_j&gcIIyW`Qp$!xu8cgN`$YG&_Z|;yiw!2mRt;$+mFTdL`}U9^hA3pd zLa}R5FMPM>LC8{?in~)iV7QiHBS)CNt3TCF-OlVpX7APom$Nt1|9DxnWJ2Cj+D=(& zh|*FmT4S?lutwB$CZ;oW7m9Z9-);FTu&S3~U|T#H7ZUB+%#r<x|*LemzwSX2K^r@chQHjS0;4^l{P}&4Qi!Gu7**fBKIMCiZKw2i_Z% z7eDY6XB14uVBt)?!IN2Olg7KwG<(hC3r|_7P|7y)It!+|=^k4Q zXtk4MLr`n8KrT?uhD94Y;%R1EY6N3;QldYSiq}kUr8O7K@=!@l5>IRGe*Xt#K$e?A zwn<|vGl<|N)ezEm2hU6MV*Yr_43p>ggD7Euua4a)>ALTt)fZVHoX&jS-R?|s-C$VE zUIap6sTJ8h3sn4T*rermKFHhY@WaC&N#yxUd|YG#c6;W0drnvj;s#iiu++oBqMQ+| z?*bHlfypR>yDN3}4#hAJ|rZ7?K4s{n+ zLDVEfxv<2sYyB3xb0dR?Jky(>@~cs>Oiuw;se%B+Vin7t7uaL8}B>PM~u zz{ppK_)XH+-w#)QxtVG>Ma<*M8LavI4Sm$q2-bi*8D{{gwt+7dqxqhT`h%;# zileHri(+DBE1L*lKqi^miUpLk_ix8_n3M8j~I^CB7009$|iB z=ppX~p*;CfVYFB1m0rRmsKhU^4$qwPnU{DO1nrN=3VXAk zg>(K#wYvj8CjTH{B8LA6qI>RKQ5U?ODoLL0e-jnj@Wjj0<6T;mw*0cjuRkL$aq_VC zPMHwPtdCIPH2njV{4+Mrjp(ciW{GnU&XbuX^LMtrYdw_v{bRyb7t$>yytCaBesn5Q z$uJX_VYL$w@@W{Rp?jOHmMDKUN+=Q90>*ab7R%gDGwPZ6FGJf_w%e*o$Iex9oIQmF z;K7aNVE|G^5g{dqueTsSoOkcQeK(ewE5$?eZx`rBnG*E~2BFQ!fC9S=O3X&L+4 z8}&md(JUn@-4y@`QV3VxX`~>LXn8>_>4 z+V`+uWL(;j*FVm&tH(eWqwoTCGM7W=gJ$#RHNj!UKV#hL#E9-P0z+8{G z?}e_${{-C0M%c_F$o#HUodaH#)aE%Qe>4IvHlUy~MhXii`sA1r@X*mkaFUQS)KIWLeP4?{^Qu#5QcsJH|X&G0_ z4%W8|z%ksB`!vmcZw^Ws)7d^k)tAidS9nwB7uyQ%jIb zfXfN)?7anS5Q(PW>wn>Wl;;s+SG5kG=$Y&o;0nY~U{1!$|66BhO(4G5Hxlr1p%>Azqgp%o@0ha|OXIV^)6FzhI@p(^ueyY1WG zdJ0Mh?y5+tZ%W^Ansul@fC_b1-oFkCc5`^E3;ea>BjN=rynKGM!Cna{lF}X0sJ|9JDmlAp zidlNQS`>UnrfG(Y8l*%WXipfxJYpOhmM{SDGz7<2@N+>CyYIM7D;d|XYBln@u2Oh0^fN{dpPKav{3IN~lW9~G_4T7$%t21-t^L#>XjoyKXg9Yuf zx8BRVaJ$|kYTXs_YtoskPt%Oxz-}AJL!gTN2S7$&|;NM%&ov`cI_14PO z!xC)*SE%y8d%fo8#|Bh%-MG1JZq5r~L=0M3T*-J70)`%REF5LzWMNYP`Sm%=R3)K* znxwMhJ7^ys#yuxQ@20u|q@;4wa815R{ZBE>!Q6hlT75ucQvV$)*f~@Q?XNp9iA3XWtBz z1QPv*XJNrm41 zX~rzrDViW7mO)%N=*A7~l!e&Pmj`3QS&;?kgU2_x6{BGSAy*lAmHp?`@sd@&Y=Bp9 zi$m2MJktnogo0PM8Wv)rk6{?$hq7^Y=rlse6Sy(|U^dDY@X?qYnM%M%gUSfTb36S% zJV6fH<|+llLgXQvWN)dxc>LFx{yqG4?*puvz$JqyK1~OCEf+zC&zE3;4S8UpJGtrr zy2rKfF{ad(9`#(_xWtC<$R~8ddltZWel`H!35chO3mcz&h^3iuTbJUC=(trVURs4a z$hU9a$lKS{!Sp!0g)2=F@3pY~zD?!jlHO*9$@!*0wO|TWG@%qUG9G7Thl$x>fl>6d zA7GUx3It7&j)pe}p@g#@G!z2HEzja9SopUas%*xmnFOfyezsOKiW@*Q1s|S<^G|)~ za=Eh^V$;}W2tQl3gO-r6!C8nXEhx(a&*g2RAgygTQLMyyoRJ7pSztmBOR#>9Fv6=d z*cz3N++{;QcHYNi#|^w(ywy@qs$a|KQrm_0w*q~S%5ZQncFVQndJ3Yiq9%xlb>AL>CeSLT)j0Zvi$ zUJGvSpplq)l0ao$GVPH>%nVaNAu0dJnD|2nhMoIS#*A4W|2#ka$li7|ijf0wiUu;- zp9I4V(FHb!T9e#QvdowoKd1AO>C2QbtT?NC?{oON7ryH9Yr!=sUgXk9J7MhjyV8w` zuV>6}&e*74sU+l}!GxfNX+-25J%C2rB2<(=;`un-r{{ym3}fur3)L~DK(^=K!WM6y zjdk*$q1)%l*D4hE9+%M-7=G!80D#K)Q2uTvKLhwUP8bjQM;W{5jTNxNBx&Zy=+8 zR*^gONQ(dR-4d;6nnTgW;6d&RWw+NW-AZrn(j^rJPWY{IoGO!~eUhwXw(oPAin6J{^i!RWJyp#* zU$xm5nf+41srgsd9DVn8kko8_@Aeiz$5FUr+Jm877rJ}Aknz~ijfLWO#5!8puS5MB z>YE23#1yPX6`j4fJ7P+!VrPa%Er@c6V}+sjp1(~6A2<)OYpU4`$VmzuHm(x2U8nID zh#M_gQ1VX$;f*~UGDx_}r>z^6lL-|kq>i-a=h(WfuT?m%(=k?*x?+4eHTA>GnuGe3ijYEWO7<#G!qKAA==O*mF zpB&A&D&$?-a6;jkHKu52x%MAbICSESfYC}pXn}v#F-k9#3EsH+3lSCYR1Y`h{~25m z?!r@>vyo}R0TY_hGTHObzKFRL&1p=JsJh9ps1*zD#ZkW*0;JsI2FR%7>HEKo_OF?5 zu~{oL@EE4LFW0xzY&Eq7Co`9YC8{?M1p3Hp@m3Y*eD0485Auc$GhH{1UU+=_FUgVR zyr3Y<75w@!Dj;3I;g(#ZOYxj{_nl**D@KOf_dXc^xpK`^k-h7U-Xg{ z-alPl{{FMtdKRbuPcMJKGEgKK7X=QwpLXiixY5Zi!=eq3CxQr_cQ^v#Z?fHiKROSMcsuKsCb+ zNZVn+={BE#sE+wReyQnUzA6gU`&%#|M7uOtAk#XQN)d<{vWa4o;{n@a8L}}d4%0O% z6-uKM`2v^L{FRMH=ZhNZd8(q2{t-pUEvKV8H`Z9(2v)1OUi{knsc*4C{QtB>5Xeb) zvy&K^O7KzBN`l<$XIdLv=b00ohV;UI!?R>v#XU(61v_Yf5j9ez9+Ae-%P4`jzj{)0UxeFb{m!5u? zF4K5Fu$zU0U3icv4L+QJUQPk5M}?J{;VD^}x6VmjoTZ;3TQExRC#6`fk6vusfgKPl zx7VXGzO9F8roY zxXmtevhA4*KHTu~i6H1#3pt&`(>@xNS;G;8eJ&TkxEBpCX#bSqPF!=o2Wr}nIr12i zx2%qxh-`dc=U^4up3DDhTw$pqNPPZn07v{0*n^I<_a?Xz_51sIl%7M_=sxweI>A0# zp87Edd#3wopn`T$p@($CqVN_>;0CiP)ZbnOd885LqfoGDgg~Q`+gwX`nC%59mVV4( zmDJc@fJ+d$f;)}TLVv41E!|q|(9C*Q5lr^S6=%a4I?L!{mMhpv4(sO>TWGXTzcExy zf51BVIiR&g9$WEYN$bHCO5S%u7YkWQcqYS4x~^KmN{fSH4V2o~?52vA6W8`O@52=3 zI!CTpxEmzfX`3otafs)q0h-Fg>u8Lm2?*5L>kq@Wtw}=@7332g^q^yBl zE92B=92BhW$)wOxJ#)>g8$-JLfqBs>SsL;F_V{DA67NE|`vJ0xv<7cuKys^)rM2t& zzj)s(d9CJ=aAAv;aKl*cg!R)sn|-RiKmT)Z$jiaQ`A;1UhieMt=uTpcwt{6Eel`_9 zPDJa|@?<6re?`9jPdcU7{e8{Erz-19nYf#8*xO3B*_zO0U3?ssFzw@K5WUAI{7cfF zN82P6{v1adqgwMk{SwDrv@G~@?!}@<@_)xCkh2Kyr-WWx))Y&~avAcsp4lyuDbf^f z-Pt-mu^v|Fzpd03g0$b;Ar-{?jIrevoPIaXD9_nnnKQ{&t~T45VXJJrnRjl{WK=E! zSEY%-JrjI>1c@U-`SSvy%LLMBA%zxvRl4hg&L%}AEXra)69m?I*BDek%QT95zp+*> za;j}Q{}^J^p8iVBp2c)nSW>iMi~1wzVk*#zjIo(|r6WZTy-ps#4mV&SeeP6t_Pa0| zstVw%zkZ=ILU{ah%SpF9-d%wwUYxl%qRmYTX*S9{;aZNwj30WdSW|$e)(d$Of#^Zj z_}O_0y@sVJn>rr#VdVAvuv1}jIiEtPzFPK*z|CI+6|Dv}K2Mq<^24hR{gu{3osxPQ zE7FrEP`r>+ca%}OGP6a2nK5X82=~KYxOY*b2@pvz z(x`N@ykgE#{+J}Ml}kv{zx`*Q%pDABIYtlV6K@?;r-|#) zB4aZypyFi9z8wao_j~{ODrF1s=|tm{zks(>4w4f?Piz;_5H0Vx%qkzS2ayV82hTKn z)~4d#O2*gQ2_hrb{5cB+0rqcTZvmmpq+paKGE*5Vt9OCMxeNVuU1N$lcl9YDRwo=Q~e;uKm#1tZhI)R{f7EEZz z9^Fvd;lt%|P4~#W?0h--HJVEPu?$G+B9>;|tUaHn3deJ&$z~@7ktHgV^0URm8VlDh zvc2cSMp*4i`cTyoRs}*BpF4LxFFNJpY&Gq%WJQ0n?Ciq6oBe*WHW+g#UgRJed#LBh zrO)E7P=}h26*Gq%0+0ThNryO*yEraU0-$5x9~-iXkNmZF$;+Bs?|1fbt?H|YI0nE%4IB<$>7