From 82116bc0899f21fbe2a9c0fa8894d44073010552 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 23 Nov 2023 22:17:23 +0100 Subject: [PATCH 001/176] chore: Update dependencies --- app/build.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0a9687fa3..df77255b8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,13 +94,13 @@ dependencies { implementation 'androidx.core:core-ktx:1.12.0' implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0') implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' - implementation 'androidx.activity:activity-compose:1.7.2' + implementation 'androidx.activity:activity-compose:1.8.1' implementation platform('androidx.compose:compose-bom:2022.10.00') implementation 'androidx.compose.ui:ui' implementation 'androidx.compose.ui:ui-graphics' implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.compose.material3:material3' - implementation "androidx.compose.material:material-icons-extended:1.5.1" + implementation "androidx.compose.material:material-icons-extended:1.5.4" implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.documentfile:documentfile:1.0.1' testImplementation 'junit:junit:4.13.2' @@ -111,11 +111,11 @@ dependencies { debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' - implementation "androidx.navigation:navigation-compose:2.7.2" + implementation "androidx.navigation:navigation-compose:2.7.5" - implementation 'com.google.dagger:hilt-android:2.46.1' + implementation 'com.google.dagger:hilt-android:2.48' annotationProcessor 'com.google.dagger:hilt-compiler:2.46.1' - implementation "androidx.hilt:hilt-navigation-compose:1.0.0" + implementation "androidx.hilt:hilt-navigation-compose:1.1.0" implementation 'com.arthenica:ffmpeg-kit-min-gpl:5.1' @@ -123,12 +123,12 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1" - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' implementation 'com.maxkeppeler.sheets-compose-dialogs:core:1.2.0' implementation 'com.maxkeppeler.sheets-compose-dialogs:duration:1.2.0' implementation 'com.maxkeppeler.sheets-compose-dialogs:list:1.2.0' implementation 'com.maxkeppeler.sheets-compose-dialogs:input:1.2.0' - implementation 'androidx.activity:activity-ktx:1.8.0' + implementation 'androidx.activity:activity-ktx:1.8.1' } \ No newline at end of file From 63198c316e722246900026ddaf09ece5f30ab251 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 25 Nov 2023 14:30:34 +0100 Subject: [PATCH 002/176] feat: Add poc --- app/build.gradle | 11 ++ app/src/main/AndroidManifest.xml | 8 ++ .../myzel394/alibi/services/VideoService.kt | 111 ++++++++++++++++++ .../java/app/myzel394/alibi/ui/Navigation.kt | 6 +- .../app/myzel394/alibi/ui/screens/POCVideo.kt | 21 ++++ 5 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/services/VideoService.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt diff --git a/app/build.gradle b/app/build.gradle index df77255b8..8f5b38507 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -79,6 +79,7 @@ android { buildFeatures { compose true buildConfig = true + viewBinding = true } composeOptions { kotlinCompilerExtensionVersion '1.5.1' @@ -103,6 +104,7 @@ dependencies { implementation "androidx.compose.material:material-icons-extended:1.5.4" implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.documentfile:documentfile:1.0.1' + implementation 'androidx.lifecycle:lifecycle-service:2.6.2' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' @@ -131,4 +133,13 @@ dependencies { implementation 'com.maxkeppeler.sheets-compose-dialogs:input:1.2.0' implementation 'androidx.activity:activity-ktx:1.8.1' + + def camerax_version = "1.3.0" + implementation "androidx.camera:camera-core:${camerax_version}" + implementation "androidx.camera:camera-camera2:${camerax_version}" + implementation "androidx.camera:camera-lifecycle:${camerax_version}" + implementation "androidx.camera:camera-video:${camerax_version}" + + implementation "androidx.camera:camera-view:${camerax_version}" + implementation "androidx.camera:camera-extensions:${camerax_version}" } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8f67b7cd0..c0254e2df 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,9 @@ + + + + + = Build.VERSION_CODES.Q) { + put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/Recorded Videos") + } + } + return MediaStoreOutputOptions.Builder( + contentResolver, + MediaStore.Video.Media.EXTERNAL_CONTENT_URI + ) + .setContentValues(contentValues) + .build() + } + + @SuppressLint("MissingPermission") + override fun onCreate() { + super.onCreate() + + val notification = NotificationCompat.Builder( + this, + NotificationHelper.RECORDER_CHANNEL_ID + ).setContentTitle("Video Recorder") + .setContentText("Recording video") + .setSmallIcon(android.R.drawable.ic_media_play) + .build() + + ServiceCompat.startForeground( + this, + NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID, + notification, + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA + } else { + 0 + }, + ) + + val cameraProviderFuture = ProcessCameraProvider.getInstance(this) + cameraProviderFuture.addListener({ + // Used to bind the lifecycle of cameras to the lifecycle owner + val cameraProvider = cameraProviderFuture.get() + val recorder = Recorder.Builder() + .setQualitySelector(QualitySelector.from(Quality.HIGHEST)) + .build() + val videoCapture = withOutput(recorder) + // Select back camera as a default + val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA + + // Unbind use cases before rebinding + cameraProvider?.unbindAll() + // Bind use cases to camera + cameraProvider?.bindToLifecycle(this@VideoService, cameraSelector, videoCapture) + + val options = createMediaStoreOutputOptions() + + val recording = videoCapture.output.prepareRecording(this@VideoService, options) + .withAudioEnabled() + + val result = recording.start(ContextCompat.getMainExecutor(this@VideoService), {}) + + scope.launch { + delay(15000) + + result.stop() + + cameraProvider.unbindAll() + stopSelf() + } + }, ContextCompat.getMainExecutor(this)) + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index 985e79d56..09384ada5 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -24,6 +24,7 @@ import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.screens.AboutScreen import app.myzel394.alibi.ui.screens.AudioRecorderScreen import app.myzel394.alibi.ui.screens.CustomRecordingNotificationsScreen +import app.myzel394.alibi.ui.screens.POCVideo import app.myzel394.alibi.ui.screens.SettingsScreen import app.myzel394.alibi.ui.screens.WelcomeScreen @@ -70,10 +71,7 @@ fun Navigation( scaleOut(targetScale = SCALE_IN) + fadeOut(tween(durationMillis = 150)) } ) { - AudioRecorderScreen( - navController = navController, - audioRecorder = audioRecorder, - ) + POCVideo() } composable( Screen.Settings.route, diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt new file mode 100644 index 000000000..75b0f2022 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt @@ -0,0 +1,21 @@ +package app.myzel394.alibi.ui.screens + +import android.content.Intent +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.platform.LocalContext +import androidx.core.content.ContextCompat +import app.myzel394.alibi.services.VideoService + +@Composable +fun POCVideo() { + val context = LocalContext.current + + LaunchedEffect(Unit) { + val intent = Intent(context, VideoService::class.java) + ContextCompat.startForegroundService(context, intent) + } + + Text(text = "POCVideo") +} \ No newline at end of file From 0de720ccf4ed84b548a1233b56ca2b0ad0c64c5c Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 25 Nov 2023 15:29:42 +0100 Subject: [PATCH 003/176] debug: Add PoC for recurring video --- .../app/myzel394/alibi/services/VideoService.kt | 17 ++++++++++++++++- .../app/myzel394/alibi/ui/screens/POCVideo.kt | 16 +++++++++++----- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoService.kt index 6e23094ab..6f3924906 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoService.kt @@ -101,10 +101,25 @@ class VideoService : LifecycleService() { scope.launch { delay(15000) + result.stop() + // Unbind use cases before rebinding + cameraProvider?.unbindAll() + // Bind use cases to camera + cameraProvider?.bindToLifecycle(this@VideoService, cameraSelector, videoCapture) + + delay(5000) + + val recording = videoCapture.output.prepareRecording(this@VideoService, options) + .withAudioEnabled() + val result = recording.start(ContextCompat.getMainExecutor(this@VideoService), {}) + + delay(15000) + result.stop() - cameraProvider.unbindAll() stopSelf() + stopForeground(STOP_FOREGROUND_REMOVE) + } }, ContextCompat.getMainExecutor(this)) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt index 75b0f2022..a7cae216c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt @@ -1,9 +1,13 @@ package app.myzel394.alibi.ui.screens import android.content.Intent +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.core.content.ContextCompat import app.myzel394.alibi.services.VideoService @@ -12,10 +16,12 @@ import app.myzel394.alibi.services.VideoService fun POCVideo() { val context = LocalContext.current - LaunchedEffect(Unit) { - val intent = Intent(context, VideoService::class.java) - ContextCompat.startForegroundService(context, intent) + Box(modifier = Modifier.fillMaxSize()) { + Button(onClick = { + val intent = Intent(context, VideoService::class.java) + ContextCompat.startForegroundService(context, intent) + }) { + Text("Start") + } } - - Text(text = "POCVideo") } \ No newline at end of file From f6bdd1b3458dee2ea0c368ef445647f66166256e Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 25 Nov 2023 17:13:28 +0100 Subject: [PATCH 004/176] debug: Fix microphone not accessible in background --- app/src/main/AndroidManifest.xml | 2 +- .../myzel394/alibi/services/VideoService.kt | 75 ++++++++++--------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c0254e2df..dbaa89273 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,7 +53,7 @@ android:foregroundServiceType="microphone" /> + android:foregroundServiceType="camera|microphone" /> = Build.VERSION_CODES.Q) { @@ -68,11 +77,10 @@ class VideoService : LifecycleService() { this, NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID, notification, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA - } else { - 0 - }, + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) + ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA + ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE + else + 0, ) val cameraProviderFuture = ProcessCameraProvider.getInstance(this) @@ -89,37 +97,36 @@ class VideoService : LifecycleService() { // Unbind use cases before rebinding cameraProvider?.unbindAll() // Bind use cases to camera - cameraProvider?.bindToLifecycle(this@VideoService, cameraSelector, videoCapture) + cameraProvider?.bindToLifecycle( + this@VideoService, + cameraSelector, + videoCapture + ) val options = createMediaStoreOutputOptions() - val recording = videoCapture.output.prepareRecording(this@VideoService, options) - .withAudioEnabled() - - val result = recording.start(ContextCompat.getMainExecutor(this@VideoService), {}) - - scope.launch { - delay(15000) - - result.stop() - // Unbind use cases before rebinding - cameraProvider?.unbindAll() - // Bind use cases to camera - cameraProvider?.bindToLifecycle(this@VideoService, cameraSelector, videoCapture) - - delay(5000) - - val recording = videoCapture.output.prepareRecording(this@VideoService, options) - .withAudioEnabled() - val result = recording.start(ContextCompat.getMainExecutor(this@VideoService), {}) - - delay(15000) - - result.stop() - - stopSelf() - stopForeground(STOP_FOREGROUND_REMOVE) - + cycleTimer = Executors.newSingleThreadScheduledExecutor().also { + it.scheduleAtFixedRate( + { + val mainHandler = ContextCompat.getMainExecutor(this@VideoService) + + mainHandler.execute { + runCatching { + recording?.stop() + } + + val r = + videoCapture.output.prepareRecording(this@VideoService, options) + .withAudioEnabled() + + recording = + r.start(ContextCompat.getMainExecutor(this@VideoService), {}) + } + }, + 0, + 10_000, + TimeUnit.MILLISECONDS + ) } }, ContextCompat.getMainExecutor(this)) } From d21580b0cb47106b9ad48f725cdae79ffe425d9b Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 26 Nov 2023 14:12:31 +0100 Subject: [PATCH 005/176] chore: Improvements --- app/src/main/AndroidManifest.xml | 2 +- .../alibi/services/AudioRecorderService.kt | 94 ++++++++++++++++++- .../ExtraRecorderInformationService.kt | 55 ----------- .../alibi/services/IntervalRecorderService.kt | 63 ++----------- .../{VideoService.kt => OldVideoService.kt} | 28 ++---- .../alibi/services/RecorderService.kt | 8 +- .../app/myzel394/alibi/ui/screens/POCVideo.kt | 13 ++- 7 files changed, 126 insertions(+), 137 deletions(-) delete mode 100644 app/src/main/java/app/myzel394/alibi/services/ExtraRecorderInformationService.kt rename app/src/main/java/app/myzel394/alibi/services/{VideoService.kt => OldVideoService.kt} (87%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dbaa89273..25181364e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,7 +52,7 @@ android:name=".services.AudioRecorderService" android:foregroundServiceType="microphone" /> diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index 79d04faf7..dcbcd2b39 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -10,13 +10,17 @@ import android.net.Uri import android.os.Build import android.os.Handler import android.os.Looper +import androidx.compose.material3.SnackbarDuration import androidx.documentfile.provider.DocumentFile +import app.myzel394.alibi.db.AudioRecorderSettings +import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.ui.utils.MicrophoneInfo import java.lang.IllegalStateException -class AudioRecorderService : IntervalRecorderService() { +class AudioRecorderService : + IntervalRecorderService() { var amplitudesAmount = 1000 var selectedMicrophone: MicrophoneInfo? = null @@ -27,6 +31,37 @@ class AudioRecorderService : IntervalRecorderService() { var onMicrophoneDisconnected: () -> Unit = {} var onMicrophoneReconnected: () -> Unit = {} + var amplitudes = mutableListOf() + private set + + private val handler = Handler(Looper.getMainLooper()) + + var onAmplitudeChange: ((List) -> Unit)? = null + + private fun updateAmplitude() { + if (state !== RecorderState.RECORDING) { + return + } + + amplitudes.add(getAmplitude()) + onAmplitudeChange?.invoke(amplitudes) + + // Delete old amplitudes + if (amplitudes.size > getAmplitudeAmount()) { + // Should be more efficient than dropping the elements, getting a new list + // clearing old list and adding new elements to it + repeat(amplitudes.size - getAmplitudeAmount()) { + amplitudes.removeAt(0) + } + } + + handler.postDelayed(::updateAmplitude, 100) + } + + private fun createAmplitudesTimer() { + handler.postDelayed(::updateAmplitude, 100) + } + /// Tell Android to use the correct bluetooth microphone, if any selected private fun startAudioDevice() { if (selectedMicrophone == null) { @@ -102,6 +137,14 @@ class AudioRecorderService : IntervalRecorderService() { } } + override fun getRecordingInformation() = RecordingInformation( + folderPath = batchesFolder.exportFolderForSettings(), + recordingStart = recordingStart, + maxDuration = settings.maxDuration, + fileExtension = settings.fileExtension, + intervalDuration = settings.intervalDuration, + ) + override fun startNewCycle() { super.startNewCycle() @@ -123,6 +166,7 @@ class AudioRecorderService : IntervalRecorderService() { override fun start() { super.start() + createAmplitudesTimer() registerMicrophoneListener() } @@ -140,9 +184,14 @@ class AudioRecorderService : IntervalRecorderService() { unregisterMicrophoneListener() } - override fun getAmplitudeAmount(): Int = amplitudesAmount + override fun resume() { + super.resume() + createAmplitudesTimer() + } + + private fun getAmplitudeAmount(): Int = amplitudesAmount - override fun getAmplitude(): Int { + private fun getAmplitude(): Int { return try { recorder!!.maxAmplitude } catch (error: IllegalStateException) { @@ -213,4 +262,43 @@ class AudioRecorderService : IntervalRecorderService() { audioManager.unregisterAudioDeviceCallback(audioDeviceCallback) } + + data class Settings( + override val maxDuration: Long, + override val intervalDuration: Long, + val bitRate: Int, + val samplingRate: Int, + val outputFormat: Int, + val encoder: Int, + val folder: String? = null, + ) : IntervalRecorderService.Settings( + maxDuration = maxDuration, + intervalDuration = intervalDuration + ) { + val fileExtension: String + get() = when (outputFormat) { + MediaRecorder.OutputFormat.AAC_ADTS -> "aac" + MediaRecorder.OutputFormat.THREE_GPP -> "3gp" + MediaRecorder.OutputFormat.MPEG_4 -> "mp4" + MediaRecorder.OutputFormat.MPEG_2_TS -> "ts" + MediaRecorder.OutputFormat.WEBM -> "webm" + MediaRecorder.OutputFormat.AMR_NB -> "amr" + MediaRecorder.OutputFormat.AMR_WB -> "awb" + MediaRecorder.OutputFormat.OGG -> "ogg" + else -> "raw" + } + + companion object { + fun from(audioRecorderSettings: AudioRecorderSettings): IntervalRecorderService.Settings { + return Settings( + intervalDuration = audioRecorderSettings.intervalDuration, + bitRate = audioRecorderSettings.bitRate, + samplingRate = audioRecorderSettings.getSamplingRate(), + outputFormat = audioRecorderSettings.getOutputFormat(), + encoder = audioRecorderSettings.getEncoder(), + maxDuration = audioRecorderSettings.maxDuration, + ) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/services/ExtraRecorderInformationService.kt b/app/src/main/java/app/myzel394/alibi/services/ExtraRecorderInformationService.kt deleted file mode 100644 index 013dc523a..000000000 --- a/app/src/main/java/app/myzel394/alibi/services/ExtraRecorderInformationService.kt +++ /dev/null @@ -1,55 +0,0 @@ -package app.myzel394.alibi.services - -import android.os.Handler -import android.os.Looper -import app.myzel394.alibi.enums.RecorderState -import java.util.Timer -import java.util.TimerTask -import java.util.concurrent.Executors -import java.util.concurrent.ScheduledExecutorService -import java.util.concurrent.TimeUnit - -abstract class ExtraRecorderInformationService : RecorderService() { - abstract fun getAmplitudeAmount(): Int - abstract fun getAmplitude(): Int - - var amplitudes = mutableListOf() - private set - - private val handler = Handler(Looper.getMainLooper()) - - var onAmplitudeChange: ((List) -> Unit)? = null - - private fun updateAmplitude() { - if (state !== RecorderState.RECORDING) { - return - } - - amplitudes.add(getAmplitude()) - onAmplitudeChange?.invoke(amplitudes) - - // Delete old amplitudes - if (amplitudes.size > getAmplitudeAmount()) { - // Should be more efficient than dropping the elements, getting a new list - // clearing old list and adding new elements to it - repeat(amplitudes.size - getAmplitudeAmount()) { - amplitudes.removeAt(0) - } - } - - handler.postDelayed(::updateAmplitude, 100) - } - - private fun createAmplitudesTimer() { - handler.postDelayed(::updateAmplitude, 100) - } - - override fun start() { - createAmplitudesTimer() - } - - override fun resume() { - createAmplitudesTimer() - } - -} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index cc6ac5540..8bcaede8f 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -19,14 +19,12 @@ import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit -abstract class IntervalRecorderService : ExtraRecorderInformationService() { - private var job = SupervisorJob() - private var scope = CoroutineScope(Dispatchers.IO + job) - +abstract class IntervalRecorderService : + RecorderService() { protected var counter = 0L private set - lateinit var settings: Settings + lateinit var settings: S private lateinit var cycleTimer: ScheduledExecutorService @@ -34,13 +32,7 @@ abstract class IntervalRecorderService : ExtraRecorderInformationService() { var onCustomOutputFolderNotAccessible: () -> Unit = {} - fun getRecordingInformation(): RecordingInformation = RecordingInformation( - folderPath = batchesFolder.exportFolderForSettings(), - recordingStart = recordingStart, - maxDuration = settings.maxDuration, - fileExtension = settings.fileExtension, - intervalDuration = settings.intervalDuration, - ) + abstract fun getRecordingInformation(): I // Make overrideable open fun startNewCycle() { @@ -62,8 +54,6 @@ abstract class IntervalRecorderService : ExtraRecorderInformationService() { } override fun start() { - super.start() - batchesFolder.initFolders() if (!batchesFolder.checkIfFolderIsAccessible()) { batchesFolder = @@ -81,10 +71,6 @@ abstract class IntervalRecorderService : ExtraRecorderInformationService() { override fun resume() { createTimer() - - // We first want to start our timers, so the `ExtraRecorderInformationService` can fetch - // amplitudes - super.resume() } override fun stop() { @@ -96,45 +82,14 @@ abstract class IntervalRecorderService : ExtraRecorderInformationService() { } private fun deleteOldRecordings() { - val timeMultiplier = settings!!.maxDuration / settings!!.intervalDuration + val timeMultiplier = settings.maxDuration / settings.intervalDuration val earliestCounter = counter - timeMultiplier batchesFolder.deleteOldRecordings(earliestCounter) } - data class Settings( - val maxDuration: Long, - val intervalDuration: Long, - val bitRate: Int, - val samplingRate: Int, - val outputFormat: Int, - val encoder: Int, - val folder: String? = null, - ) { - val fileExtension: String - get() = when (outputFormat) { - MediaRecorder.OutputFormat.AAC_ADTS -> "aac" - MediaRecorder.OutputFormat.THREE_GPP -> "3gp" - MediaRecorder.OutputFormat.MPEG_4 -> "mp4" - MediaRecorder.OutputFormat.MPEG_2_TS -> "ts" - MediaRecorder.OutputFormat.WEBM -> "webm" - MediaRecorder.OutputFormat.AMR_NB -> "amr" - MediaRecorder.OutputFormat.AMR_WB -> "awb" - MediaRecorder.OutputFormat.OGG -> "ogg" - else -> "raw" - } - - companion object { - fun from(audioRecorderSettings: AudioRecorderSettings): Settings { - return Settings( - intervalDuration = audioRecorderSettings.intervalDuration, - bitRate = audioRecorderSettings.bitRate, - samplingRate = audioRecorderSettings.getSamplingRate(), - outputFormat = audioRecorderSettings.getOutputFormat(), - encoder = audioRecorderSettings.getEncoder(), - maxDuration = audioRecorderSettings.maxDuration, - ) - } - } - } + abstract class Settings( + open val maxDuration: Long, + open val intervalDuration: Long, + ) } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoService.kt b/app/src/main/java/app/myzel394/alibi/services/OldVideoService.kt similarity index 87% rename from app/src/main/java/app/myzel394/alibi/services/VideoService.kt rename to app/src/main/java/app/myzel394/alibi/services/OldVideoService.kt index c48e66dac..2d109a933 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/OldVideoService.kt @@ -3,26 +3,16 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint import android.content.ContentValues import android.content.pm.ServiceInfo -import android.graphics.SurfaceTexture -import android.hardware.Camera import android.os.Build import android.provider.MediaStore -import android.view.Surface -import android.view.TextureView -import android.view.ViewGroup -import androidx.camera.core.CameraProvider import androidx.camera.core.CameraSelector -import androidx.camera.core.Preview -import androidx.camera.core.Preview.SurfaceProvider import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.video.MediaStoreOutputOptions -import androidx.camera.video.PendingRecording import androidx.camera.video.Quality import androidx.camera.video.QualitySelector import androidx.camera.video.Recorder import androidx.camera.video.Recording import androidx.camera.video.VideoCapture.withOutput -import androidx.camera.video.VideoRecordEvent import androidx.core.app.NotificationCompat import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat @@ -31,13 +21,15 @@ import app.myzel394.alibi.NotificationHelper import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit -class VideoService : LifecycleService() { + +class VideoService : IntervalRecorderService() { +} + +class OldVideoService : LifecycleService() { private var job = SupervisorJob() private var scope = CoroutineScope(Dispatchers.IO + job) @@ -88,7 +80,7 @@ class VideoService : LifecycleService() { // Used to bind the lifecycle of cameras to the lifecycle owner val cameraProvider = cameraProviderFuture.get() val recorder = Recorder.Builder() - .setQualitySelector(QualitySelector.from(Quality.HIGHEST)) + .setQualitySelector(QualitySelector.from(Quality.LOWEST)) .build() val videoCapture = withOutput(recorder) // Select back camera as a default @@ -98,7 +90,7 @@ class VideoService : LifecycleService() { cameraProvider?.unbindAll() // Bind use cases to camera cameraProvider?.bindToLifecycle( - this@VideoService, + this@OldVideoService, cameraSelector, videoCapture ) @@ -108,7 +100,7 @@ class VideoService : LifecycleService() { cycleTimer = Executors.newSingleThreadScheduledExecutor().also { it.scheduleAtFixedRate( { - val mainHandler = ContextCompat.getMainExecutor(this@VideoService) + val mainHandler = ContextCompat.getMainExecutor(this@OldVideoService) mainHandler.execute { runCatching { @@ -116,11 +108,11 @@ class VideoService : LifecycleService() { } val r = - videoCapture.output.prepareRecording(this@VideoService, options) + videoCapture.output.prepareRecording(this@OldVideoService, options) .withAudioEnabled() recording = - r.start(ContextCompat.getMainExecutor(this@VideoService), {}) + r.start(ContextCompat.getMainExecutor(this@OldVideoService), {}) } }, 0, diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index 02b579794..5648acbc8 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -10,6 +10,7 @@ import android.os.Build import android.os.IBinder import androidx.core.app.NotificationManagerCompat import androidx.core.app.ServiceCompat +import androidx.lifecycle.LifecycleService import app.myzel394.alibi.NotificationHelper import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.ui.utils.PermissionHelper @@ -20,7 +21,7 @@ import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit -abstract class RecorderService : Service() { +abstract class RecorderService : LifecycleService() { private val binder = RecorderBinder() private var isPaused: Boolean = false @@ -44,7 +45,10 @@ abstract class RecorderService : Service() { protected abstract fun resume() protected abstract fun stop() - override fun onBind(p0: Intent?): IBinder? = binder + override fun onBind(intent: Intent): IBinder? { + super.onBind(intent) + return binder + } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { when (intent?.action) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt index a7cae216c..fc42e098d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt @@ -3,22 +3,27 @@ package app.myzel394.alibi.ui.screens import android.content.Intent import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat -import app.myzel394.alibi.services.VideoService +import app.myzel394.alibi.services.OldVideoService @Composable fun POCVideo() { val context = LocalContext.current - Box(modifier = Modifier.fillMaxSize()) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(64.dp) + ) { Button(onClick = { - val intent = Intent(context, VideoService::class.java) + val intent = Intent(context, OldVideoService::class.java) ContextCompat.startForegroundService(context, intent) }) { Text("Start") From f033550f8f41ade79b3381eb1fca6f96afda01aa Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 26 Nov 2023 19:10:15 +0100 Subject: [PATCH 006/176] feat: Slowly creating workable camera support --- app/src/main/AndroidManifest.xml | 2 +- .../alibi/services/AudioRecorderService.kt | 2 +- .../alibi/services/IntervalRecorderService.kt | 4 +- .../alibi/services/OldVideoService.kt | 159 +++++++++++++++++- .../alibi/ui/models/AudioRecorderModel.kt | 2 +- .../app/myzel394/alibi/ui/screens/POCVideo.kt | 25 ++- 6 files changed, 185 insertions(+), 9 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 25181364e..dbaa89273 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,7 +52,7 @@ android:name=".services.AudioRecorderService" android:foregroundServiceType="microphone" /> diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index dcbcd2b39..fdaac5160 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -289,7 +289,7 @@ class AudioRecorderService : } companion object { - fun from(audioRecorderSettings: AudioRecorderSettings): IntervalRecorderService.Settings { + fun from(audioRecorderSettings: AudioRecorderSettings): Settings { return Settings( intervalDuration = audioRecorderSettings.intervalDuration, bitRate = audioRecorderSettings.bitRate, diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index 8bcaede8f..3bf7b7dc9 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -43,9 +43,7 @@ abstract class IntervalRecorderService private fun createTimer() { cycleTimer = Executors.newSingleThreadScheduledExecutor().also { it.scheduleAtFixedRate( - { - startNewCycle() - }, + ::startNewCycle, 0, settings.intervalDuration, TimeUnit.MILLISECONDS diff --git a/app/src/main/java/app/myzel394/alibi/services/OldVideoService.kt b/app/src/main/java/app/myzel394/alibi/services/OldVideoService.kt index 2d109a933..8ad2b7e16 100644 --- a/app/src/main/java/app/myzel394/alibi/services/OldVideoService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/OldVideoService.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.content.ContentValues import android.content.pm.ServiceInfo import android.os.Build +import android.os.Environment import android.provider.MediaStore import androidx.camera.core.CameraSelector import androidx.camera.lifecycle.ProcessCameraProvider @@ -12,21 +13,177 @@ import androidx.camera.video.Quality import androidx.camera.video.QualitySelector import androidx.camera.video.Recorder import androidx.camera.video.Recording +import androidx.camera.video.VideoCapture import androidx.camera.video.VideoCapture.withOutput import androidx.core.app.NotificationCompat import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleService import app.myzel394.alibi.NotificationHelper +import app.myzel394.alibi.db.RecordingInformation +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit -class VideoService : IntervalRecorderService() { +class VideoService : IntervalRecorderService() { + private val job = SupervisorJob() + private val scope = CoroutineScope(Dispatchers.IO + job) + + private var cameraProvider: ProcessCameraProvider? = null + private var videoCapture: VideoCapture? = null + private var activeRecording: Recording? = null + + // Used to listen and check if the camera is available + private var _cameraAvailableListener = CompletableDeferred() + + // Runs a function in the main thread + private fun runInMain(callback: () -> Unit) { + val mainHandler = ContextCompat.getMainExecutor(this) + + mainHandler.execute(callback) + } + + // Open the camera. + // Used to open it for a longer time, shouldn't be called when pausing / resuming. + // This should only be called when starting a recording. + private suspend fun openCamera() { + cameraProvider = withContext(Dispatchers.IO) { + ProcessCameraProvider.getInstance(this@VideoService).get() + } + + val recorder = Recorder.Builder() + .setQualitySelector(QualitySelector.from(Quality.HIGHEST)) + .build() + videoCapture = withOutput(recorder) + + runInMain { + cameraProvider!!.bindToLifecycle( + this, + settings.camera, + videoCapture + ) + + _cameraAvailableListener.complete(true) + } + } + + // Close the camera + // Used to close it finally, shouldn't be called when pausing / resuming. + // This should only be called after recording has finished. + private fun closeCamera() { + clearOldVideoRecording() + + runCatching { + cameraProvider?.unbindAll() + } + + cameraProvider = null + videoCapture = null + } + + override fun start() { + super.start() + + scope.launch { + openCamera() + } + } + + override fun stop() { + super.stop() + + closeCamera() + } + + private fun clearOldVideoRecording() { + runCatching { + activeRecording?.stop() + } + } + + @SuppressLint("MissingPermission") + private fun prepareVideoRecording() = + videoCapture!!.output + .prepareRecording(this, settings.getOutputOptions(this)) + .withAudioEnabled() + + @SuppressLint("MissingPermission") + override fun startNewCycle() { + super.startNewCycle() + + fun action() { + println("=======================") + activeRecording?.stop() + val newRecording = prepareVideoRecording() + + activeRecording = newRecording.start(ContextCompat.getMainExecutor(this), {}) + } + + if (_cameraAvailableListener.isCompleted) { + action() + } else { + // Race condition of `startNewCycle` being called before `invpkeOnCompletion` + // has been called can be ignored, as the camera usually opens within 5 seconds + // and the interval can't be set shorter than 10 seconds. + _cameraAvailableListener.invokeOnCompletion { + action() + } + } + } + + override fun getRecordingInformation(): RecordingInformation = RecordingInformation( + folderPath = batchesFolder.exportFolderForSettings(), + recordingStart = recordingStart, + maxDuration = settings.maxDuration, + fileExtension = settings.fileExtension, + intervalDuration = settings.intervalDuration, + ) + + data class Settings( + override val maxDuration: Long, + override val intervalDuration: Long, + val folder: String? = null, + val camera: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA, + ) : IntervalRecorderService.Settings( + maxDuration = maxDuration, + intervalDuration = intervalDuration + ) { + val fileExtension + get() = "mp4" + + fun getOutputOptions(video: VideoService): MediaStoreOutputOptions { + val contentValues = ContentValues().apply { + put(MediaStore.Video.Media.DISPLAY_NAME, "${video.counter}.$fileExtension") + + put( + MediaStore.MediaColumns.RELATIVE_PATH, + "DCIM/Recordings" + ) + } + + // TODO: Find a way to make this work with the internal storage + return MediaStoreOutputOptions.Builder( + video.contentResolver, + MediaStore.Video.Media.EXTERNAL_CONTENT_URI, + ) + .setContentValues(contentValues) + .build() + } + + companion object { + fun from() = Settings( + maxDuration = 60_000, + intervalDuration = 10_000, + ) + } + } } class OldVideoService : LifecycleService() { diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index 14540c1cc..44a545996 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -92,7 +92,7 @@ class AudioRecorderModel : ViewModel() { } recorder.batchesFolder = batchesFolder ?: recorder.batchesFolder recorder.settings = - IntervalRecorderService.Settings.from(settings.audioRecorderSettings) + AudioRecorderService.Settings.from(settings.audioRecorderSettings) recorder.clearAllRecordings() }.also { diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt index fc42e098d..634a84872 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt @@ -1,6 +1,9 @@ package app.myzel394.alibi.ui.screens +import android.content.ComponentName +import android.content.Context import android.content.Intent +import android.os.IBinder import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -11,7 +14,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat -import app.myzel394.alibi.services.OldVideoService +import app.myzel394.alibi.services.AudioRecorderService +import app.myzel394.alibi.services.RecorderService +import app.myzel394.alibi.services.VideoService @Composable fun POCVideo() { @@ -23,8 +28,24 @@ fun POCVideo() { .padding(64.dp) ) { Button(onClick = { - val intent = Intent(context, OldVideoService::class.java) + val connection = object : android.content.ServiceConnection { + override fun onServiceConnected(name: ComponentName?, service: IBinder?) { + val binder = + ((service as RecorderService.RecorderBinder).getService() as VideoService).also { recorder -> + recorder.settings = VideoService.Settings.from() + recorder.startRecording() + } + } + + override fun onServiceDisconnected(name: ComponentName?) { + } + } + + val intent = Intent(context, VideoService::class.java).apply { + action = "init" + } ContextCompat.startForegroundService(context, intent) + context.bindService(intent, connection, Context.BIND_AUTO_CREATE) }) { Text("Start") } From d9e707aeeaf6322a47d2b1192e2d6eb3b30d7af4 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 26 Nov 2023 19:55:54 +0100 Subject: [PATCH 007/176] feat: Add VideoRecorderModel --- .../alibi/services/AudioRecorderService.kt | 1 - .../alibi/services/RecorderService.kt | 1 + .../java/app/myzel394/alibi/ui/Navigation.kt | 6 +- .../alibi/ui/models/AudioRecorderModel.kt | 164 +++--------------- .../alibi/ui/models/BaseRecorderModel.kt | 153 ++++++++++++++++ .../alibi/ui/models/VideoRecorderModel.kt | 25 +++ .../app/myzel394/alibi/ui/screens/POCVideo.kt | 37 ++-- 7 files changed, 231 insertions(+), 156 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index fdaac5160..b4fd73ea2 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -26,7 +26,6 @@ class AudioRecorderService : var recorder: MediaRecorder? = null private set - var onError: () -> Unit = {} var onSelectedMicrophoneChange: (MicrophoneInfo?) -> Unit = {} var onMicrophoneDisconnected: () -> Unit = {} var onMicrophoneReconnected: () -> Unit = {} diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index 5648acbc8..e80c1ffa9 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -33,6 +33,7 @@ abstract class RecorderService : LifecycleService() { private set var onStateChange: ((RecorderState) -> Unit)? = null + var onError: () -> Unit = {} var recordingTime = 0L private set diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index 09384ada5..1a64b8e98 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -21,6 +21,7 @@ import androidx.navigation.compose.rememberNavController import app.myzel394.alibi.dataStore import app.myzel394.alibi.ui.enums.Screen import app.myzel394.alibi.ui.models.AudioRecorderModel +import app.myzel394.alibi.ui.models.VideoRecorderModel import app.myzel394.alibi.ui.screens.AboutScreen import app.myzel394.alibi.ui.screens.AudioRecorderScreen import app.myzel394.alibi.ui.screens.CustomRecordingNotificationsScreen @@ -32,7 +33,8 @@ const val SCALE_IN = 1.25f @Composable fun Navigation( - audioRecorder: AudioRecorderModel = viewModel() + audioRecorder: AudioRecorderModel = viewModel(), + videoRecorder: VideoRecorderModel = viewModel(), ) { val navController = rememberNavController() val context = LocalContext.current @@ -71,7 +73,7 @@ fun Navigation( scaleOut(targetScale = SCALE_IN) + fadeOut(tween(durationMillis = 150)) } ) { - POCVideo() + POCVideo(videoRecorder = videoRecorder, settings = settings) } composable( Screen.Settings.route, diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index 44a545996..dd633e7c3 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -24,11 +24,10 @@ import app.myzel394.alibi.services.RecorderService import kotlinx.serialization.json.Json import app.myzel394.alibi.ui.utils.MicrophoneInfo -class AudioRecorderModel : ViewModel() { - var recorderState by mutableStateOf(RecorderState.IDLE) - private set - var recordingTime by mutableStateOf(null) - private set +class AudioRecorderModel : + BaseRecorderModel() { + override val intentClass = AudioRecorderService::class.java + var amplitudes by mutableStateOf>(emptyList()) private set var selectedMicrophone by mutableStateOf(null) @@ -36,25 +35,6 @@ class AudioRecorderModel : ViewModel() { var onAmplitudeChange: () -> Unit = {} - val isInRecording: Boolean - get() = recorderState !== RecorderState.IDLE && recordingTime != null - - val isPaused: Boolean - get() = recorderState === RecorderState.PAUSED - - val progress: Float - get() = (recordingTime!! / recorderService!!.settings!!.maxDuration).toFloat() - - var recorderService: AudioRecorderService? = null - private set - - var onRecordingSave: () -> Unit = {} - var onError: () -> Unit = {} - var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null - var batchesFolder: BatchesFolder? = null - - private lateinit var settings: AppSettings - var microphoneStatus: MicrophoneConnectivityStatus = MicrophoneConnectivityStatus.CONNECTED private set @@ -63,78 +43,29 @@ class AudioRecorderModel : ViewModel() { DISCONNECTED } - private val connection = object : ServiceConnection { - override fun onServiceConnected(className: ComponentName, service: IBinder) { - recorderService = - ((service as RecorderService.RecorderBinder).getService() as AudioRecorderService).also { recorder -> - // Init variables from us to the service - recorder.onStateChange = { state -> - recorderState = state - } - recorder.onRecordingTimeChange = { time -> - recordingTime = time - } - recorder.onAmplitudeChange = { amps -> - amplitudes = amps - onAmplitudeChange() - } - recorder.onError = { - onError() - } - recorder.onSelectedMicrophoneChange = { microphone -> - selectedMicrophone = microphone - } - recorder.onMicrophoneDisconnected = { - microphoneStatus = MicrophoneConnectivityStatus.DISCONNECTED - } - recorder.onMicrophoneReconnected = { - microphoneStatus = MicrophoneConnectivityStatus.CONNECTED - } - recorder.batchesFolder = batchesFolder ?: recorder.batchesFolder - recorder.settings = - AudioRecorderService.Settings.from(settings.audioRecorderSettings) - - recorder.clearAllRecordings() - }.also { - // Init UI from the service - it.startRecording() - - recorderState = it.state - recordingTime = it.recordingTime - amplitudes = it.amplitudes - selectedMicrophone = it.selectedMicrophone - } + override fun onServiceConnected(service: AudioRecorderService) { + service.onSelectedMicrophoneChange = { microphone -> + selectedMicrophone = microphone } - - override fun onServiceDisconnected(arg0: ComponentName) { - recorderService = null - reset() + service.onMicrophoneDisconnected = { + microphoneStatus = MicrophoneConnectivityStatus.DISCONNECTED } - } + service.onMicrophoneReconnected = { + microphoneStatus = MicrophoneConnectivityStatus.CONNECTED + } + service.settings = + AudioRecorderService.Settings.from(settings.audioRecorderSettings) - fun reset() { - recorderState = RecorderState.IDLE - recordingTime = null - amplitudes = emptyList() - selectedMicrophone = null - microphoneStatus = MicrophoneConnectivityStatus.CONNECTED - } + service.clearAllRecordings() + service.startRecording() - fun startRecording(context: Context, settings: AppSettings) { - runCatching { - recorderService?.clearAllRecordings() - context.unbindService(connection) - } + recorderState = service.state + recordingTime = service.recordingTime + amplitudes = service.amplitudes + selectedMicrophone = service.selectedMicrophone + } - notificationDetails = settings.notificationSettings.let { - if (it == null) - null - else - RecorderNotificationHelper.NotificationDetails.fromNotificationSettings( - context, - it - ) - } + override fun startRecording(context: Context, settings: AppSettings) { batchesFolder = if (settings.audioRecorderSettings.saveFolder == null) BatchesFolder.viaInternalFolder(context) else @@ -145,42 +76,15 @@ class AudioRecorderModel : ViewModel() { Uri.parse(settings.audioRecorderSettings.saveFolder) )!! ) - this.settings = settings - val intent = Intent(context, AudioRecorderService::class.java).apply { - action = "init" - - if (notificationDetails != null) { - putExtra( - "notificationDetails", - Json.encodeToString( - RecorderNotificationHelper.NotificationDetails.serializer(), - notificationDetails!!, - ), - ) - } - } - ContextCompat.startForegroundService(context, intent) - context.bindService(intent, connection, Context.BIND_AUTO_CREATE) + super.startRecording(context, settings) } - fun stopRecording(context: Context) { - runCatching { - context.unbindService(connection) - } - - val intent = Intent(context, AudioRecorderService::class.java) - context.stopService(intent) - - reset() - } - - fun pauseRecording() { - recorderService!!.changeState(RecorderState.PAUSED) - } - - fun resumeRecording() { - recorderService!!.changeState(RecorderState.RECORDING) + override fun reset() { + super.reset() + amplitudes = emptyList() + selectedMicrophone = null + microphoneStatus = MicrophoneConnectivityStatus.CONNECTED } fun setMaxAmplitudesAmount(amount: Int) { @@ -194,16 +98,4 @@ class AudioRecorderModel : ViewModel() { microphoneStatus = MicrophoneConnectivityStatus.CONNECTED } } - - fun bindToService(context: Context) { - Intent(context, AudioRecorderService::class.java).also { intent -> - context.bindService(intent, connection, 0) - } - } - - fun unbindFromService(context: Context) { - runCatching { - context.unbindService(connection) - } - } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt new file mode 100644 index 000000000..0d0d09931 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -0,0 +1,153 @@ +package app.myzel394.alibi.ui.models + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.net.Uri +import android.os.IBinder +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.core.content.ContextCompat +import androidx.documentfile.provider.DocumentFile +import androidx.lifecycle.ViewModel +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.enums.RecorderState +import app.myzel394.alibi.helpers.BatchesFolder +import app.myzel394.alibi.services.AudioRecorderService +import app.myzel394.alibi.services.IntervalRecorderService +import app.myzel394.alibi.services.RecorderNotificationHelper +import app.myzel394.alibi.services.RecorderService +import kotlinx.serialization.json.Json + +abstract class BaseRecorderModel> : + ViewModel() { + protected abstract val intentClass: Class + + var recorderState by mutableStateOf(RecorderState.IDLE) + protected set + var recordingTime by mutableStateOf(null) + protected set + + val isInRecording: Boolean + get() = recorderState !== RecorderState.IDLE && recordingTime != null + + val isPaused: Boolean + get() = recorderState === RecorderState.PAUSED + + val progress: Float + get() = (recordingTime!! / recorderService!!.settings.maxDuration).toFloat() + + var recorderService: T? = null + protected set + + var onRecordingSave: () -> Unit = {} + var onError: () -> Unit = {} + var batchesFolder: BatchesFolder? = null + + private var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null + + protected lateinit var settings: AppSettings + + protected abstract fun onServiceConnected(service: T) + + private val connection = object : ServiceConnection { + override fun onServiceConnected(className: ComponentName, service: IBinder) { + recorderService = + ((service as RecorderService.RecorderBinder).getService() as T).also { recorder -> + // Init variables from us to the service + recorder.onStateChange = { state -> + recorderState = state + } + recorder.onRecordingTimeChange = { time -> + recordingTime = time + } + recorder.onError = { + onError() + } + recorder.batchesFolder = batchesFolder ?: recorder.batchesFolder + + // Rest should be initialized from the child class + onServiceConnected(recorder) + } + } + + override fun onServiceDisconnected(arg0: ComponentName) { + recorderService = null + reset() + } + } + + open fun reset() { + recorderState = RecorderState.IDLE + recordingTime = null + } + + // If override, call `super` AFTER setting the settings + open fun startRecording(context: Context, settings: AppSettings) { + this.settings = settings + + runCatching { + recorderService?.clearAllRecordings() + context.unbindService(connection) + } + + notificationDetails = settings.notificationSettings.let { + if (it == null) + null + else + RecorderNotificationHelper.NotificationDetails.fromNotificationSettings( + context, + it + ) + } + + val intent = Intent(context, intentClass).apply { + action = "init" + + if (notificationDetails != null) { + putExtra( + "notificationDetails", + Json.encodeToString( + RecorderNotificationHelper.NotificationDetails.serializer(), + notificationDetails!!, + ), + ) + } + } + ContextCompat.startForegroundService(context, intent) + context.bindService(intent, connection, Context.BIND_AUTO_CREATE) + } + + fun stopRecording(context: Context) { + runCatching { + context.unbindService(connection) + } + + val intent = Intent(context, intentClass) + context.stopService(intent) + + reset() + } + + fun pauseRecording() { + recorderService!!.changeState(RecorderState.PAUSED) + } + + fun resumeRecording() { + recorderService!!.changeState(RecorderState.RECORDING) + } + + fun bindToService(context: Context) { + Intent(context, intentClass).also { intent -> + context.bindService(intent, connection, 0) + } + } + + fun unbindFromService(context: Context) { + runCatching { + context.unbindService(connection) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt new file mode 100644 index 000000000..e806579a3 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt @@ -0,0 +1,25 @@ +package app.myzel394.alibi.ui.models + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import app.myzel394.alibi.db.RecordingInformation +import app.myzel394.alibi.enums.RecorderState +import app.myzel394.alibi.services.AudioRecorderService +import app.myzel394.alibi.services.VideoService + +class VideoRecorderModel : + BaseRecorderModel() { + override val intentClass = VideoService::class.java + + override fun onServiceConnected(service: VideoService) { + service.settings = VideoService.Settings.from() + + service.clearAllRecordings() + service.startRecording() + + recorderState = service.state + recordingTime = service.recordingTime + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt index 634a84872..5e38898f6 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt @@ -10,42 +10,45 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat +import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.services.AudioRecorderService import app.myzel394.alibi.services.RecorderService import app.myzel394.alibi.services.VideoService +import app.myzel394.alibi.ui.models.VideoRecorderModel @Composable -fun POCVideo() { +fun POCVideo( + videoRecorder: VideoRecorderModel, + settings: AppSettings, +) { val context = LocalContext.current + var started by rememberSaveable { + mutableStateOf(false) + } + Box( modifier = Modifier .fillMaxSize() .padding(64.dp) ) { Button(onClick = { - val connection = object : android.content.ServiceConnection { - override fun onServiceConnected(name: ComponentName?, service: IBinder?) { - val binder = - ((service as RecorderService.RecorderBinder).getService() as VideoService).also { recorder -> - recorder.settings = VideoService.Settings.from() - recorder.startRecording() - } - } - - override fun onServiceDisconnected(name: ComponentName?) { - } + if (!started) { + videoRecorder.startRecording(context, settings) + } else { + videoRecorder.stopRecording(context) } - val intent = Intent(context, VideoService::class.java).apply { - action = "init" - } - ContextCompat.startForegroundService(context, intent) - context.bindService(intent, connection, Context.BIND_AUTO_CREATE) + started = !started }) { Text("Start") } From cb4f76efe02831cdb5e8bd2948781486967b44d2 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 26 Nov 2023 20:02:19 +0100 Subject: [PATCH 008/176] chore: Cleanup --- ...ideoService.kt => VideoRecorderService.kt} | 96 ------------------- 1 file changed, 96 deletions(-) rename app/src/main/java/app/myzel394/alibi/services/{OldVideoService.kt => VideoRecorderService.kt} (62%) diff --git a/app/src/main/java/app/myzel394/alibi/services/OldVideoService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt similarity index 62% rename from app/src/main/java/app/myzel394/alibi/services/OldVideoService.kt rename to app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 8ad2b7e16..fbfb46125 100644 --- a/app/src/main/java/app/myzel394/alibi/services/OldVideoService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.content.ContentValues import android.content.pm.ServiceInfo import android.os.Build -import android.os.Environment import android.provider.MediaStore import androidx.camera.core.CameraSelector import androidx.camera.lifecycle.ProcessCameraProvider @@ -185,98 +184,3 @@ class VideoService : IntervalRecorderService= Build.VERSION_CODES.Q) { - put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/Recorded Videos") - } - } - return MediaStoreOutputOptions.Builder( - contentResolver, - MediaStore.Video.Media.EXTERNAL_CONTENT_URI - ) - .setContentValues(contentValues) - .build() - } - - @SuppressLint("MissingPermission") - override fun onCreate() { - super.onCreate() - - val notification = NotificationCompat.Builder( - this, - NotificationHelper.RECORDER_CHANNEL_ID - ).setContentTitle("Video Recorder") - .setContentText("Recording video") - .setSmallIcon(android.R.drawable.ic_media_play) - .build() - - ServiceCompat.startForeground( - this, - NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID, - notification, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) - ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA + ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE - else - 0, - ) - - val cameraProviderFuture = ProcessCameraProvider.getInstance(this) - cameraProviderFuture.addListener({ - // Used to bind the lifecycle of cameras to the lifecycle owner - val cameraProvider = cameraProviderFuture.get() - val recorder = Recorder.Builder() - .setQualitySelector(QualitySelector.from(Quality.LOWEST)) - .build() - val videoCapture = withOutput(recorder) - // Select back camera as a default - val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA - - // Unbind use cases before rebinding - cameraProvider?.unbindAll() - // Bind use cases to camera - cameraProvider?.bindToLifecycle( - this@OldVideoService, - cameraSelector, - videoCapture - ) - - val options = createMediaStoreOutputOptions() - - cycleTimer = Executors.newSingleThreadScheduledExecutor().also { - it.scheduleAtFixedRate( - { - val mainHandler = ContextCompat.getMainExecutor(this@OldVideoService) - - mainHandler.execute { - runCatching { - recording?.stop() - } - - val r = - videoCapture.output.prepareRecording(this@OldVideoService, options) - .withAudioEnabled() - - recording = - r.start(ContextCompat.getMainExecutor(this@OldVideoService), {}) - } - }, - 0, - 10_000, - TimeUnit.MILLISECONDS - ) - } - }, ContextCompat.getMainExecutor(this)) - } -} \ No newline at end of file From a9b6225717f5cf4ec94d9f54eb937c52abb2a61b Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 26 Nov 2023 22:06:49 +0100 Subject: [PATCH 009/176] feat: Add internal folder support --- .../myzel394/alibi/helpers/BatchesFolder.kt | 4 ++-- .../alibi/services/VideoRecorderService.kt | 22 ++++++------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 025a48ad0..b1dd01425 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -34,11 +34,11 @@ data class BatchesFolder( customFileFileDescriptor?.close() } - private fun getInternalFolder(): File { + fun getInternalFolder(): File { return getFolder(context) } - private fun getCustomDefinedFolder(): DocumentFile { + fun getCustomDefinedFolder(): DocumentFile { return customFolder!!.findFile(subfolderName)!! } diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index fbfb46125..264677422 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -7,6 +7,7 @@ import android.os.Build import android.provider.MediaStore import androidx.camera.core.CameraSelector import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.camera.video.FileOutputOptions import androidx.camera.video.MediaStoreOutputOptions import androidx.camera.video.Quality import androidx.camera.video.QualitySelector @@ -17,6 +18,7 @@ import androidx.camera.video.VideoCapture.withOutput import androidx.core.app.NotificationCompat import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat +import androidx.core.net.toUri import androidx.lifecycle.LifecycleService import app.myzel394.alibi.NotificationHelper import app.myzel394.alibi.db.RecordingInformation @@ -157,23 +159,13 @@ class VideoService : IntervalRecorderService Date: Sun, 26 Nov 2023 22:23:39 +0100 Subject: [PATCH 010/176] chore: Use general MediaConverter for concatenation --- .../myzel394/alibi/helpers/BatchesFolder.kt | 22 +++++++++ .../myzel394/alibi/helpers/MediaConverter.kt | 47 +++++++++++++++++++ .../alibi/ui/screens/AudioRecorderScreen.kt | 13 ++--- 3 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index b1dd01425..d40d5755b 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -122,6 +122,28 @@ data class BatchesFolder( } } + suspend fun exportToOneFile( + recordingStart: LocalDateTime, + extension: String, + disableCache: Boolean = false, + ) { + if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) { + return + } + + val filePaths = getBatchesForFFmpeg() + val outputFile = getOutputFileForFFmpeg( + date = recordingStart, + extension = extension, + ) + + MediaConverter.concatenate( + inputFiles = filePaths, + outputFile = outputFile, + extraCommand = " -acodec copy" + ).await() + } + fun exportFolderForSettings(): String { return when (type) { BatchType.INTERNAL -> "_'internal" diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt new file mode 100644 index 000000000..865ccc0b7 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -0,0 +1,47 @@ +package app.myzel394.alibi.helpers + +import android.util.Log +import com.arthenica.ffmpegkit.FFmpegKit +import com.arthenica.ffmpegkit.ReturnCode +import kotlinx.coroutines.CompletableDeferred + +class MediaConverter { + companion object { + fun concatenate( + inputFiles: Iterable, + outputFile: String, + extraCommand: String = "", + ): CompletableDeferred { + val completer = CompletableDeferred() + + val filePathsConcatenated = inputFiles.joinToString("|") + val command = + "-protocol_whitelist saf,concat,content,file,subfile" + + " -i 'concat:$filePathsConcatenated' -y" + + extraCommand + + " $outputFile" + + FFmpegKit.executeAsync( + command + ) { session -> + if (!ReturnCode.isSuccess(session!!.returnCode)) { + Log.d( + "Audio Concatenation", + String.format( + "Command failed with state %s and rc %s.%s", + session.state, + session.returnCode, + session.failStackTrace, + ) + ) + + completer.completeExceptionally(Exception("Failed to concatenate audios")) + } else { + completer.complete(Unit) + } + } + + return completer + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt index abc7d6788..b95e3612f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt @@ -33,7 +33,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -45,7 +44,6 @@ import app.myzel394.alibi.ui.utils.rememberFileSaverDialog import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings -import app.myzel394.alibi.helpers.AudioRecorderExporter import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.ui.effects.rememberSettings import app.myzel394.alibi.ui.models.AudioRecorderModel @@ -131,16 +129,13 @@ fun AudioRecorderScreen( ?: settings.lastRecording ?: throw Exception("No recording information available") val batchesFolder = BatchesFolder.importFromFolder(recording.folderPath, context) - val outputFile = batchesFolder.getOutputFileForFFmpeg( - recording.recordingStart, - recording.fileExtension - ) - AudioRecorderExporter(recording).concatenateFiles( - batchesFolder, - outputFile, + batchesFolder.exportToOneFile( + recording.recordingStart, + recording.fileExtension, ) + // Save file val name = batchesFolder.getName( recording.recordingStart, recording.fileExtension, From f75a1a8a336f07225f6f743544086efa9095a2af Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 26 Nov 2023 22:26:51 +0100 Subject: [PATCH 011/176] chore: Rename VideoService -> VideoRecorderService --- app/src/main/AndroidManifest.xml | 6 +++++- .../alibi/services/VideoRecorderService.kt | 20 ++++--------------- .../alibi/ui/models/VideoRecorderModel.kt | 16 +++++---------- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dbaa89273..1e5db5d11 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,10 @@ + + @@ -52,7 +56,7 @@ android:name=".services.AudioRecorderService" android:foregroundServiceType="microphone" /> diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 264677422..3e607bb83 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -1,26 +1,16 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint -import android.content.ContentValues -import android.content.pm.ServiceInfo -import android.os.Build -import android.provider.MediaStore import androidx.camera.core.CameraSelector import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.video.FileOutputOptions -import androidx.camera.video.MediaStoreOutputOptions import androidx.camera.video.Quality import androidx.camera.video.QualitySelector import androidx.camera.video.Recorder import androidx.camera.video.Recording import androidx.camera.video.VideoCapture import androidx.camera.video.VideoCapture.withOutput -import androidx.core.app.NotificationCompat -import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat -import androidx.core.net.toUri -import androidx.lifecycle.LifecycleService -import app.myzel394.alibi.NotificationHelper import app.myzel394.alibi.db.RecordingInformation import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope @@ -28,12 +18,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.concurrent.Executors -import java.util.concurrent.ScheduledExecutorService -import java.util.concurrent.TimeUnit -class VideoService : IntervalRecorderService() { +class VideoRecorderService : + IntervalRecorderService() { private val job = SupervisorJob() private val scope = CoroutineScope(Dispatchers.IO + job) @@ -56,7 +44,7 @@ class VideoService : IntervalRecorderService() { - override val intentClass = VideoService::class.java + BaseRecorderModel() { + override val intentClass = VideoRecorderService::class.java - override fun onServiceConnected(service: VideoService) { - service.settings = VideoService.Settings.from() + override fun onServiceConnected(service: VideoRecorderService) { + service.settings = VideoRecorderService.Settings.from() service.clearAllRecordings() service.startRecording() From 92d1d6582a19394ad047ca976de786eccc67ae59 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 26 Nov 2023 23:01:48 +0100 Subject: [PATCH 012/176] feat: Added VideoRecorderSettings --- .../java/app/myzel394/alibi/db/AppSettings.kt | 67 ++++++++++++------- .../alibi/services/VideoRecorderService.kt | 18 +++-- 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 49b548141..2a612b9c0 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -3,20 +3,18 @@ package app.myzel394.alibi.db import android.content.Context import android.media.MediaRecorder import android.os.Build +import androidx.camera.video.Quality +import androidx.camera.video.QualitySelector import app.myzel394.alibi.R -import app.myzel394.alibi.helpers.AudioRecorderExporter import app.myzel394.alibi.helpers.BatchesFolder -import com.arthenica.ffmpegkit.FFmpegKit -import com.arthenica.ffmpegkit.ReturnCode import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json -import java.io.File import java.time.LocalDateTime -import java.time.format.DateTimeFormatter.ISO_DATE_TIME @Serializable data class AppSettings( - val audioRecorderSettings: AudioRecorderSettings = AudioRecorderSettings(), + val audioRecorderSettings: AudioRecorderSettings = AudioRecorderSettings.getDefaultInstance(), + val videoRecorderSettings: VideoRecorderSettings = VideoRecorderSettings.getDefaultInstance(), val notificationSettings: NotificationSettings? = null, val hasSeenOnboarding: Boolean = false, val showAdvancedSettings: Boolean = false, @@ -163,24 +161,6 @@ data class AudioRecorderSettings( else MediaRecorder.AudioEncoder.AMR_NB - fun getSaveFolder(context: Context): File { - val defaultFolder = AudioRecorderExporter.getFolder(context) - - if (saveFolder == null) { - return defaultFolder - } - - runCatching { - return File(saveFolder!!).apply { - if (!exists()) { - mkdirs() - } - } - } - - return defaultFolder - } - fun setIntervalDuration(duration: Long): AudioRecorderSettings { if (duration < 10 * 1000L || duration > 60 * 60 * 1000L) { throw Exception("Interval duration must be between 10 seconds and 1 hour") @@ -368,6 +348,45 @@ data class AudioRecorderSettings( } } +@Serializable +data class VideoRecorderSettings( + val targetedVideoBitRate: Int? = null, + val quality: String? = null, + val targetFrameRate: Int? = null, +) { + fun setTargetedVideoBitRate(bitRate: Int?): VideoRecorderSettings { + return copy(targetedVideoBitRate = bitRate) + } + + fun setQuality(quality: String): VideoRecorderSettings { + return copy(quality = quality) + } + + fun setTargetFrameRate(frameRate: Int?): VideoRecorderSettings { + return copy(targetFrameRate = frameRate) + } + + fun getQualitySelector(): QualitySelector? = + quality?.let { + QualitySelector.from( + QUALITY_NAME_QUALITY_MAP[it]!! + ) + } + + companion object { + fun getDefaultInstance() = VideoRecorderSettings() + + val QUALITY_NAME_QUALITY_MAP: Map = mapOf( + "LOWEST" to Quality.LOWEST, + "HIGHEST" to Quality.HIGHEST, + "SD" to Quality.SD, + "HD" to Quality.HD, + "FHD" to Quality.FHD, + "UHD" to Quality.UHD, + ) + } +} + @Serializable data class NotificationSettings( val title: String, diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 3e607bb83..aeab57557 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -1,6 +1,7 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint +import androidx.camera.core.Camera import androidx.camera.core.CameraSelector import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.video.FileOutputOptions @@ -9,7 +10,6 @@ import androidx.camera.video.QualitySelector import androidx.camera.video.Recorder import androidx.camera.video.Recording import androidx.camera.video.VideoCapture -import androidx.camera.video.VideoCapture.withOutput import androidx.core.content.ContextCompat import app.myzel394.alibi.db.RecordingInformation import kotlinx.coroutines.CompletableDeferred @@ -25,6 +25,7 @@ class VideoRecorderService : private val job = SupervisorJob() private val scope = CoroutineScope(Dispatchers.IO + job) + private var camera: Camera? = null private var cameraProvider: ProcessCameraProvider? = null private var videoCapture: VideoCapture? = null private var activeRecording: Recording? = null @@ -48,14 +49,15 @@ class VideoRecorderService : } val recorder = Recorder.Builder() - .setQualitySelector(QualitySelector.from(Quality.HIGHEST)) + .setQualitySelector(settings.quality) + .build() + videoCapture = VideoCapture.Builder(recorder) .build() - videoCapture = withOutput(recorder) runInMain { - cameraProvider!!.bindToLifecycle( + camera = cameraProvider!!.bindToLifecycle( this, - settings.camera, + settings.cameraSelector, videoCapture ) @@ -75,6 +77,7 @@ class VideoRecorderService : cameraProvider = null videoCapture = null + camera = null } override fun start() { @@ -108,7 +111,6 @@ class VideoRecorderService : super.startNewCycle() fun action() { - println("=======================") activeRecording?.stop() val newRecording = prepareVideoRecording() @@ -139,7 +141,9 @@ class VideoRecorderService : override val maxDuration: Long, override val intervalDuration: Long, val folder: String? = null, - val camera: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA, + val targetVideoBitRate: Int? = null, + val cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA, + val quality: QualitySelector = QualitySelector.from(Quality.HIGHEST), ) : IntervalRecorderService.Settings( maxDuration = maxDuration, intervalDuration = intervalDuration From 3d17012fb730e56223beabfc8c0689a1b9a89987 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:14:28 +0100 Subject: [PATCH 013/176] fix: Use Unit for completer --- .../java/app/myzel394/alibi/services/VideoRecorderService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index aeab57557..f18e62e2a 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -31,7 +31,7 @@ class VideoRecorderService : private var activeRecording: Recording? = null // Used to listen and check if the camera is available - private var _cameraAvailableListener = CompletableDeferred() + private var _cameraAvailableListener = CompletableDeferred() // Runs a function in the main thread private fun runInMain(callback: () -> Unit) { @@ -61,7 +61,7 @@ class VideoRecorderService : videoCapture ) - _cameraAvailableListener.complete(true) + _cameraAvailableListener.complete(Unit) } } From b3bb43367a03a02647486e9cd4762728ab715935 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:57:42 +0100 Subject: [PATCH 014/176] feat: Use VideoBatchesFolder and AudioBatchesFolder --- .../alibi/helpers/AudioBatchesFolder.kt | 71 +++++++++++++++++++ .../myzel394/alibi/helpers/BatchesFolder.kt | 66 ++++------------- .../myzel394/alibi/helpers/MediaConverter.kt | 58 ++++++++++++++- .../alibi/helpers/VideoBatchesFolder.kt | 71 +++++++++++++++++++ .../alibi/services/VideoRecorderService.kt | 1 + .../alibi/ui/screens/AudioRecorderScreen.kt | 6 +- 6 files changed, 216 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt create mode 100644 app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt new file mode 100644 index 000000000..47df50730 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -0,0 +1,71 @@ +package app.myzel394.alibi.helpers + +import android.content.Context +import android.net.Uri +import androidx.documentfile.provider.DocumentFile +import com.arthenica.ffmpegkit.FFmpegKitConfig +import java.time.LocalDateTime + +class AudioBatchesFolder( + override val context: Context, + override val type: BatchType, + override val customFolder: DocumentFile? = null, + override val subfolderName: String = ".recordings", +) : BatchesFolder( + context, + type, + customFolder, + subfolderName, +) { + override fun getOutputFileForFFmpeg( + date: LocalDateTime, + extension: String, + ): String { + return when (type) { + BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath + BatchType.CUSTOM -> FFmpegKitConfig.getSafParameterForWrite( + context, + customFolder!!.createFile( + "audio/${extension}", + getName(date, extension), + )!!.uri + )!! + } + } + + override suspend fun concatenate( + recordingStart: LocalDateTime, + extension: String, + disableCache: Boolean, + ) { + if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) { + return + } + + val filePaths = getBatchesForFFmpeg() + val outputFile = getOutputFileForFFmpeg( + date = recordingStart, + extension = extension, + ) + + MediaConverter.concatenateAudioFiles( + inputFiles = filePaths, + outputFile = outputFile, + ).await() + } + + companion object { + fun viaInternalFolder(context: Context): BatchesFolder { + return AudioBatchesFolder(context, BatchType.INTERNAL) + } + + fun viaCustomFolder(context: Context, folder: DocumentFile): BatchesFolder { + return AudioBatchesFolder(context, BatchType.CUSTOM, folder) + } + + fun importFromFolder(folder: String, context: Context): BatchesFolder = when (folder) { + "_'internal" -> viaInternalFolder(context) + else -> viaCustomFolder(context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!!) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index d40d5755b..234c83a3b 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -11,11 +11,11 @@ import android.net.Uri import android.os.ParcelFileDescriptor import java.io.FileDescriptor -data class BatchesFolder( - val context: Context, - val type: BatchType, - val customFolder: DocumentFile? = null, - val subfolderName: String = ".recordings", +abstract class BatchesFolder( + open val context: Context, + open val type: BatchType, + open val customFolder: DocumentFile? = null, + open val subfolderName: String = ".recordings", ) { private var customFileFileDescriptor: ParcelFileDescriptor? = null @@ -24,7 +24,7 @@ data class BatchesFolder( BatchType.INTERNAL -> getFolder(context).mkdirs() BatchType.CUSTOM -> { if (customFolder!!.findFile(subfolderName) == null) { - customFolder.createDirectory(subfolderName) + customFolder!!.createDirectory(subfolderName) } } } @@ -89,22 +89,6 @@ data class BatchesFolder( return getCustomDefinedFolder().createFile("audio/$extension", getName(date, extension))!! } - fun getOutputFileForFFmpeg( - date: LocalDateTime, - extension: String, - ): String { - return when (type) { - BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath - BatchType.CUSTOM -> FFmpegKitConfig.getSafParameterForWrite( - context, - customFolder!!.createFile( - "audio/${extension}", - getName(date, extension), - )!!.uri - )!! - } - } - fun checkIfOutputAlreadyExists( date: LocalDateTime, extension: String @@ -122,27 +106,16 @@ data class BatchesFolder( } } - suspend fun exportToOneFile( + abstract fun getOutputFileForFFmpeg( + date: LocalDateTime, + extension: String, + ): String + + abstract suspend fun concatenate( recordingStart: LocalDateTime, extension: String, disableCache: Boolean = false, - ) { - if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) { - return - } - - val filePaths = getBatchesForFFmpeg() - val outputFile = getOutputFileForFFmpeg( - date = recordingStart, - extension = extension, - ) - - MediaConverter.concatenate( - inputFiles = filePaths, - outputFile = outputFile, - extraCommand = " -acodec copy" - ).await() - } + ) fun exportFolderForSettings(): String { return when (type) { @@ -218,20 +191,7 @@ data class BatchesFolder( } companion object { - fun viaInternalFolder(context: Context): BatchesFolder { - return BatchesFolder(context, BatchType.INTERNAL) - } - - fun viaCustomFolder(context: Context, folder: DocumentFile): BatchesFolder { - return BatchesFolder(context, BatchType.CUSTOM, folder) - } - fun getFolder(context: Context) = File(context.filesDir, RECORDER_SUBFOLDER_NAME) - - fun importFromFolder(folder: String, context: Context): BatchesFolder = when (folder) { - "_'internal" -> viaInternalFolder(context) - else -> viaCustomFolder(context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!!) - } } } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index 865ccc0b7..ec2e46550 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -1,13 +1,16 @@ package app.myzel394.alibi.helpers +import android.content.Context import android.util.Log import com.arthenica.ffmpegkit.FFmpegKit import com.arthenica.ffmpegkit.ReturnCode import kotlinx.coroutines.CompletableDeferred +import java.io.File +import java.util.UUID class MediaConverter { companion object { - fun concatenate( + fun concatenateAudioFiles( inputFiles: Iterable, outputFile: String, extraCommand: String = "", @@ -17,7 +20,9 @@ class MediaConverter { val filePathsConcatenated = inputFiles.joinToString("|") val command = "-protocol_whitelist saf,concat,content,file,subfile" + - " -i 'concat:$filePathsConcatenated' -y" + + " -i 'concat:$filePathsConcatenated'" + + " -y" + + " -acodec copy" + extraCommand + " $outputFile" @@ -43,5 +48,54 @@ class MediaConverter { return completer } + + private fun createTempFile(content: String): File { + val name = UUID.randomUUID().toString() + + return File.createTempFile("temp-$name", ".txt").apply { + writeText(content) + } + } + + fun concatenateVideoFiles( + inputFiles: Iterable, + outputFile: String, + extraCommand: String = "", + ): CompletableDeferred { + val completer = CompletableDeferred() + + val listFile = createTempFile(inputFiles.joinToString("\n", prefix = "file ")) + + val command = + "-protocol_whitelist saf,concat,content,file,subfile" + + " -f concat" + + " -y" + + " -i ${listFile.absolutePath}" + + " -c copy" + + extraCommand + + " $outputFile" + + FFmpegKit.executeAsync( + command + ) { session -> + if (!ReturnCode.isSuccess(session!!.returnCode)) { + Log.d( + "Video Concatenation", + String.format( + "Command failed with state %s and rc %s.%s", + session.state, + session.returnCode, + session.failStackTrace, + ) + ) + + completer.completeExceptionally(Exception("Failed to concatenate videos")) + } else { + completer.complete(Unit) + } + } + + return completer + } } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt new file mode 100644 index 000000000..f5ace45cc --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -0,0 +1,71 @@ +package app.myzel394.alibi.helpers + +import android.content.Context +import android.net.Uri +import androidx.documentfile.provider.DocumentFile +import com.arthenica.ffmpegkit.FFmpegKitConfig +import java.time.LocalDateTime + +class VideoBatchesFolder( + override val context: Context, + override val type: BatchesFolder.BatchType, + override val customFolder: DocumentFile? = null, + override val subfolderName: String = ".recordings", +) : BatchesFolder( + context, + type, + customFolder, + subfolderName, +) { + override fun getOutputFileForFFmpeg(date: LocalDateTime, extension: String): String { + return when (type) { + BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath + BatchType.CUSTOM -> FFmpegKitConfig.getSafParameterForWrite( + context, + customFolder!!.createFile( + "video/${extension}", + getName(date, extension), + )!!.uri + )!! + } + } + + override suspend fun concatenate( + recordingStart: LocalDateTime, + extension: String, + disableCache: Boolean + ) { + if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) { + return + } + + val filePaths = getBatchesForFFmpeg() + val outputFile = getOutputFileForFFmpeg( + date = recordingStart, + extension = extension, + ) + + MediaConverter.concatenateAudioFiles( + inputFiles = filePaths, + outputFile = outputFile, + ).await() + } + + companion object { + fun viaInternalFolder(context: Context): BatchesFolder { + return VideoBatchesFolder(context, BatchType.INTERNAL) + } + + fun viaCustomFolder(context: Context, folder: DocumentFile): BatchesFolder { + return VideoBatchesFolder(context, BatchType.CUSTOM, folder) + } + + fun importFromFolder(folder: String, context: Context): BatchesFolder = when (folder) { + "_'internal" -> AudioBatchesFolder.viaInternalFolder(context) + else -> AudioBatchesFolder.viaCustomFolder( + context, + DocumentFile.fromTreeUri(context, Uri.parse(folder))!! + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index f18e62e2a..d8d23ecf3 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -11,6 +11,7 @@ import androidx.camera.video.Recorder import androidx.camera.video.Recording import androidx.camera.video.VideoCapture import androidx.core.content.ContextCompat +import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt index b95e3612f..09f0866f3 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt @@ -44,6 +44,7 @@ import app.myzel394.alibi.ui.utils.rememberFileSaverDialog import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.ui.effects.rememberSettings import app.myzel394.alibi.ui.models.AudioRecorderModel @@ -128,9 +129,10 @@ fun AudioRecorderScreen( val recording = audioRecorder.recorderService?.getRecordingInformation() ?: settings.lastRecording ?: throw Exception("No recording information available") - val batchesFolder = BatchesFolder.importFromFolder(recording.folderPath, context) + val batchesFolder = + AudioBatchesFolder.importFromFolder(recording.folderPath, context) - batchesFolder.exportToOneFile( + batchesFolder.concatenate( recording.recordingStart, recording.fileExtension, ) From 448108a974f6fc83d0b8d69a2d8e0abdcc3bce65 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 27 Nov 2023 15:03:27 +0100 Subject: [PATCH 015/176] feat: Improve settings for video recording --- .../java/app/myzel394/alibi/db/AppSettings.kt | 17 +++++++++++++++-- .../alibi/services/AudioRecorderService.kt | 5 +++-- .../alibi/services/VideoRecorderService.kt | 17 ++++++++++++----- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 2a612b9c0..63f577ad9 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -6,7 +6,8 @@ import android.os.Build import androidx.camera.video.Quality import androidx.camera.video.QualitySelector import app.myzel394.alibi.R -import app.myzel394.alibi.helpers.BatchesFolder +import app.myzel394.alibi.helpers.AudioBatchesFolder +import app.myzel394.alibi.helpers.VideoBatchesFolder import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import java.time.LocalDateTime @@ -75,9 +76,21 @@ data class RecordingInformation( val maxDuration: Long, val intervalDuration: Long, val fileExtension: String, + val type: Type, ) { fun hasRecordingsAvailable(context: Context): Boolean = - BatchesFolder.importFromFolder(folderPath, context).hasRecordingsAvailable() + when (type) { + Type.AUDIO -> AudioBatchesFolder.importFromFolder(folderPath, context) + .hasRecordingsAvailable() + + Type.VIDEO -> VideoBatchesFolder.importFromFolder(folderPath, context) + .hasRecordingsAvailable() + } + + enum class Type { + AUDIO, + VIDEO, + } } @Serializable diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index b4fd73ea2..4590ce374 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -142,6 +142,7 @@ class AudioRecorderService : maxDuration = settings.maxDuration, fileExtension = settings.fileExtension, intervalDuration = settings.intervalDuration, + type = RecordingInformation.Type.AUDIO, ) override fun startNewCycle() { @@ -214,7 +215,7 @@ class AudioRecorderService : super.onAudioDevicesAdded(addedDevices) if (selectedMicrophone == null) { - return; + return } // We can't compare the ID, as it seems to be changing on each reconnect @@ -238,7 +239,7 @@ class AudioRecorderService : super.onAudioDevicesRemoved(removedDevices) if (selectedMicrophone == null) { - return; + return } if (removedDevices?.find { it.id == selectedMicrophone!!.deviceInfo.id } != null) { diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index d8d23ecf3..4e622bc45 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -58,7 +58,7 @@ class VideoRecorderService : runInMain { camera = cameraProvider!!.bindToLifecycle( this, - settings.cameraSelector, + CameraSelector.DEFAULT_BACK_CAMERA, videoCapture ) @@ -136,6 +136,7 @@ class VideoRecorderService : maxDuration = settings.maxDuration, fileExtension = settings.fileExtension, intervalDuration = settings.intervalDuration, + type = RecordingInformation.Type.VIDEO, ) data class Settings( @@ -143,7 +144,6 @@ class VideoRecorderService : override val intervalDuration: Long, val folder: String? = null, val targetVideoBitRate: Int? = null, - val cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA, val quality: QualitySelector = QualitySelector.from(Quality.HIGHEST), ) : IntervalRecorderService.Settings( maxDuration = maxDuration, @@ -162,9 +162,16 @@ class VideoRecorderService : } companion object { - fun from() = Settings( - maxDuration = 60_000, - intervalDuration = 10_000, + fun from(appSettings: AppSettings) = Settings( + // TODO: Migrate audioSettings + maxDuration = appSettings.audioRecorderSettings.maxDuration, + intervalDuration = appSettings.audioRecorderSettings.intervalDuration, + folder = appSettings.audioRecorderSettings.saveFolder, + targetVideoBitRate = appSettings.videoRecorderSettings.targetedVideoBitRate, + quality = appSettings.videoRecorderSettings.getQualitySelector() + ?: QualitySelector.from( + Quality.HIGHEST + ), ) } } From 89baa35ed74e263a66bc47cf5e3d9fa7c146972f Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:43:31 +0100 Subject: [PATCH 016/176] fix: Properly assign batchesFolder --- .../alibi/services/AudioRecorderService.kt | 6 +++--- .../alibi/services/IntervalRecorderService.kt | 20 ++----------------- .../alibi/services/VideoRecorderService.kt | 4 ++++ .../alibi/ui/models/AudioRecorderModel.kt | 7 +++++-- .../alibi/ui/models/BaseRecorderModel.kt | 5 ++++- .../alibi/ui/models/VideoRecorderModel.kt | 2 +- .../app/myzel394/alibi/ui/screens/POCVideo.kt | 13 ++++-------- 7 files changed, 23 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index 4590ce374..ce763b535 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -6,21 +6,21 @@ import android.media.AudioDeviceInfo import android.media.AudioManager import android.media.MediaRecorder import android.media.MediaRecorder.OnErrorListener -import android.net.Uri import android.os.Build import android.os.Handler import android.os.Looper -import androidx.compose.material3.SnackbarDuration -import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.db.AudioRecorderSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState +import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.ui.utils.MicrophoneInfo import java.lang.IllegalStateException class AudioRecorderService : IntervalRecorderService() { + override var batchesFolder: BatchesFolder = AudioBatchesFolder.viaInternalFolder(this) + var amplitudesAmount = 1000 var selectedMicrophone: MicrophoneInfo? = null diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index 3bf7b7dc9..5ab1171b7 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -1,20 +1,6 @@ package app.myzel394.alibi.services -import android.media.MediaRecorder -import android.net.Uri -import androidx.documentfile.provider.DocumentFile -import app.myzel394.alibi.dataStore -import app.myzel394.alibi.db.AudioRecorderSettings -import app.myzel394.alibi.db.RecordingInformation -import app.myzel394.alibi.helpers.AudioRecorderExporter import app.myzel394.alibi.helpers.BatchesFolder -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch -import org.w3c.dom.DocumentFragment -import java.io.File import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit @@ -28,7 +14,7 @@ abstract class IntervalRecorderService private lateinit var cycleTimer: ScheduledExecutorService - var batchesFolder: BatchesFolder = BatchesFolder.viaInternalFolder(this) + abstract var batchesFolder: BatchesFolder var onCustomOutputFolderNotAccessible: () -> Unit = {} @@ -54,10 +40,8 @@ abstract class IntervalRecorderService override fun start() { batchesFolder.initFolders() if (!batchesFolder.checkIfFolderIsAccessible()) { - batchesFolder = - BatchesFolder.viaInternalFolder(this@IntervalRecorderService) - batchesFolder.initFolders() onCustomOutputFolderNotAccessible() + return } createTimer() diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 4e622bc45..d593e0c04 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -13,6 +13,8 @@ import androidx.camera.video.VideoCapture import androidx.core.content.ContextCompat import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation +import app.myzel394.alibi.helpers.BatchesFolder +import app.myzel394.alibi.helpers.VideoBatchesFolder import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -23,6 +25,8 @@ import kotlinx.coroutines.withContext class VideoRecorderService : IntervalRecorderService() { + override var batchesFolder: BatchesFolder = VideoBatchesFolder.viaInternalFolder(this) + private val job = SupervisorJob() private val scope = CoroutineScope(Dispatchers.IO + job) diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index dd633e7c3..291dd8442 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -15,6 +15,7 @@ import androidx.lifecycle.ViewModel import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState +import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.AudioRecorderExporter import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.services.AudioRecorderService @@ -67,9 +68,9 @@ class AudioRecorderModel : override fun startRecording(context: Context, settings: AppSettings) { batchesFolder = if (settings.audioRecorderSettings.saveFolder == null) - BatchesFolder.viaInternalFolder(context) + AudioBatchesFolder.viaInternalFolder(context) else - BatchesFolder.viaCustomFolder( + AudioBatchesFolder.viaCustomFolder( context, DocumentFile.fromTreeUri( context, @@ -95,6 +96,8 @@ class AudioRecorderModel : recorderService!!.changeMicrophone(microphone) if (microphone == null) { + // Microphone was reset to default, + // default is always assumed to be connected microphoneStatus = MicrophoneConnectivityStatus.CONNECTED } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 0d0d09931..1b3c10417 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -66,7 +66,10 @@ abstract class BaseRecorderModel Date: Tue, 28 Nov 2023 17:48:03 +0100 Subject: [PATCH 017/176] fix: Improve BatchesFolders; concatenate video files --- .../alibi/helpers/AudioBatchesFolder.kt | 23 ++++++++++--------- .../myzel394/alibi/helpers/BatchesFolder.kt | 2 +- .../alibi/helpers/VideoBatchesFolder.kt | 23 ++++++++++--------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt index 47df50730..e49e689c8 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -10,7 +10,7 @@ class AudioBatchesFolder( override val context: Context, override val type: BatchType, override val customFolder: DocumentFile? = null, - override val subfolderName: String = ".recordings", + override val subfolderName: String = ".audio_recordings", ) : BatchesFolder( context, type, @@ -37,21 +37,22 @@ class AudioBatchesFolder( recordingStart: LocalDateTime, extension: String, disableCache: Boolean, - ) { - if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) { - return - } - - val filePaths = getBatchesForFFmpeg() + ): String { val outputFile = getOutputFileForFFmpeg( date = recordingStart, extension = extension, ) - MediaConverter.concatenateAudioFiles( - inputFiles = filePaths, - outputFile = outputFile, - ).await() + if (disableCache || checkIfOutputAlreadyExists(recordingStart, extension)) { + val filePaths = getBatchesForFFmpeg() + + MediaConverter.concatenateAudioFiles( + inputFiles = filePaths, + outputFile = outputFile, + ).await() + } + + return outputFile } companion object { diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 234c83a3b..c801e6e04 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -115,7 +115,7 @@ abstract class BatchesFolder( recordingStart: LocalDateTime, extension: String, disableCache: Boolean = false, - ) + ): String fun exportFolderForSettings(): String { return when (type) { diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index f5ace45cc..db84f1fe2 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -10,7 +10,7 @@ class VideoBatchesFolder( override val context: Context, override val type: BatchesFolder.BatchType, override val customFolder: DocumentFile? = null, - override val subfolderName: String = ".recordings", + override val subfolderName: String = ".video_recordings", ) : BatchesFolder( context, type, @@ -34,21 +34,22 @@ class VideoBatchesFolder( recordingStart: LocalDateTime, extension: String, disableCache: Boolean - ) { - if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) { - return - } - - val filePaths = getBatchesForFFmpeg() + ): String { val outputFile = getOutputFileForFFmpeg( date = recordingStart, extension = extension, ) - MediaConverter.concatenateAudioFiles( - inputFiles = filePaths, - outputFile = outputFile, - ).await() + if (disableCache || !checkIfOutputAlreadyExists(recordingStart, extension)) { + val filePaths = getBatchesForFFmpeg() + + MediaConverter.concatenateVideoFiles( + inputFiles = filePaths, + outputFile = outputFile, + ).await() + } + + return outputFile } companion object { From 1eb7a7dd9ae562dae06868cc2a5e880ce93125c5 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:48:13 +0100 Subject: [PATCH 018/176] fix: Remove unused AudioRecorderExporter.kt --- .../alibi/helpers/AudioRecorderExporter.kt | 66 ------------------- 1 file changed, 66 deletions(-) delete mode 100644 app/src/main/java/app/myzel394/alibi/helpers/AudioRecorderExporter.kt diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioRecorderExporter.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioRecorderExporter.kt deleted file mode 100644 index 7a30d5cdc..000000000 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioRecorderExporter.kt +++ /dev/null @@ -1,66 +0,0 @@ -package app.myzel394.alibi.helpers - -import android.content.Context -import android.net.Uri -import android.system.Os -import android.util.Log -import androidx.documentfile.provider.DocumentFile -import app.myzel394.alibi.db.RecordingInformation -import app.myzel394.alibi.ui.RECORDER_SUBFOLDER_NAME -import com.arthenica.ffmpegkit.FFmpegKit -import com.arthenica.ffmpegkit.FFmpegKitConfig -import com.arthenica.ffmpegkit.ReturnCode -import java.io.File -import java.time.format.DateTimeFormatter - -data class AudioRecorderExporter( - val recording: RecordingInformation, -) { - suspend fun concatenateFiles( - batchesFolder: BatchesFolder, - outputFilePath: String, - forceConcatenation: Boolean = false, - ) { - val filePaths = batchesFolder.getBatchesForFFmpeg() - - if (batchesFolder.checkIfOutputAlreadyExists( - recording.recordingStart, - recording.fileExtension - ) && !forceConcatenation - ) { - return - } - - val filePathsConcatenated = filePaths.joinToString("|") - val command = - "-protocol_whitelist saf,concat,content,file,subfile" + - " -i 'concat:$filePathsConcatenated' -y" + - " -acodec copy" + - " -metadata date='${recording.recordingStart.format(DateTimeFormatter.ISO_DATE_TIME)}'" + - " -metadata batch_count='${filePaths.size}'" + - " -metadata batch_duration='${recording.intervalDuration}'" + - " -metadata max_duration='${recording.maxDuration}'" + - " $outputFilePath" - - val session = FFmpegKit.execute(command) - - if (!ReturnCode.isSuccess(session.returnCode)) { - Log.d( - "Audio Concatenation", - String.format( - "Command failed with state %s and rc %s.%s", - session.state, - session.returnCode, - session.failStackTrace, - ) - ) - - throw Exception("Failed to concatenate audios") - } - } - - companion object { - fun getFolder(context: Context) = File(context.filesDir, RECORDER_SUBFOLDER_NAME) - } -} - From fe9d9d7298de2ce4a118ca0827484a5e472e12ad Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Tue, 28 Nov 2023 18:59:01 +0100 Subject: [PATCH 019/176] debug: current stand trying to make ffmpeg to work --- .../alibi/helpers/AudioBatchesFolder.kt | 11 ++--- .../myzel394/alibi/helpers/BatchesFolder.kt | 8 +--- .../myzel394/alibi/helpers/MediaConverter.kt | 4 +- .../alibi/helpers/VideoBatchesFolder.kt | 15 +++---- .../alibi/services/RecorderService.kt | 8 ++-- .../alibi/services/VideoRecorderService.kt | 40 ++++++++++++++----- .../organisms/RecordingStatus.kt | 5 +-- .../alibi/ui/models/AudioRecorderModel.kt | 16 +------- .../alibi/ui/models/BaseRecorderModel.kt | 17 ++++---- .../alibi/ui/models/VideoRecorderModel.kt | 4 +- .../alibi/ui/screens/AudioRecorderScreen.kt | 2 +- .../app/myzel394/alibi/ui/screens/POCVideo.kt | 19 ++++++++- 12 files changed, 80 insertions(+), 69 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt index e49e689c8..187386754 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -56,15 +56,12 @@ class AudioBatchesFolder( } companion object { - fun viaInternalFolder(context: Context): BatchesFolder { - return AudioBatchesFolder(context, BatchType.INTERNAL) - } + fun viaInternalFolder(context: Context) = AudioBatchesFolder(context, BatchType.INTERNAL) - fun viaCustomFolder(context: Context, folder: DocumentFile): BatchesFolder { - return AudioBatchesFolder(context, BatchType.CUSTOM, folder) - } + fun viaCustomFolder(context: Context, folder: DocumentFile) = + AudioBatchesFolder(context, BatchType.CUSTOM, folder) - fun importFromFolder(folder: String, context: Context): BatchesFolder = when (folder) { + fun importFromFolder(folder: String, context: Context) = when (folder) { "_'internal" -> viaInternalFolder(context) else -> viaCustomFolder(context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!!) } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index c801e6e04..c9c2827b0 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -21,7 +21,7 @@ abstract class BatchesFolder( fun initFolders() { when (type) { - BatchType.INTERNAL -> getFolder(context).mkdirs() + BatchType.INTERNAL -> getInternalFolder().mkdirs() BatchType.CUSTOM -> { if (customFolder!!.findFile(subfolderName) == null) { customFolder!!.createDirectory(subfolderName) @@ -35,7 +35,7 @@ abstract class BatchesFolder( } fun getInternalFolder(): File { - return getFolder(context) + return File(context.filesDir, subfolderName) } fun getCustomDefinedFolder(): DocumentFile { @@ -189,9 +189,5 @@ abstract class BatchesFolder( INTERNAL, CUSTOM, } - - companion object { - fun getFolder(context: Context) = File(context.filesDir, RECORDER_SUBFOLDER_NAME) - } } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index ec2e46550..d2dd32451 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -67,9 +67,9 @@ class MediaConverter { val listFile = createTempFile(inputFiles.joinToString("\n", prefix = "file ")) val command = - "-protocol_whitelist saf,concat,content,file,subfile" + - " -f concat" + + " -f concat" + " -y" + + " -safe 0" + " -i ${listFile.absolutePath}" + " -c copy" + extraCommand + diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index db84f1fe2..ba01cb95c 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -53,17 +53,14 @@ class VideoBatchesFolder( } companion object { - fun viaInternalFolder(context: Context): BatchesFolder { - return VideoBatchesFolder(context, BatchType.INTERNAL) - } + fun viaInternalFolder(context: Context) = VideoBatchesFolder(context, BatchType.INTERNAL) - fun viaCustomFolder(context: Context, folder: DocumentFile): BatchesFolder { - return VideoBatchesFolder(context, BatchType.CUSTOM, folder) - } + fun viaCustomFolder(context: Context, folder: DocumentFile) = + VideoBatchesFolder(context, BatchType.CUSTOM, folder) - fun importFromFolder(folder: String, context: Context): BatchesFolder = when (folder) { - "_'internal" -> AudioBatchesFolder.viaInternalFolder(context) - else -> AudioBatchesFolder.viaCustomFolder( + fun importFromFolder(folder: String, context: Context) = when (folder) { + "_'internal" -> viaInternalFolder(context) + else -> viaCustomFolder( context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!! ) diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index e80c1ffa9..e550dfa43 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -162,12 +162,14 @@ abstract class RecorderService : LifecycleService() { changeState(RecorderState.RECORDING) } + fun stopRecording() { + changeState(RecorderState.IDLE) + stop() + } + override fun onDestroy() { super.onDestroy() - stop() - changeState(RecorderState.IDLE) - stopForeground(STOP_FOREGROUND_REMOVE) NotificationManagerCompat.from(this) .cancel(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID) diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index d593e0c04..e5334ed02 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -1,6 +1,7 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint +import android.util.Range import androidx.camera.core.Camera import androidx.camera.core.CameraSelector import androidx.camera.lifecycle.ProcessCameraProvider @@ -38,6 +39,8 @@ class VideoRecorderService : // Used to listen and check if the camera is available private var _cameraAvailableListener = CompletableDeferred() + private var selectedCamera: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA + // Runs a function in the main thread private fun runInMain(callback: () -> Unit) { val mainHandler = ContextCompat.getMainExecutor(this) @@ -45,6 +48,23 @@ class VideoRecorderService : mainHandler.execute(callback) } + private fun buildRecorder() = Recorder.Builder() + .setQualitySelector(settings.quality) + .apply { + if (settings.targetVideoBitRate != null) { + setTargetVideoEncodingBitRate(settings.targetVideoBitRate!!) + } + } + .build() + + private fun buildVideoCapture(recorder: Recorder) = VideoCapture.Builder(recorder) + .apply { + if (settings.targetFrameRate != null) { + setTargetFrameRate(Range(settings.targetFrameRate!!, settings.targetFrameRate!!)) + } + } + .build() + // Open the camera. // Used to open it for a longer time, shouldn't be called when pausing / resuming. // This should only be called when starting a recording. @@ -53,16 +73,13 @@ class VideoRecorderService : ProcessCameraProvider.getInstance(this@VideoRecorderService).get() } - val recorder = Recorder.Builder() - .setQualitySelector(settings.quality) - .build() - videoCapture = VideoCapture.Builder(recorder) - .build() + val recorder = buildRecorder() + videoCapture = buildVideoCapture(recorder) runInMain { camera = cameraProvider!!.bindToLifecycle( this, - CameraSelector.DEFAULT_BACK_CAMERA, + selectedCamera, videoCapture ) @@ -74,7 +91,7 @@ class VideoRecorderService : // Used to close it finally, shouldn't be called when pausing / resuming. // This should only be called after recording has finished. private fun closeCamera() { - clearOldVideoRecording() + stopActiveRecording() runCatching { cameraProvider?.unbindAll() @@ -99,10 +116,8 @@ class VideoRecorderService : closeCamera() } - private fun clearOldVideoRecording() { - runCatching { - activeRecording?.stop() - } + private fun stopActiveRecording() { + activeRecording?.stop() } @SuppressLint("MissingPermission") @@ -143,11 +158,13 @@ class VideoRecorderService : type = RecordingInformation.Type.VIDEO, ) + // TODO: Save camera selector as it doesn't make sense to change the camera midway data class Settings( override val maxDuration: Long, override val intervalDuration: Long, val folder: String? = null, val targetVideoBitRate: Int? = null, + val targetFrameRate: Int? = null, val quality: QualitySelector = QualitySelector.from(Quality.HIGHEST), ) : IntervalRecorderService.Settings( maxDuration = maxDuration, @@ -172,6 +189,7 @@ class VideoRecorderService : intervalDuration = appSettings.audioRecorderSettings.intervalDuration, folder = appSettings.audioRecorderSettings.saveFolder, targetVideoBitRate = appSettings.videoRecorderSettings.targetedVideoBitRate, + targetFrameRate = appSettings.videoRecorderSettings.targetFrameRate, quality = appSettings.videoRecorderSettings.getQualitySelector() ?: QualitySelector.from( Quality.HIGHEST diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/RecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/RecordingStatus.kt index ac2b163d8..d76154d75 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/RecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/RecordingStatus.kt @@ -26,7 +26,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings -import app.myzel394.alibi.helpers.AudioRecorderExporter import app.myzel394.alibi.ui.components.AudioRecorder.atoms.DeleteButton import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneDisconnectedDialog import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneReconnectedDialog @@ -106,7 +105,7 @@ fun RecordingStatus( ) { DeleteButton( onDelete = { - audioRecorder.stopRecording(context) + //audioRecorder.stopRecording(context) audioRecorder.batchesFolder!!.deleteRecordings(); } ) @@ -136,7 +135,7 @@ fun RecordingStatus( SaveButton( onSave = { runCatching { - audioRecorder.stopRecording(context) + //audioRecorder.stopRecording(context) } audioRecorder.onRecordingSave() } diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index 291dd8442..0395f8c2d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -1,32 +1,20 @@ package app.myzel394.alibi.ui.models -import android.content.ComponentName import android.content.Context -import android.content.Intent -import android.content.ServiceConnection import android.net.Uri -import android.os.IBinder import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.core.content.ContextCompat import androidx.documentfile.provider.DocumentFile -import androidx.lifecycle.ViewModel import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation -import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.AudioBatchesFolder -import app.myzel394.alibi.helpers.AudioRecorderExporter -import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.services.AudioRecorderService -import app.myzel394.alibi.services.IntervalRecorderService -import app.myzel394.alibi.services.RecorderNotificationHelper -import app.myzel394.alibi.services.RecorderService -import kotlinx.serialization.json.Json import app.myzel394.alibi.ui.utils.MicrophoneInfo class AudioRecorderModel : - BaseRecorderModel() { + BaseRecorderModel() { + override var batchesFolder: AudioBatchesFolder? = null override val intentClass = AudioRecorderService::class.java var amplitudes by mutableStateOf>(emptyList()) diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 1b3c10417..13e5e3486 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -19,9 +19,10 @@ import app.myzel394.alibi.services.AudioRecorderService import app.myzel394.alibi.services.IntervalRecorderService import app.myzel394.alibi.services.RecorderNotificationHelper import app.myzel394.alibi.services.RecorderService +import kotlinx.coroutines.delay import kotlinx.serialization.json.Json -abstract class BaseRecorderModel> : +abstract class BaseRecorderModel, B : BatchesFolder?> : ViewModel() { protected abstract val intentClass: Class @@ -44,7 +45,7 @@ abstract class BaseRecorderModel Unit = {} var onError: () -> Unit = {} - var batchesFolder: BatchesFolder? = null + abstract var batchesFolder: B private var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null @@ -69,6 +70,8 @@ abstract class BaseRecorderModel() { + BaseRecorderModel() { + override var batchesFolder: VideoBatchesFolder? = null override val intentClass = VideoRecorderService::class.java override fun onServiceConnected(service: VideoRecorderService) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt index 09f0866f3..ed6f35449 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt @@ -178,7 +178,7 @@ fun AudioRecorderScreen( audioRecorder.onError = { saveAsLastRecording() - audioRecorder.stopRecording(context) + //audioRecorder.stopRecording(context) showRecorderError = true } diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt index 28930c448..4440718bd 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt @@ -9,6 +9,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier @@ -16,6 +17,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.models.VideoRecorderModel +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch @SuppressLint("NewApi") @Composable @@ -29,6 +32,8 @@ fun POCVideo( mutableStateOf(false) } + val scope = rememberCoroutineScope() + Box( modifier = Modifier .fillMaxSize() @@ -38,9 +43,19 @@ fun POCVideo( if (!started) { videoRecorder.startRecording(context, settings) } else { - videoRecorder.stopRecording(context) + scope.launch { + val information = videoRecorder.recorderService!!.getRecordingInformation() + val batchesFolder = videoRecorder.batchesFolder!! + videoRecorder.stopRecording(context) + + delay(5000) - val folder = "content://media/external/video/media/DCIM/Recordings" + batchesFolder.concatenate( + recordingStart = information.recordingStart, + extension = information.fileExtension, + disableCache = true, + ) + } } started = !started From 1be3912812cdab382fc5a69a2ab2855d92c999e2 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 29 Nov 2023 18:31:53 +0100 Subject: [PATCH 020/176] refactor: Improve BatchesFolder concatenation behavior --- .../alibi/helpers/AudioBatchesFolder.kt | 39 ++++++-------- .../myzel394/alibi/helpers/BatchesFolder.kt | 37 +++++++++++-- .../myzel394/alibi/helpers/MediaConverter.kt | 10 ++-- .../alibi/helpers/VideoBatchesFolder.kt | 53 +++++++++++-------- 4 files changed, 87 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt index 187386754..891a54a4c 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -3,6 +3,7 @@ package app.myzel394.alibi.helpers import android.content.Context import android.net.Uri import androidx.documentfile.provider.DocumentFile +import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateAudioFiles import com.arthenica.ffmpegkit.FFmpegKitConfig import java.time.LocalDateTime @@ -17,6 +18,9 @@ class AudioBatchesFolder( customFolder, subfolderName, ) { + override val concatenateFunction = ::concatenateAudioFiles + override val ffmpegParameters = FFMPEG_PARAMETERS + override fun getOutputFileForFFmpeg( date: LocalDateTime, extension: String, @@ -33,28 +37,6 @@ class AudioBatchesFolder( } } - override suspend fun concatenate( - recordingStart: LocalDateTime, - extension: String, - disableCache: Boolean, - ): String { - val outputFile = getOutputFileForFFmpeg( - date = recordingStart, - extension = extension, - ) - - if (disableCache || checkIfOutputAlreadyExists(recordingStart, extension)) { - val filePaths = getBatchesForFFmpeg() - - MediaConverter.concatenateAudioFiles( - inputFiles = filePaths, - outputFile = outputFile, - ).await() - } - - return outputFile - } - companion object { fun viaInternalFolder(context: Context) = AudioBatchesFolder(context, BatchType.INTERNAL) @@ -65,5 +47,18 @@ class AudioBatchesFolder( "_'internal" -> viaInternalFolder(context) else -> viaCustomFolder(context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!!) } + + // Parameters to be passed in descending order + // Those parameters first try to concatenate without re-encoding + // if that fails, it'll try several fallback methods + // this is audio only + val FFMPEG_PARAMETERS = arrayOf( + " -c copy", + " -acodec copy", + " -c:a aac", + " -c:a libmp3lame", + " -c:a libopus", + " -c:a libvorbis", + ) } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index c9c2827b0..3eb187f23 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -2,14 +2,14 @@ package app.myzel394.alibi.helpers import android.content.Context import androidx.documentfile.provider.DocumentFile -import app.myzel394.alibi.ui.RECORDER_SUBFOLDER_NAME import java.io.File import java.time.LocalDateTime import java.time.format.DateTimeFormatter import com.arthenica.ffmpegkit.FFmpegKitConfig -import android.net.Uri import android.os.ParcelFileDescriptor +import kotlinx.coroutines.CompletableDeferred import java.io.FileDescriptor +import kotlin.reflect.KFunction3 abstract class BatchesFolder( open val context: Context, @@ -19,6 +19,9 @@ abstract class BatchesFolder( ) { private var customFileFileDescriptor: ParcelFileDescriptor? = null + abstract val concatenateFunction: KFunction3, String, String, CompletableDeferred> + abstract val ffmpegParameters: Array + fun initFolders() { when (type) { BatchType.INTERNAL -> getInternalFolder().mkdirs() @@ -111,11 +114,37 @@ abstract class BatchesFolder( extension: String, ): String - abstract suspend fun concatenate( + open suspend fun concatenate( recordingStart: LocalDateTime, extension: String, disableCache: Boolean = false, - ): String + onNextParameterTry: (String) -> Unit = {}, + ): String { + val outputFile = getOutputFileForFFmpeg( + date = recordingStart, + extension = extension, + ) + + if (disableCache || !checkIfOutputAlreadyExists(recordingStart, extension)) { + val filePaths = getBatchesForFFmpeg() + + for (parameter in ffmpegParameters) { + onNextParameterTry(parameter) + + try { + concatenateFunction( + filePaths, + outputFile, + parameter, + ) + } catch (e: MediaConverter.FFmpegException) { + continue + } + } + } + + return outputFile + } fun exportFolderForSettings(): String { return when (type) { diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index d2dd32451..d45d95ea2 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -22,7 +22,6 @@ class MediaConverter { "-protocol_whitelist saf,concat,content,file,subfile" + " -i 'concat:$filePathsConcatenated'" + " -y" + - " -acodec copy" + extraCommand + " $outputFile" @@ -68,16 +67,17 @@ class MediaConverter { val command = " -f concat" + - " -y" + " -safe 0" + " -i ${listFile.absolutePath}" + - " -c copy" + extraCommand + + " -y" + " $outputFile" FFmpegKit.executeAsync( command ) { session -> + listFile.delete() + if (!ReturnCode.isSuccess(session!!.returnCode)) { Log.d( "Video Concatenation", @@ -89,7 +89,7 @@ class MediaConverter { ) ) - completer.completeExceptionally(Exception("Failed to concatenate videos")) + completer.completeExceptionally(FFmpegException("Failed to concatenate videos")) } else { completer.complete(Unit) } @@ -98,4 +98,6 @@ class MediaConverter { return completer } } + + class FFmpegException(message: String) : Exception(message) } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index ba01cb95c..990b25610 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -3,7 +3,9 @@ package app.myzel394.alibi.helpers import android.content.Context import android.net.Uri import androidx.documentfile.provider.DocumentFile +import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles import com.arthenica.ffmpegkit.FFmpegKitConfig +import com.arthenica.ffmpegkit.ReturnCode import java.time.LocalDateTime class VideoBatchesFolder( @@ -17,6 +19,9 @@ class VideoBatchesFolder( customFolder, subfolderName, ) { + override val concatenateFunction = ::concatenateVideoFiles + override val ffmpegParameters = FFMPEG_PARAMETERS + override fun getOutputFileForFFmpeg(date: LocalDateTime, extension: String): String { return when (type) { BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath @@ -30,28 +35,6 @@ class VideoBatchesFolder( } } - override suspend fun concatenate( - recordingStart: LocalDateTime, - extension: String, - disableCache: Boolean - ): String { - val outputFile = getOutputFileForFFmpeg( - date = recordingStart, - extension = extension, - ) - - if (disableCache || !checkIfOutputAlreadyExists(recordingStart, extension)) { - val filePaths = getBatchesForFFmpeg() - - MediaConverter.concatenateVideoFiles( - inputFiles = filePaths, - outputFile = outputFile, - ).await() - } - - return outputFile - } - companion object { fun viaInternalFolder(context: Context) = VideoBatchesFolder(context, BatchType.INTERNAL) @@ -65,5 +48,31 @@ class VideoBatchesFolder( DocumentFile.fromTreeUri(context, Uri.parse(folder))!! ) } + + // Parameters to be passed in descending order + // Those parameters first try to concatenate without re-encoding + // if that fails, it'll try several fallback methods + val FFMPEG_PARAMETERS = arrayOf( + " -c copy", + " -c:v copy", + " -c:v copy -c:a aac", + " -c:v copy -c:a libmp3lame", + " -c:v copy -c:a libopus", + " -c:v copy -c:a libvorbis", + " -c:a copy", + // There's nothing else we can do to avoid re-encoding, + // so we'll just have to re-encode the whole thing + " -c:v libx264 -c:a copy", + " -c:v libx264 -c:a aac", + " -c:v libx265 -c:a aac", + " -c:v libx264 -c:a libmp3lame", + " -c:v libx264 -c:a libopus", + " -c:v libx264 -c:a libvorbis", + " -c:v libx265 -c:a copy", + " -c:v libx265 -c:a aac", + " -c:v libx265 -c:a libmp3lame", + " -c:v libx265 -c:a libopus", + " -c:v libx265 -c:a libvorbis", + ) } } \ No newline at end of file From 0d618380fa642cf135e4c782fc840174c9dfbc3d Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 29 Nov 2023 19:02:32 +0100 Subject: [PATCH 021/176] current stand: trying to improve concatenation of file --- app/build.gradle | 2 +- .../main/java/app/myzel394/alibi/helpers/BatchesFolder.kt | 2 ++ .../main/java/app/myzel394/alibi/helpers/MediaConverter.kt | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8f5b38507..4d282bba9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -119,7 +119,7 @@ dependencies { annotationProcessor 'com.google.dagger:hilt-compiler:2.46.1' implementation "androidx.hilt:hilt-navigation-compose:1.1.0" - implementation 'com.arthenica:ffmpeg-kit-min-gpl:5.1' + implementation 'com.arthenica:ffmpeg-kit-full-gpl:5.1' implementation "androidx.datastore:datastore-preferences:1.0.0" diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 3eb187f23..6582d8691 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -7,6 +7,7 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import com.arthenica.ffmpegkit.FFmpegKitConfig import android.os.ParcelFileDescriptor +import android.util.Log import kotlinx.coroutines.CompletableDeferred import java.io.FileDescriptor import kotlin.reflect.KFunction3 @@ -129,6 +130,7 @@ abstract class BatchesFolder( val filePaths = getBatchesForFFmpeg() for (parameter in ffmpegParameters) { + Log.i("Concatenation", "Trying parameter $parameter") onNextParameterTry(parameter) try { diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index d45d95ea2..7d73732f8 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -49,9 +49,9 @@ class MediaConverter { } private fun createTempFile(content: String): File { - val name = UUID.randomUUID().toString() + val id = UUID.randomUUID().toString() - return File.createTempFile("temp-$name", ".txt").apply { + return File.createTempFile(".temp-ffmpeg-files-$id", ".txt").apply { writeText(content) } } @@ -63,7 +63,7 @@ class MediaConverter { ): CompletableDeferred { val completer = CompletableDeferred() - val listFile = createTempFile(inputFiles.joinToString("\n", prefix = "file ")) + val listFile = createTempFile(inputFiles.joinToString("\n") { "file '$it'" }) val command = " -f concat" + From 3cee858b56fb4270a9f274a9f43387277650730f Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:20:48 +0100 Subject: [PATCH 022/176] fix: Fix ffmpeg --- .../main/java/app/myzel394/alibi/helpers/BatchesFolder.kt | 5 +++-- .../main/java/app/myzel394/alibi/helpers/MediaConverter.kt | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 6582d8691..0525b101b 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -138,14 +138,15 @@ abstract class BatchesFolder( filePaths, outputFile, parameter, - ) + ).await() + return outputFile } catch (e: MediaConverter.FFmpegException) { continue } } } - return outputFile + throw MediaConverter.FFmpegException("Failed to concatenate") } fun exportFolderForSettings(): String { diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index 7d73732f8..b438bc42e 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -70,13 +70,16 @@ class MediaConverter { " -safe 0" + " -i ${listFile.absolutePath}" + extraCommand + + " -strict normal" + " -y" + " $outputFile" FFmpegKit.executeAsync( command ) { session -> - listFile.delete() + runCatching { + listFile.delete() + } if (!ReturnCode.isSuccess(session!!.returnCode)) { Log.d( From 569794d437c41541a442cb1ae295778995135f4b Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 1 Dec 2023 00:04:14 +0100 Subject: [PATCH 023/176] feat: Wait for camera to finish completely --- .../alibi/services/AudioRecorderService.kt | 2 +- .../alibi/services/IntervalRecorderService.kt | 2 +- .../alibi/services/RecorderService.kt | 4 +-- .../alibi/services/VideoRecorderService.kt | 25 +++++++++++++------ .../alibi/ui/models/BaseRecorderModel.kt | 1 - .../app/myzel394/alibi/ui/screens/POCVideo.kt | 2 -- 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index ce763b535..57f4a2c48 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -176,7 +176,7 @@ class AudioRecorderService : resetRecorder() } - override fun stop() { + override suspend fun stop() { super.stop() resetRecorder() diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index 5ab1171b7..238830860 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -55,7 +55,7 @@ abstract class IntervalRecorderService createTimer() } - override fun stop() { + override suspend fun stop() { cycleTimer.shutdown() } diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index e550dfa43..7b5865a72 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -44,7 +44,7 @@ abstract class RecorderService : LifecycleService() { protected abstract fun start() protected abstract fun pause() protected abstract fun resume() - protected abstract fun stop() + protected abstract suspend fun stop() override fun onBind(intent: Intent): IBinder? { super.onBind(intent) @@ -162,7 +162,7 @@ abstract class RecorderService : LifecycleService() { changeState(RecorderState.RECORDING) } - fun stopRecording() { + suspend fun stopRecording() { changeState(RecorderState.IDLE) stop() } diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index e5334ed02..2bed4c8b8 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.util.Range import androidx.camera.core.Camera import androidx.camera.core.CameraSelector +import androidx.camera.core.impl.CameraConfig import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.video.FileOutputOptions import androidx.camera.video.Quality @@ -11,6 +12,7 @@ import androidx.camera.video.QualitySelector import androidx.camera.video.Recorder import androidx.camera.video.Recording import androidx.camera.video.VideoCapture +import androidx.camera.video.VideoRecordEvent import androidx.core.content.ContextCompat import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation @@ -38,11 +40,12 @@ class VideoRecorderService : // Used to listen and check if the camera is available private var _cameraAvailableListener = CompletableDeferred() + private var _cameraClosedListener = CompletableDeferred() private var selectedCamera: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA // Runs a function in the main thread - private fun runInMain(callback: () -> Unit) { + private fun runOnMain(callback: () -> Unit) { val mainHandler = ContextCompat.getMainExecutor(this) mainHandler.execute(callback) @@ -76,7 +79,7 @@ class VideoRecorderService : val recorder = buildRecorder() videoCapture = buildVideoCapture(recorder) - runInMain { + runOnMain { camera = cameraProvider!!.bindToLifecycle( this, selectedCamera, @@ -91,10 +94,10 @@ class VideoRecorderService : // Used to close it finally, shouldn't be called when pausing / resuming. // This should only be called after recording has finished. private fun closeCamera() { - stopActiveRecording() - runCatching { - cameraProvider?.unbindAll() + runOnMain { + cameraProvider?.unbindAll() + } } cameraProvider = null @@ -110,9 +113,12 @@ class VideoRecorderService : } } - override fun stop() { + override suspend fun stop() { super.stop() + stopActiveRecording() + _cameraClosedListener.await() + // Camera can only be closed after the recording has been finalized closeCamera() } @@ -134,7 +140,12 @@ class VideoRecorderService : activeRecording?.stop() val newRecording = prepareVideoRecording() - activeRecording = newRecording.start(ContextCompat.getMainExecutor(this), {}) + activeRecording = newRecording.start(ContextCompat.getMainExecutor(this)) { event -> + // TODO: Add timeout to completer + if (event is VideoRecordEvent.Finalize) { + _cameraClosedListener.complete(Unit) + } + } } if (_cameraAvailableListener.isCompleted) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 13e5e3486..9152a30fb 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -128,7 +128,6 @@ abstract class BaseRecorderModel Date: Fri, 1 Dec 2023 00:13:51 +0100 Subject: [PATCH 024/176] feat: Add timeout to camera closing --- .../myzel394/alibi/services/VideoRecorderService.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 2bed4c8b8..11cc34dc9 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -24,7 +24,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout +import kotlinx.coroutines.withTimeoutOrNull +const val CAMERA_CLOSE_TIMEOUT = 20000L class VideoRecorderService : IntervalRecorderService() { @@ -117,8 +120,12 @@ class VideoRecorderService : super.stop() stopActiveRecording() - _cameraClosedListener.await() - // Camera can only be closed after the recording has been finalized + + withTimeoutOrNull(CAMERA_CLOSE_TIMEOUT) { + // Camera can only be closed after the recording has been finalized + _cameraClosedListener.await() + } + closeCamera() } @@ -141,7 +148,6 @@ class VideoRecorderService : val newRecording = prepareVideoRecording() activeRecording = newRecording.start(ContextCompat.getMainExecutor(this)) { event -> - // TODO: Add timeout to completer if (event is VideoRecordEvent.Finalize) { _cameraClosedListener.complete(Unit) } From 79b33ced2ecee80d287814767aecfe9b66579877 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 1 Dec 2023 00:18:04 +0100 Subject: [PATCH 025/176] refactor: Small improvements for changeState in RecorderService --- .../alibi/services/RecorderService.kt | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index 7b5865a72..f69c7928e 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -91,6 +91,14 @@ abstract class RecorderService : LifecycleService() { } } + protected fun _changeStateValue(newState: RecorderState) { + state = newState + + onStateChange?.invoke(newState) + } + + // Used to change the state of the service + // will internally call start() / pause() / resume() / stop() @SuppressLint("MissingPermission") fun changeState(newState: RecorderState) { if (state == newState) { @@ -106,27 +114,23 @@ abstract class RecorderService : LifecycleService() { } else { start() } + + createRecordingTimeTimer() } RecorderState.PAUSED -> { pause() isPaused = true - } - else -> {} - } - - when (newState) { - RecorderState.RECORDING -> { - createRecordingTimeTimer() + recordingTimeTimer.shutdown() } - RecorderState.PAUSED, RecorderState.IDLE -> { + RecorderState.IDLE -> { recordingTimeTimer.shutdown() } } - + // Update notification if ( arrayOf( RecorderState.RECORDING, @@ -140,7 +144,8 @@ abstract class RecorderService : LifecycleService() { notification ) } - onStateChange?.invoke(newState) + + _changeStateValue(newState) } // Must be immediately called after creating the service! From c40361aced05d8b4b527170470384663c1c6eac3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 1 Dec 2023 00:20:23 +0100 Subject: [PATCH 026/176] fix: Add pause method to VideoRecorderService --- .../app/myzel394/alibi/services/VideoRecorderService.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 11cc34dc9..734339878 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -129,6 +129,14 @@ class VideoRecorderService : closeCamera() } + override fun pause() { + super.pause() + + stopActiveRecording() + } + + // `resume` override not needed as `startNewCycle` is called by `IntervalRecorderService` + private fun stopActiveRecording() { activeRecording?.stop() } From 65be9a1e3e3a7c401ef9d743a1ea6f4efa396b0e Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 1 Dec 2023 00:29:41 +0100 Subject: [PATCH 027/176] chore: Move code for better readability --- .../alibi/services/AudioRecorderService.kt | 145 +++++++++--------- .../alibi/services/VideoRecorderService.kt | 131 +++++++++------- 2 files changed, 153 insertions(+), 123 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index 57f4a2c48..a1cb8597c 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -21,21 +21,79 @@ class AudioRecorderService : IntervalRecorderService() { override var batchesFolder: BatchesFolder = AudioBatchesFolder.viaInternalFolder(this) + private val handler = Handler(Looper.getMainLooper()) + + var amplitudes = mutableListOf() + private set var amplitudesAmount = 1000 + var selectedMicrophone: MicrophoneInfo? = null var recorder: MediaRecorder? = null private set + + // Callbacks var onSelectedMicrophoneChange: (MicrophoneInfo?) -> Unit = {} var onMicrophoneDisconnected: () -> Unit = {} var onMicrophoneReconnected: () -> Unit = {} + var onAmplitudeChange: ((List) -> Unit)? = null - var amplitudes = mutableListOf() - private set + override fun startNewCycle() { + super.startNewCycle() - private val handler = Handler(Looper.getMainLooper()) + val newRecorder = createRecorder().also { + it.prepare() + } - var onAmplitudeChange: ((List) -> Unit)? = null + resetRecorder() + startAudioDevice() + + try { + recorder = newRecorder + newRecorder.start() + } catch (error: RuntimeException) { + onError() + } + } + + override fun start() { + super.start() + + createAmplitudesTimer() + registerMicrophoneListener() + } + + override fun pause() { + super.pause() + + resetRecorder() + } + + override suspend fun stop() { + super.stop() + + resetRecorder() + selectedMicrophone = null + unregisterMicrophoneListener() + } + + override fun resume() { + super.resume() + createAmplitudesTimer() + } + + // ==== Amplitude related ==== + private fun getAmplitudeAmount(): Int = amplitudesAmount + + private fun getAmplitude(): Int { + return try { + recorder!!.maxAmplitude + } catch (error: IllegalStateException) { + 0 + } catch (error: RuntimeException) { + 0 + } + } private fun updateAmplitude() { if (state !== RecorderState.RECORDING) { @@ -61,6 +119,8 @@ class AudioRecorderService : handler.postDelayed(::updateAmplitude, 100) } + // ==== Audio device related ==== + /// Tell Android to use the correct bluetooth microphone, if any selected private fun startAudioDevice() { if (selectedMicrophone == null) { @@ -86,6 +146,7 @@ class AudioRecorderService : } } + // ==== Actual recording related ==== private fun createRecorder(): MediaRecorder { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { MediaRecorder(this) @@ -125,6 +186,7 @@ class AudioRecorderService : } } + // ==== Microphone related ==== private fun resetRecorder() { runCatching { recorder?.let { @@ -136,71 +198,6 @@ class AudioRecorderService : } } - override fun getRecordingInformation() = RecordingInformation( - folderPath = batchesFolder.exportFolderForSettings(), - recordingStart = recordingStart, - maxDuration = settings.maxDuration, - fileExtension = settings.fileExtension, - intervalDuration = settings.intervalDuration, - type = RecordingInformation.Type.AUDIO, - ) - - override fun startNewCycle() { - super.startNewCycle() - - val newRecorder = createRecorder().also { - it.prepare() - } - - resetRecorder() - startAudioDevice() - - try { - recorder = newRecorder - newRecorder.start() - } catch (error: RuntimeException) { - onError() - } - } - - override fun start() { - super.start() - - createAmplitudesTimer() - registerMicrophoneListener() - } - - override fun pause() { - super.pause() - - resetRecorder() - } - - override suspend fun stop() { - super.stop() - - resetRecorder() - selectedMicrophone = null - unregisterMicrophoneListener() - } - - override fun resume() { - super.resume() - createAmplitudesTimer() - } - - private fun getAmplitudeAmount(): Int = amplitudesAmount - - private fun getAmplitude(): Int { - return try { - recorder!!.maxAmplitude - } catch (error: IllegalStateException) { - 0 - } catch (error: RuntimeException) { - 0 - } - } - fun changeMicrophone(microphone: MicrophoneInfo?) { selectedMicrophone = microphone onSelectedMicrophoneChange(microphone) @@ -263,6 +260,16 @@ class AudioRecorderService : audioManager.unregisterAudioDeviceCallback(audioDeviceCallback) } + // ==== Settings ==== + override fun getRecordingInformation() = RecordingInformation( + folderPath = batchesFolder.exportFolderForSettings(), + recordingStart = recordingStart, + maxDuration = settings.maxDuration, + fileExtension = settings.fileExtension, + intervalDuration = settings.intervalDuration, + type = RecordingInformation.Type.AUDIO, + ) + data class Settings( override val maxDuration: Long, override val intervalDuration: Long, diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 734339878..be3c1b045 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -3,7 +3,9 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint import android.util.Range import androidx.camera.core.Camera +import androidx.camera.core.CameraInfo import androidx.camera.core.CameraSelector +import androidx.camera.core.TorchState import androidx.camera.core.impl.CameraConfig import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.video.FileOutputOptions @@ -47,6 +49,64 @@ class VideoRecorderService : private var selectedCamera: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA + var cameraControl: CameraControl? = null + private set + + override fun start() { + super.start() + + scope.launch { + openCamera() + } + } + + override suspend fun stop() { + super.stop() + + stopActiveRecording() + + withTimeoutOrNull(CAMERA_CLOSE_TIMEOUT) { + // Camera can only be closed after the recording has been finalized + _cameraClosedListener.await() + } + + closeCamera() + } + + override fun pause() { + super.pause() + + stopActiveRecording() + } + + @SuppressLint("MissingPermission") + override fun startNewCycle() { + super.startNewCycle() + + fun action() { + activeRecording?.stop() + val newRecording = prepareVideoRecording() + + activeRecording = newRecording.start(ContextCompat.getMainExecutor(this)) { event -> + if (event is VideoRecordEvent.Finalize) { + _cameraClosedListener.complete(Unit) + } + } + } + + if (_cameraAvailableListener.isCompleted) { + action() + } else { + // Race condition of `startNewCycle` being called before `invpkeOnCompletion` + // has been called can be ignored, as the camera usually opens within 5 seconds + // and the interval can't be set shorter than 10 seconds. + _cameraAvailableListener.invokeOnCompletion { + action() + } + } + } + + // Runs a function in the main thread private fun runOnMain(callback: () -> Unit) { val mainHandler = ContextCompat.getMainExecutor(this) @@ -88,6 +148,7 @@ class VideoRecorderService : selectedCamera, videoCapture ) + cameraControl = CameraControl(camera!!) _cameraAvailableListener.complete(Unit) } @@ -108,33 +169,6 @@ class VideoRecorderService : camera = null } - override fun start() { - super.start() - - scope.launch { - openCamera() - } - } - - override suspend fun stop() { - super.stop() - - stopActiveRecording() - - withTimeoutOrNull(CAMERA_CLOSE_TIMEOUT) { - // Camera can only be closed after the recording has been finalized - _cameraClosedListener.await() - } - - closeCamera() - } - - override fun pause() { - super.pause() - - stopActiveRecording() - } - // `resume` override not needed as `startNewCycle` is called by `IntervalRecorderService` private fun stopActiveRecording() { @@ -147,33 +181,6 @@ class VideoRecorderService : .prepareRecording(this, settings.getOutputOptions(this)) .withAudioEnabled() - @SuppressLint("MissingPermission") - override fun startNewCycle() { - super.startNewCycle() - - fun action() { - activeRecording?.stop() - val newRecording = prepareVideoRecording() - - activeRecording = newRecording.start(ContextCompat.getMainExecutor(this)) { event -> - if (event is VideoRecordEvent.Finalize) { - _cameraClosedListener.complete(Unit) - } - } - } - - if (_cameraAvailableListener.isCompleted) { - action() - } else { - // Race condition of `startNewCycle` being called before `invpkeOnCompletion` - // has been called can be ignored, as the camera usually opens within 5 seconds - // and the interval can't be set shorter than 10 seconds. - _cameraAvailableListener.invokeOnCompletion { - action() - } - } - } - override fun getRecordingInformation(): RecordingInformation = RecordingInformation( folderPath = batchesFolder.exportFolderForSettings(), recordingStart = recordingStart, @@ -222,4 +229,20 @@ class VideoRecorderService : ) } } + + class CameraControl( + private val camera: Camera, + ) { + fun enableTorch() { + camera.cameraControl.enableTorch(true) + } + + fun disableTorch() { + camera.cameraControl.enableTorch(false) + } + + fun isTorchEnabled(): Boolean { + return camera.cameraInfo.torchState.value == TorchState.ON + } + } } From 5f7c6a91402d9ce7747c118f069a2f14f23dc44f Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 16:03:03 +0100 Subject: [PATCH 028/176] fix: Close camera correctly --- .../alibi/services/VideoRecorderService.kt | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index be3c1b045..da58476f2 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -3,10 +3,8 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint import android.util.Range import androidx.camera.core.Camera -import androidx.camera.core.CameraInfo import androidx.camera.core.CameraSelector import androidx.camera.core.TorchState -import androidx.camera.core.impl.CameraConfig import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.video.FileOutputOptions import androidx.camera.video.Quality @@ -18,15 +16,16 @@ import androidx.camera.video.VideoRecordEvent import androidx.core.content.ContextCompat import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation +import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeoutOrNull const val CAMERA_CLOSE_TIMEOUT = 20000L @@ -45,7 +44,10 @@ class VideoRecorderService : // Used to listen and check if the camera is available private var _cameraAvailableListener = CompletableDeferred() - private var _cameraClosedListener = CompletableDeferred() + private var _videoFinalizerListener = CompletableDeferred() + + // Absolute last completer that can be awaited to ensure that the camera is closed + private var _cameraCloserListener = CompletableDeferred() private var selectedCamera: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA @@ -65,12 +67,16 @@ class VideoRecorderService : stopActiveRecording() + // Camera can only be closed after the recording has been finalized withTimeoutOrNull(CAMERA_CLOSE_TIMEOUT) { - // Camera can only be closed after the recording has been finalized - _cameraClosedListener.await() + _videoFinalizerListener.await() } closeCamera() + + withTimeoutOrNull(CAMERA_CLOSE_TIMEOUT) { + _cameraCloserListener.await() + } } override fun pause() { @@ -88,8 +94,8 @@ class VideoRecorderService : val newRecording = prepareVideoRecording() activeRecording = newRecording.start(ContextCompat.getMainExecutor(this)) { event -> - if (event is VideoRecordEvent.Finalize) { - _cameraClosedListener.complete(Unit) + if (event is VideoRecordEvent.Finalize && this@VideoRecorderService.state == RecorderState.IDLE) { + _videoFinalizerListener.complete(Unit) } } } @@ -158,15 +164,19 @@ class VideoRecorderService : // Used to close it finally, shouldn't be called when pausing / resuming. // This should only be called after recording has finished. private fun closeCamera() { - runCatching { - runOnMain { + runOnMain { + runCatching { cameraProvider?.unbindAll() } + _cameraCloserListener.complete(Unit) + + // Doesn't need to run on main thread, but + // if it runs outside `runOnMain`, `cameraProvider` is already null + // before it's unbound + cameraProvider = null + videoCapture = null + camera = null } - - cameraProvider = null - videoCapture = null - camera = null } // `resume` override not needed as `startNewCycle` is called by `IntervalRecorderService` From b98718214c54614f32261f49a238c141af66388a Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 16:34:15 +0100 Subject: [PATCH 029/176] fix: Improve AppSettings structure --- .../java/app/myzel394/alibi/db/AppSettings.kt | 82 ++++++++++--------- .../java/app/myzel394/alibi/ui/Navigation.kt | 5 +- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 63f577ad9..558f60578 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -16,11 +16,21 @@ import java.time.LocalDateTime data class AppSettings( val audioRecorderSettings: AudioRecorderSettings = AudioRecorderSettings.getDefaultInstance(), val videoRecorderSettings: VideoRecorderSettings = VideoRecorderSettings.getDefaultInstance(), - val notificationSettings: NotificationSettings? = null, + val hasSeenOnboarding: Boolean = false, val showAdvancedSettings: Boolean = false, val theme: Theme = Theme.SYSTEM, val lastRecording: RecordingInformation? = null, + + /// Recording information + // 30 minutes + val maxDuration: Long = 30 * 60 * 1000L, + // 60 seconds + val intervalDuration: Long = 60 * 1000L, + + val notificationSettings: NotificationSettings? = null, + val deleteRecordingsImmediately: Boolean = false, + val saveFolder: String? = null, ) { fun setShowAdvancedSettings(showAdvancedSettings: Boolean): AppSettings { return copy(showAdvancedSettings = showAdvancedSettings) @@ -46,6 +56,38 @@ data class AppSettings( return copy(lastRecording = lastRecording) } + fun setMaxDuration(duration: Long): AppSettings { + if (duration < 60 * 1000L || duration > 10 * 24 * 60 * 60 * 1000L) { + throw Exception("Max duration must be between 1 minute and 10 days") + } + + if (duration < intervalDuration) { + throw Exception("Max duration must be greater than interval duration") + } + + return copy(maxDuration = duration) + } + + fun setIntervalDuration(duration: Long): AppSettings { + if (duration < 10 * 1000L || duration > 60 * 60 * 1000L) { + throw Exception("Interval duration must be between 10 seconds and 1 hour") + } + + if (duration > maxDuration) { + throw Exception("Interval duration must be less than max duration") + } + + return copy(intervalDuration = duration) + } + + fun setDeleteRecordingsImmediately(deleteRecordingsImmediately: Boolean): AppSettings { + return copy(deleteRecordingsImmediately = deleteRecordingsImmediately) + } + + fun setSaveFolder(saveFolder: String?): AppSettings { + return copy(saveFolder = saveFolder) + } + enum class Theme { SYSTEM, LIGHT, @@ -95,18 +137,12 @@ data class RecordingInformation( @Serializable data class AudioRecorderSettings( - // 30 minutes - val maxDuration: Long = 30 * 60 * 1000L, - // 60 seconds - val intervalDuration: Long = 60 * 1000L, // 320 Kbps val bitRate: Int = 320000, val samplingRate: Int? = null, val outputFormat: Int? = null, val encoder: Int? = null, val showAllMicrophones: Boolean = false, - val deleteRecordingsImmediately: Boolean = false, - val saveFolder: String? = null, ) { fun getOutputFormat(): Int { if (outputFormat != null) { @@ -174,18 +210,6 @@ data class AudioRecorderSettings( else MediaRecorder.AudioEncoder.AMR_NB - fun setIntervalDuration(duration: Long): AudioRecorderSettings { - if (duration < 10 * 1000L || duration > 60 * 60 * 1000L) { - throw Exception("Interval duration must be between 10 seconds and 1 hour") - } - - if (duration > maxDuration) { - throw Exception("Interval duration must be less than max duration") - } - - return copy(intervalDuration = duration) - } - fun setBitRate(bitRate: Int): AudioRecorderSettings { if (bitRate !in 1000..320000) { throw Exception("Bit rate must be between 1000 and 320000") @@ -218,30 +242,10 @@ data class AudioRecorderSettings( return copy(encoder = encoder) } - fun setMaxDuration(duration: Long): AudioRecorderSettings { - if (duration < 60 * 1000L || duration > 10 * 24 * 60 * 60 * 1000L) { - throw Exception("Max duration must be between 1 minute and 10 days") - } - - if (duration < intervalDuration) { - throw Exception("Max duration must be greater than interval duration") - } - - return copy(maxDuration = duration) - } - fun setShowAllMicrophones(showAllMicrophones: Boolean): AudioRecorderSettings { return copy(showAllMicrophones = showAllMicrophones) } - fun setDeleteRecordingsImmediately(deleteRecordingsImmediately: Boolean): AudioRecorderSettings { - return copy(deleteRecordingsImmediately = deleteRecordingsImmediately) - } - - fun setSaveFolder(saveFolder: String?): AudioRecorderSettings { - return copy(saveFolder = saveFolder) - } - fun isEncoderCompatible(encoder: Int): Boolean { if (outputFormat == null || outputFormat == MediaRecorder.OutputFormat.DEFAULT) { return true diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index 1a64b8e98..8a947526f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -73,7 +73,10 @@ fun Navigation( scaleOut(targetScale = SCALE_IN) + fadeOut(tween(durationMillis = 150)) } ) { - POCVideo(videoRecorder = videoRecorder, settings = settings) + AudioRecorderScreen( + navController = navController, + audioRecorder = audioRecorder, + ) } composable( Screen.Settings.route, From d4a5612b7741c4d87b8a848b0a362a92e0703a68 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 16:47:11 +0100 Subject: [PATCH 030/176] chore(ui): Improve structure for settings --- .../alibi/services/AudioRecorderService.kt | 15 +++---- .../alibi/services/VideoRecorderService.kt | 7 ++-- .../AudioRecorder/molecules/StartRecording.kt | 2 +- .../{atoms => Tiles}/AboutTile.kt | 2 +- .../AudioRecorderBitrateTile.kt} | 6 +-- .../AudioRecorderEncoderTile.kt} | 5 +-- .../AudioRecorderOutputFormatTile.kt} | 4 +- .../AudioRecorderSamplingRateTile.kt} | 6 +-- .../AudioRecorderShowAllMicrophonesTile.kt} | 7 +--- .../CustomNotificationTile.kt | 2 +- .../DeleteRecordingsImmediatelyTile.kt | 8 ++-- .../{atoms => Tiles}/ImportExport.kt | 4 +- .../{atoms => Tiles}/IntervalDurationTile.kt | 12 +++--- .../{atoms => Tiles}/MaxDurationTile.kt | 10 ++--- .../{atoms => Tiles}/SaveFolderTile.kt | 15 +++---- .../alibi/ui/models/AudioRecorderModel.kt | 6 +-- .../alibi/ui/screens/AudioRecorderScreen.kt | 6 +-- .../alibi/ui/screens/SettingsScreen.kt | 39 ++++++++++--------- 18 files changed, 70 insertions(+), 86 deletions(-) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms => Tiles}/AboutTile.kt (97%) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms/BitrateTile.kt => Tiles/AudioRecorderBitrateTile.kt} (96%) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms/EncoderTile.kt => Tiles/AudioRecorderEncoderTile.kt} (97%) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms/OutputFormatTile.kt => Tiles/AudioRecorderOutputFormatTile.kt} (97%) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms/SamplingRateTile.kt => Tiles/AudioRecorderSamplingRateTile.kt} (96%) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms/ShowAllMicrophonesTile.kt => Tiles/AudioRecorderShowAllMicrophonesTile.kt} (85%) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms => Tiles}/CustomNotificationTile.kt (96%) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms => Tiles}/DeleteRecordingsImmediatelyTile.kt (78%) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms => Tiles}/ImportExport.kt (97%) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms => Tiles}/IntervalDurationTile.kt (89%) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms => Tiles}/MaxDurationTile.kt (90%) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/{atoms => Tiles}/SaveFolderTile.kt (93%) diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index a1cb8597c..170ab5304 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -9,6 +9,7 @@ import android.media.MediaRecorder.OnErrorListener import android.os.Build import android.os.Handler import android.os.Looper +import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AudioRecorderSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState @@ -296,14 +297,14 @@ class AudioRecorderService : } companion object { - fun from(audioRecorderSettings: AudioRecorderSettings): Settings { + fun from(appSettings: AppSettings): Settings { return Settings( - intervalDuration = audioRecorderSettings.intervalDuration, - bitRate = audioRecorderSettings.bitRate, - samplingRate = audioRecorderSettings.getSamplingRate(), - outputFormat = audioRecorderSettings.getOutputFormat(), - encoder = audioRecorderSettings.getEncoder(), - maxDuration = audioRecorderSettings.maxDuration, + intervalDuration = appSettings.intervalDuration, + maxDuration = appSettings.maxDuration, + bitRate = appSettings.audioRecorderSettings.bitRate, + samplingRate = appSettings.audioRecorderSettings.getSamplingRate(), + outputFormat = appSettings.audioRecorderSettings.getOutputFormat(), + encoder = appSettings.audioRecorderSettings.getEncoder(), ) } } diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index da58476f2..67aa8019d 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -226,10 +226,9 @@ class VideoRecorderService : companion object { fun from(appSettings: AppSettings) = Settings( - // TODO: Migrate audioSettings - maxDuration = appSettings.audioRecorderSettings.maxDuration, - intervalDuration = appSettings.audioRecorderSettings.intervalDuration, - folder = appSettings.audioRecorderSettings.saveFolder, + maxDuration = appSettings.maxDuration, + intervalDuration = appSettings.intervalDuration, + folder = appSettings.saveFolder, targetVideoBitRate = appSettings.videoRecorderSettings.targetedVideoBitRate, targetFrameRate = appSettings.videoRecorderSettings.targetFrameRate, quality = appSettings.videoRecorderSettings.getQualitySelector() diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt index f1bb88937..7289b2f5d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt @@ -120,7 +120,7 @@ fun StartRecording( Text( stringResource( R.string.ui_audioRecorder_action_start_description, - settings.audioRecorderSettings.maxDuration / 1000 / 60 + settings.maxDuration / 1000 / 60 ), style = MaterialTheme.typography.bodySmall.copy( color = MaterialTheme.colorScheme.onSurfaceVariant, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/AboutTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AboutTile.kt similarity index 97% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/AboutTile.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AboutTile.kt index 857584d5a..834b2b1a2 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/AboutTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AboutTile.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import androidx.compose.foundation.background import androidx.compose.foundation.clickable diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/BitrateTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderBitrateTile.kt similarity index 96% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/BitrateTile.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderBitrateTile.kt index 5472dd5c4..4956b7e8a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/BitrateTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderBitrateTile.kt @@ -1,8 +1,7 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Mic import androidx.compose.material.icons.filled.Tune import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults @@ -11,7 +10,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -36,7 +34,7 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable -fun BitrateTile( +fun AudioRecorderBitrateTile( settings: AppSettings, ) { val scope = rememberCoroutineScope() diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/EncoderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderEncoderTile.kt similarity index 97% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/EncoderTile.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderEncoderTile.kt index c8ad06e45..5e9219f7d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/EncoderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderEncoderTile.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import android.media.MediaRecorder import androidx.compose.material.icons.Icons @@ -12,7 +12,6 @@ import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -33,7 +32,7 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable -fun EncoderTile( +fun AudioRecorderEncoderTile( snackbarHostState: SnackbarHostState, settings: AppSettings, ) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/OutputFormatTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderOutputFormatTile.kt similarity index 97% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/OutputFormatTile.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderOutputFormatTile.kt index 2d52c9f39..5f8e5d0d1 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/OutputFormatTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderOutputFormatTile.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AudioFile @@ -29,7 +29,7 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable -fun OutputFormatTile( +fun AudioRecorderOutputFormatTile( settings: AppSettings, ) { val scope = rememberCoroutineScope() diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/SamplingRateTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderSamplingRateTile.kt similarity index 96% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/SamplingRateTile.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderSamplingRateTile.kt index 53eb43c22..4b4f87a4e 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/SamplingRateTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderSamplingRateTile.kt @@ -1,9 +1,8 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.RadioButtonChecked -import androidx.compose.material.icons.filled.Tune import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api @@ -11,7 +10,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -36,7 +34,7 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SamplingRateTile( +fun AudioRecorderSamplingRateTile( settings: AppSettings, ) { val scope = rememberCoroutineScope() diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ShowAllMicrophonesTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderShowAllMicrophonesTile.kt similarity index 85% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ShowAllMicrophonesTile.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderShowAllMicrophonesTile.kt index 001b4cbaa..0d71f517d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ShowAllMicrophonesTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/AudioRecorderShowAllMicrophonesTile.kt @@ -1,12 +1,10 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.GraphicEq import androidx.compose.material.icons.filled.MicExternalOn import androidx.compose.material3.Icon import androidx.compose.material3.Switch import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -14,12 +12,11 @@ import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.components.atoms.SettingsTile -import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState import kotlinx.coroutines.launch @Composable -fun ShowAllMicrophonesTile( +fun AudioRecorderShowAllMicrophonesTile( settings: AppSettings, ) { val scope = rememberCoroutineScope() diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/CustomNotificationTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/CustomNotificationTile.kt similarity index 96% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/CustomNotificationTile.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/CustomNotificationTile.kt index d32bde411..5dc50a47b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/CustomNotificationTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/CustomNotificationTile.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import androidx.compose.foundation.clickable import androidx.compose.material.icons.Icons diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/DeleteRecordingsImmediatelyTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/DeleteRecordingsImmediatelyTile.kt similarity index 78% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/DeleteRecordingsImmediatelyTile.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/DeleteRecordingsImmediatelyTile.kt index 5dfafebbc..4300884c7 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/DeleteRecordingsImmediatelyTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/DeleteRecordingsImmediatelyTile.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.DeleteSweep @@ -34,13 +34,11 @@ fun DeleteRecordingsImmediatelyTile( }, trailing = { Switch( - checked = settings.audioRecorderSettings.deleteRecordingsImmediately, + checked = settings.deleteRecordingsImmediately, onCheckedChange = { scope.launch { dataStore.updateData { - it.setAudioRecorderSettings( - it.audioRecorderSettings.setDeleteRecordingsImmediately(it.audioRecorderSettings.deleteRecordingsImmediately.not()) - ) + it.setDeleteRecordingsImmediately(it.deleteRecordingsImmediately.not()) } } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ImportExport.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/ImportExport.kt similarity index 97% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ImportExport.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/ImportExport.kt index 830d55033..f0d78031a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ImportExport.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/ImportExport.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.Upload @@ -16,7 +15,6 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.SnackbarVisuals import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/IntervalDurationTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/IntervalDurationTile.kt similarity index 89% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/IntervalDurationTile.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/IntervalDurationTile.kt index c8dc328c4..09bc86edb 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/IntervalDurationTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/IntervalDurationTile.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Mic @@ -44,9 +44,7 @@ fun IntervalDurationTile( fun updateValue(intervalDuration: Long) { scope.launch { dataStore.updateData { - it.setAudioRecorderSettings( - it.audioRecorderSettings.setIntervalDuration(intervalDuration) - ) + it.setIntervalDuration(intervalDuration) } } } @@ -65,7 +63,7 @@ fun IntervalDurationTile( }, config = DurationConfig( timeFormat = DurationFormat.MM_SS, - currentTime = settings.audioRecorderSettings.intervalDuration / 1000, + currentTime = settings.intervalDuration / 1000, minTime = 10, maxTime = 60 * 60, ) @@ -88,7 +86,7 @@ fun IntervalDurationTile( shape = MaterialTheme.shapes.medium, ) { Text( - text = formatDuration(settings.audioRecorderSettings.intervalDuration), + text = formatDuration(settings.intervalDuration), ) } }, @@ -96,7 +94,7 @@ fun IntervalDurationTile( ExampleListRoulette( items = AudioRecorderSettings.EXAMPLE_DURATION_TIMES, onItemSelected = ::updateValue, - ) {duration -> + ) { duration -> Text( text = formatDuration(duration), ) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/MaxDurationTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/MaxDurationTile.kt similarity index 90% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/MaxDurationTile.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/MaxDurationTile.kt index aacd9f0e1..6319cb67a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/MaxDurationTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/MaxDurationTile.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Memory @@ -43,9 +43,7 @@ fun MaxDurationTile( fun updateValue(maxDuration: Long) { scope.launch { dataStore.updateData { - it.setAudioRecorderSettings( - it.audioRecorderSettings.setMaxDuration(maxDuration) - ) + it.setMaxDuration(maxDuration) } } } @@ -64,7 +62,7 @@ fun MaxDurationTile( }, config = DurationConfig( timeFormat = DurationFormat.HH_MM, - currentTime = settings.audioRecorderSettings.maxDuration / 1000, + currentTime = settings.maxDuration / 1000, minTime = 60, maxTime = 10 * 24 * 60 * 60, ) @@ -86,7 +84,7 @@ fun MaxDurationTile( ), shape = MaterialTheme.shapes.medium, ) { - Text(formatDuration(settings.audioRecorderSettings.maxDuration)) + Text(formatDuration(settings.maxDuration)) } }, extra = { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt similarity index 93% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/SaveFolderTile.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 4f2fd7ff3..c62db5c1a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -1,8 +1,7 @@ -package app.myzel394.alibi.ui.components.SettingsScreen.atoms +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import android.content.Intent import android.net.Uri -import android.text.TextUtils.split import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -50,10 +49,10 @@ fun SaveFolderTile( val dataStore = context.dataStore fun updateValue(path: String?) { - if (settings.audioRecorderSettings.saveFolder != null) { + if (settings.saveFolder != null) { runCatching { context.contentResolver.releasePersistableUriPermission( - Uri.parse(settings.audioRecorderSettings.saveFolder), + Uri.parse(settings.saveFolder), Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) } @@ -61,9 +60,7 @@ fun SaveFolderTile( scope.launch { dataStore.updateData { - it.setAudioRecorderSettings( - it.audioRecorderSettings.setSaveFolder(path) - ) + it.setSaveFolder(path) } } } @@ -172,11 +169,11 @@ fun SaveFolderTile( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp), ) { - if (settings.audioRecorderSettings.saveFolder != null) { + if (settings.saveFolder != null) { Text( text = stringResource( R.string.form_value_selected, - splitPath(settings.audioRecorderSettings.saveFolder).joinToString(" > ") + splitPath(settings.saveFolder).joinToString(" > ") ), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index 0395f8c2d..1306dbe1a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -43,7 +43,7 @@ class AudioRecorderModel : microphoneStatus = MicrophoneConnectivityStatus.CONNECTED } service.settings = - AudioRecorderService.Settings.from(settings.audioRecorderSettings) + AudioRecorderService.Settings.from(settings) service.clearAllRecordings() service.startRecording() @@ -55,14 +55,14 @@ class AudioRecorderModel : } override fun startRecording(context: Context, settings: AppSettings) { - batchesFolder = if (settings.audioRecorderSettings.saveFolder == null) + batchesFolder = if (settings.saveFolder == null) AudioBatchesFolder.viaInternalFolder(context) else AudioBatchesFolder.viaCustomFolder( context, DocumentFile.fromTreeUri( context, - Uri.parse(settings.audioRecorderSettings.saveFolder) + Uri.parse(settings.saveFolder) )!! ) diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt index ed6f35449..677e4fc8f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt @@ -67,7 +67,7 @@ fun AudioRecorderScreen( val saveFile = rememberFileSaverDialog( settings.audioRecorderSettings.getMimeType() ) { - if (settings.audioRecorderSettings.deleteRecordingsImmediately) { + if (settings.deleteRecordingsImmediately) { audioRecorder.batchesFolder!!.deleteRecordings() } @@ -84,7 +84,7 @@ fun AudioRecorderScreen( var showRecorderError by remember { mutableStateOf(false) } fun saveAsLastRecording() { - if (!settings.audioRecorderSettings.deleteRecordingsImmediately) { + if (!settings.deleteRecordingsImmediately) { scope.launch { dataStore.updateData { it.setLastRecording( @@ -156,7 +156,7 @@ fun AudioRecorderScreen( BatchesFolder.BatchType.CUSTOM -> { showSnackbar(batchesFolder.customFolder!!.uri) - if (settings.audioRecorderSettings.deleteRecordingsImmediately) { + if (settings.deleteRecordingsImmediately) { batchesFolder.deleteRecordings() } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index cdad66402..62a4ed5b3 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -25,7 +25,6 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment @@ -38,19 +37,20 @@ import androidx.navigation.NavController import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.AboutTile -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.BitrateTile -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.CustomNotificationTile -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.DeleteRecordingsImmediatelyTile -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.EncoderTile -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ImportExport +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AboutTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderBitrateTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.CustomNotificationTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DeleteRecordingsImmediatelyTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DividerTitle +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderEncoderTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.ImportExport import app.myzel394.alibi.ui.components.SettingsScreen.atoms.InAppLanguagePicker -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.IntervalDurationTile -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.MaxDurationTile -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.OutputFormatTile -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.SamplingRateTile -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.SaveFolderTile -import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ShowAllMicrophonesTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.IntervalDurationTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.MaxDurationTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderOutputFormatTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderSamplingRateTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.SaveFolderTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderShowAllMicrophonesTile import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ThemeSelector import app.myzel394.alibi.ui.components.atoms.GlobalSwitch import app.myzel394.alibi.ui.components.atoms.MessageBox @@ -160,11 +160,14 @@ fun SettingsScreen( .padding(horizontal = 16.dp, vertical = 32.dp) ) SaveFolderTile(settings = settings) - ShowAllMicrophonesTile(settings = settings) - BitrateTile(settings = settings) - SamplingRateTile(settings = settings) - EncoderTile(snackbarHostState = snackbarHostState, settings = settings) - OutputFormatTile(settings = settings) + AudioRecorderShowAllMicrophonesTile(settings = settings) + AudioRecorderBitrateTile(settings = settings) + AudioRecorderSamplingRateTile(settings = settings) + AudioRecorderEncoderTile( + snackbarHostState = snackbarHostState, + settings = settings + ) + AudioRecorderOutputFormatTile(settings = settings) } Divider( modifier = Modifier From 4f265b23f8f0950da14c12bffcc37eae484b58c8 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:03:25 +0100 Subject: [PATCH 031/176] feat(ui): Improve settings UI --- .../SettingsScreen/Tiles/SectionTitle.kt | 53 +++++++++++++++++++ .../alibi/ui/components/atoms/GlobalSwitch.kt | 25 +++++---- .../alibi/ui/screens/SettingsScreen.kt | 24 +++++---- app/src/main/res/values/strings.xml | 2 + 4 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SectionTitle.kt diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SectionTitle.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SectionTitle.kt new file mode 100644 index 000000000..7880076cd --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SectionTitle.kt @@ -0,0 +1,53 @@ +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.Divider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun DividerTitle( + modifier: Modifier = Modifier, + title: String, + description: String, +) { + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Divider( + modifier = Modifier + .weight(1f) + .align(Alignment.CenterVertically), + ) + Text( + title, + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.onSurface, + ) + Divider( + modifier = Modifier + .weight(1f) + .align(Alignment.CenterVertically), + ) + } + Text( + description, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/GlobalSwitch.kt b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/GlobalSwitch.kt index a4ba923d3..620090efc 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/GlobalSwitch.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/GlobalSwitch.kt @@ -1,5 +1,6 @@ package app.myzel394.alibi.ui.components.atoms +import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row @@ -10,6 +11,7 @@ import androidx.compose.material3.Switch import androidx.compose.material3.SwitchDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -24,12 +26,21 @@ fun GlobalSwitch( checked: Boolean, onCheckedChange: (Boolean) -> Unit, ) { + val backgroundColor by animateColorAsState( + targetValue = if (checked) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.secondaryContainer.copy( + alpha = 0.2f + ), + label = "backgroundColor" + ) + Row( modifier = Modifier .padding(16.dp) .fillMaxWidth() .clip(MaterialTheme.shapes.extraLarge) - .background(MaterialTheme.colorScheme.secondary) + .background( + backgroundColor + ) .padding(16.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, @@ -38,18 +49,10 @@ fun GlobalSwitch( label, fontSize = 18.sp, fontWeight = FontWeight.W500, - color = MaterialTheme.colorScheme.onSecondary, + color = MaterialTheme.colorScheme.onSurface, ) Switch( - colors = SwitchDefaults.colors( - uncheckedTrackColor = MaterialTheme.colorScheme.background, - checkedTrackColor = MaterialTheme.colorScheme.surfaceVariant, - checkedThumbColor = MaterialTheme.colorScheme.secondary, - uncheckedBorderColor = Color.Transparent, - checkedBorderColor = Color.Transparent, - disabledCheckedBorderColor = Color.Transparent, - disabledUncheckedBorderColor = Color.Transparent, - ), + colors = SwitchDefaults.colors(), checked = checked, onCheckedChange = { onCheckedChange(it) diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index 62a4ed5b3..400c54240 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -1,11 +1,13 @@ package app.myzel394.alibi.ui.screens import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column 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.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -131,6 +133,11 @@ fun SettingsScreen( if (!SUPPORTS_DARK_MODE_NATIVELY) { ThemeSelector() } + MaxDurationTile(settings = settings) + IntervalDurationTile(settings = settings) + InAppLanguagePicker() + DeleteRecordingsImmediatelyTile(settings = settings) + CustomNotificationTile(navController = navController, settings = settings) GlobalSwitch( label = stringResource(R.string.ui_settings_advancedSettings_label), checked = settings.showAdvancedSettings, @@ -142,24 +149,22 @@ fun SettingsScreen( } } ) - MaxDurationTile(settings = settings) - IntervalDurationTile(settings = settings) - InAppLanguagePicker() - DeleteRecordingsImmediatelyTile(settings = settings) - CustomNotificationTile(navController = navController, settings = settings) - AboutTile(navController = navController) AnimatedVisibility(visible = settings.showAdvancedSettings) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(32.dp), ) { Column { - Divider( + SaveFolderTile(settings = settings) + + DividerTitle( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 32.dp) + .padding(16.dp), + title = stringResource(R.string.ui_settings_sections_audio_title), + description = stringResource(R.string.ui_settings_sections_audio_description), ) - SaveFolderTile(settings = settings) + AudioRecorderShowAllMicrophonesTile(settings = settings) AudioRecorderBitrateTile(settings = settings) AudioRecorderSamplingRateTile(settings = settings) @@ -176,6 +181,7 @@ fun SettingsScreen( ImportExport(snackbarHostState = snackbarHostState) } } + AboutTile(navController = navController) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 40621157b..d67172c98 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -120,4 +120,6 @@ Use private, encrypted storage Recording has been saved successfully! Open + Audio Recording + Only applies to audio recordings \ No newline at end of file From 9598cd45fa4a653eca15e7ef62b0162c019b44f4 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:40:11 +0100 Subject: [PATCH 032/176] feat: Add VideoRecorderBitrateTile --- .../java/app/myzel394/alibi/db/AppSettings.kt | 18 +++ .../Tiles/VideoRecorderBitrateTile.kt | 145 ++++++++++++++++++ .../alibi/ui/screens/SettingsScreen.kt | 13 +- app/src/main/res/values/strings.xml | 6 + 4 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderBitrateTile.kt diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 558f60578..b59c13247 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -40,6 +40,10 @@ data class AppSettings( return copy(audioRecorderSettings = audioRecorderSettings) } + fun setVideoRecorderSettings(videoRecorderSettings: VideoRecorderSettings): AppSettings { + return copy(videoRecorderSettings = videoRecorderSettings) + } + fun setNotificationSettings(notificationSettings: NotificationSettings?): AppSettings { return copy(notificationSettings = notificationSettings) } @@ -401,6 +405,20 @@ data class VideoRecorderSettings( "FHD" to Quality.FHD, "UHD" to Quality.UHD, ) + + val EXAMPLE_BITRATE_VALUES = listOf( + null, + 500 * 1000, + // 1 Mbps + 1 * 1000 * 1000, + 2 * 1000 * 1000, + 4 * 1000 * 1000, + 8 * 1000 * 1000, + 16 * 1000 * 1000, + 32 * 1000 * 1000, + 50 * 1000 * 1000, + 100 * 1000 * 1000, + ) } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderBitrateTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderBitrateTile.kt new file mode 100644 index 000000000..a8a842e48 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderBitrateTile.kt @@ -0,0 +1,145 @@ +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles + +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Tune +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import app.myzel394.alibi.R +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.db.AudioRecorderSettings +import app.myzel394.alibi.db.VideoRecorderSettings +import app.myzel394.alibi.ui.components.atoms.ExampleListRoulette +import app.myzel394.alibi.ui.components.atoms.SettingsTile +import app.myzel394.alibi.ui.utils.IconResource +import com.maxkeppeker.sheets.core.models.base.Header +import com.maxkeppeker.sheets.core.models.base.IconSource +import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState +import com.maxkeppeler.sheets.input.InputDialog +import com.maxkeppeler.sheets.input.models.InputHeader +import com.maxkeppeler.sheets.input.models.InputSelection +import com.maxkeppeler.sheets.input.models.InputTextField +import com.maxkeppeler.sheets.input.models.InputTextFieldType +import com.maxkeppeler.sheets.input.models.ValidationResult +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun VideoRecorderBitrateTile( + settings: AppSettings, +) { + val scope = rememberCoroutineScope() + val showDialog = rememberUseCaseState() + val dataStore = LocalContext.current.dataStore + + fun updateValue(bitRate: Int?) { + scope.launch { + dataStore.updateData { + it.setVideoRecorderSettings( + it.videoRecorderSettings.setTargetedVideoBitRate(bitRate) + ) + } + } + } + + val notNumberLabel = stringResource(R.string.form_error_type_notNumber) + InputDialog( + state = showDialog, + header = Header.Default( + title = stringResource(R.string.ui_settings_option_videoTargetedBitrate_title), + icon = IconSource( + painter = IconResource.fromImageVector(Icons.Default.Tune).asPainterResource(), + contentDescription = null, + ) + ), + selection = InputSelection( + input = listOf( + InputTextField( + header = InputHeader( + title = stringResource(id = R.string.ui_settings_option_videoTargetedBitrate_explanation), + ), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + ), + type = InputTextFieldType.OUTLINED, + text = if (settings.videoRecorderSettings.targetedVideoBitRate == null) "" else (settings.videoRecorderSettings.targetedVideoBitRate / 1000).toString(), + validationListener = { text -> + val bitRate = text?.toIntOrNull() + + if (bitRate == null) { + return@InputTextField ValidationResult.Invalid(notNumberLabel) + } + + ValidationResult.Valid + }, + key = "bitrate", + ) + ), + ) { result -> + val bitRate = result.getString("bitrate")?.toIntOrNull() ?: return@InputSelection + + updateValue(bitRate * 1000) + } + ) + SettingsTile( + title = stringResource(R.string.ui_settings_option_videoTargetedBitrate_title), + description = stringResource(R.string.ui_settings_option_bitrate_description), + leading = { + Icon( + Icons.Default.Tune, + contentDescription = null, + ) + }, + trailing = { + Button( + onClick = showDialog::show, + colors = ButtonDefaults.filledTonalButtonColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant, + ), + shape = MaterialTheme.shapes.medium, + ) { + Text(formatBitrate(settings.videoRecorderSettings.targetedVideoBitRate)) + } + }, + extra = { + ExampleListRoulette( + items = VideoRecorderSettings.EXAMPLE_BITRATE_VALUES, + onItemSelected = ::updateValue, + ) { bitRate -> + Text(formatBitrate(bitRate)) + } + } + ) +} + +@Composable +fun formatBitrate(bitrate: Int?): String { + return if (bitrate == null) + stringResource(R.string.ui_settings_value_auto_label) + else if (bitrate >= 1000 * 1000 && bitrate % (1000 * 1000) == 0) + stringResource( + R.string.format_mbps, + bitrate / 1000 / 1000, + ) + else if (bitrate >= 1000 && bitrate % 1000 == 0) + stringResource( + R.string.format_kbps, + bitrate / 1000, + ) + else + stringResource( + R.string.format_bps, + bitrate, + ) +} + diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index 400c54240..a208a4822 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -1,13 +1,11 @@ package app.myzel394.alibi.ui.screens import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column 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.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -40,7 +38,7 @@ import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AboutTile -import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderBitrateTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderBitrateTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.CustomNotificationTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DeleteRecordingsImmediatelyTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DividerTitle @@ -164,15 +162,20 @@ fun SettingsScreen( title = stringResource(R.string.ui_settings_sections_audio_title), description = stringResource(R.string.ui_settings_sections_audio_description), ) - AudioRecorderShowAllMicrophonesTile(settings = settings) - AudioRecorderBitrateTile(settings = settings) + VideoRecorderBitrateTile(settings = settings) AudioRecorderSamplingRateTile(settings = settings) AudioRecorderEncoderTile( snackbarHostState = snackbarHostState, settings = settings ) AudioRecorderOutputFormatTile(settings = settings) + + DividerTitle( + title = stringResource(R.string.ui_settings_sections_video_title), + description = stringResource(R.string.ui_settings_sections_video_description), + ) + VideoRecorderBitrateTile(settings = settings) } Divider( modifier = Modifier diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d67172c98..ee6bbe662 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -122,4 +122,10 @@ Open Audio Recording Only applies to audio recordings + Video Recording + Only applies to video recordings + Targeted Bitrate + Bitrate for the video recording. Only applies to the video itself, not the audio. The actual bitrate may be different depending on what you will be recording. + %s MB/s + %s B/s \ No newline at end of file From e8d7b2b6f8b054115ff96177ce4565db5e99eada Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:50:54 +0100 Subject: [PATCH 033/176] feat: Add VideoFrameRate --- .../java/app/myzel394/alibi/db/AppSettings.kt | 9 ++ .../SettingsScreen/Tiles/VideoFrameRate.kt | 127 ++++++++++++++++++ .../alibi/ui/screens/SettingsScreen.kt | 6 +- app/src/main/res/values/strings.xml | 3 + 4 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index b59c13247..1bc97bfff 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -419,6 +419,15 @@ data class VideoRecorderSettings( 50 * 1000 * 1000, 100 * 1000 * 1000, ) + + val EXAMPLE_FRAME_RATE_VALUES = listOf( + null, + 24, + 30, + 60, + 120, + 240, + ) } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt new file mode 100644 index 000000000..1ae5cda1d --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt @@ -0,0 +1,127 @@ +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles + +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.BrokenImage +import androidx.compose.material.icons.filled.Tune +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import app.myzel394.alibi.R +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.db.VideoRecorderSettings +import app.myzel394.alibi.ui.components.atoms.ExampleListRoulette +import app.myzel394.alibi.ui.components.atoms.SettingsTile +import app.myzel394.alibi.ui.utils.IconResource +import com.maxkeppeker.sheets.core.models.base.Header +import com.maxkeppeker.sheets.core.models.base.IconSource +import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState +import com.maxkeppeler.sheets.input.InputDialog +import com.maxkeppeler.sheets.input.models.InputHeader +import com.maxkeppeler.sheets.input.models.InputSelection +import com.maxkeppeler.sheets.input.models.InputTextField +import com.maxkeppeler.sheets.input.models.InputTextFieldType +import com.maxkeppeler.sheets.input.models.ValidationResult +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun VideoFrameRate( + settings: AppSettings, +) { + val scope = rememberCoroutineScope() + val showDialog = rememberUseCaseState() + val dataStore = LocalContext.current.dataStore + + fun updateValue(frameRate: Int?) { + scope.launch { + dataStore.updateData { + it.setVideoRecorderSettings( + it.videoRecorderSettings.setTargetFrameRate(frameRate) + ) + } + } + } + + val notNumberLabel = stringResource(R.string.form_error_type_notNumber) + InputDialog( + state = showDialog, + header = Header.Default( + title = stringResource(R.string.ui_settings_option_videoTargetedFrameRate_title), + icon = IconSource( + painter = IconResource.fromImageVector(Icons.Default.Tune).asPainterResource(), + contentDescription = null, + ) + ), + selection = InputSelection( + input = listOf( + InputTextField( + header = InputHeader( + title = stringResource(id = R.string.ui_settings_option_videoTargetedFrameRate_explanation), + ), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + ), + type = InputTextFieldType.OUTLINED, + text = if (settings.videoRecorderSettings.targetFrameRate == null) "" else settings.videoRecorderSettings.targetFrameRate.toString(), + validationListener = { text -> + val frameRate = text?.toIntOrNull() + + if (frameRate == null) { + return@InputTextField ValidationResult.Invalid(notNumberLabel) + } + + ValidationResult.Valid + }, + key = "framerate", + ) + ), + ) { result -> + val frameRate = result.getString("framerate")?.toIntOrNull() ?: return@InputSelection + + updateValue(frameRate) + } + ) + SettingsTile( + title = stringResource(R.string.ui_settings_option_videoTargetedFrameRate_title), + leading = { + Icon( + Icons.Default.BrokenImage, + contentDescription = null, + ) + }, + trailing = { + Button( + onClick = showDialog::show, + colors = ButtonDefaults.filledTonalButtonColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant, + ), + shape = MaterialTheme.shapes.medium, + ) { + if (settings.videoRecorderSettings.targetFrameRate == null) + Text(stringResource(R.string.ui_settings_value_auto_label)) + else + Text(settings.videoRecorderSettings.targetFrameRate.toString()) + } + }, + extra = { + ExampleListRoulette( + items = VideoRecorderSettings.EXAMPLE_FRAME_RATE_VALUES, + onItemSelected = ::updateValue, + ) { frameRate -> + Text( + frameRate?.toString() ?: stringResource(R.string.ui_settings_value_auto_label) + ) + } + } + ) +} diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index a208a4822..0248ce1b8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -38,7 +38,7 @@ import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AboutTile -import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderBitrateTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoFrameRate import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.CustomNotificationTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DeleteRecordingsImmediatelyTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DividerTitle @@ -51,6 +51,7 @@ import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderOutput import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderSamplingRateTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.SaveFolderTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderShowAllMicrophonesTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderBitrateTile import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ThemeSelector import app.myzel394.alibi.ui.components.atoms.GlobalSwitch import app.myzel394.alibi.ui.components.atoms.MessageBox @@ -163,7 +164,7 @@ fun SettingsScreen( description = stringResource(R.string.ui_settings_sections_audio_description), ) AudioRecorderShowAllMicrophonesTile(settings = settings) - VideoRecorderBitrateTile(settings = settings) + VideoFrameRate(settings = settings) AudioRecorderSamplingRateTile(settings = settings) AudioRecorderEncoderTile( snackbarHostState = snackbarHostState, @@ -176,6 +177,7 @@ fun SettingsScreen( description = stringResource(R.string.ui_settings_sections_video_description), ) VideoRecorderBitrateTile(settings = settings) + VideoFrameRate(settings = settings) } Divider( modifier = Modifier diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ee6bbe662..bc51404d6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -128,4 +128,7 @@ Bitrate for the video recording. Only applies to the video itself, not the audio. The actual bitrate may be different depending on what you will be recording. %s MB/s %s B/s + Targeted Frame Rate + How many frames per second should be recorded. + The actual frame rate may be different. This can for example happen if the device is not able to record with the specified frame rate. \ No newline at end of file From b6d8bdf607404c5d4c3cce09c71f6cc0f1bddded Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:54:44 +0100 Subject: [PATCH 034/176] fix: Icon --- .../alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt index 1ae5cda1d..c1235c56b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt @@ -58,7 +58,8 @@ fun VideoFrameRate( header = Header.Default( title = stringResource(R.string.ui_settings_option_videoTargetedFrameRate_title), icon = IconSource( - painter = IconResource.fromImageVector(Icons.Default.Tune).asPainterResource(), + painter = IconResource.fromImageVector(Icons.Default.BrokenImage) + .asPainterResource(), contentDescription = null, ) ), From b309d584a7ded87a817ee4f634ec91e1c602943b Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:56:23 +0100 Subject: [PATCH 035/176] fix: Rename VideoFrameRate.kt -> VideoRecorderFrameRate.kt --- .../Tiles/{VideoFrameRate.kt => VideoRecorderFrameRate.kt} | 3 +-- .../java/app/myzel394/alibi/ui/screens/SettingsScreen.kt | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/{VideoFrameRate.kt => VideoRecorderFrameRate.kt} (98%) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderFrameRate.kt similarity index 98% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderFrameRate.kt index c1235c56b..b7c4fd120 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoFrameRate.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderFrameRate.kt @@ -3,7 +3,6 @@ package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.BrokenImage -import androidx.compose.material.icons.filled.Tune import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api @@ -35,7 +34,7 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable -fun VideoFrameRate( +fun VideoRecorderFrameRate( settings: AppSettings, ) { val scope = rememberCoroutineScope() diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index 0248ce1b8..03dc6eb4a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -38,7 +38,7 @@ import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AboutTile -import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoFrameRate +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderFrameRate import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.CustomNotificationTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DeleteRecordingsImmediatelyTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DividerTitle @@ -164,7 +164,7 @@ fun SettingsScreen( description = stringResource(R.string.ui_settings_sections_audio_description), ) AudioRecorderShowAllMicrophonesTile(settings = settings) - VideoFrameRate(settings = settings) + VideoRecorderFrameRate(settings = settings) AudioRecorderSamplingRateTile(settings = settings) AudioRecorderEncoderTile( snackbarHostState = snackbarHostState, @@ -177,7 +177,7 @@ fun SettingsScreen( description = stringResource(R.string.ui_settings_sections_video_description), ) VideoRecorderBitrateTile(settings = settings) - VideoFrameRate(settings = settings) + VideoRecorderFrameRate(settings = settings) } Divider( modifier = Modifier From 7cf2e14df2fa7a42418f67448c0003135b892253 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:56:50 +0100 Subject: [PATCH 036/176] fix: Rename VideoFrameRate.kt -> VideoRecorderFrameRate.kt --- ...eoRecorderFrameRate.kt => VideoRecorderFrameRateTile.kt} | 2 +- .../java/app/myzel394/alibi/ui/screens/SettingsScreen.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/{VideoRecorderFrameRate.kt => VideoRecorderFrameRateTile.kt} (99%) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderFrameRate.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderFrameRateTile.kt similarity index 99% rename from app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderFrameRate.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderFrameRateTile.kt index b7c4fd120..fb2cfc55c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderFrameRate.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderFrameRateTile.kt @@ -34,7 +34,7 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable -fun VideoRecorderFrameRate( +fun VideoRecorderFrameRateTile( settings: AppSettings, ) { val scope = rememberCoroutineScope() diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index 03dc6eb4a..7772a658b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -38,7 +38,7 @@ import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AboutTile -import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderFrameRate +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderFrameRateTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.CustomNotificationTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DeleteRecordingsImmediatelyTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DividerTitle @@ -164,7 +164,7 @@ fun SettingsScreen( description = stringResource(R.string.ui_settings_sections_audio_description), ) AudioRecorderShowAllMicrophonesTile(settings = settings) - VideoRecorderFrameRate(settings = settings) + VideoRecorderFrameRateTile(settings = settings) AudioRecorderSamplingRateTile(settings = settings) AudioRecorderEncoderTile( snackbarHostState = snackbarHostState, @@ -177,7 +177,7 @@ fun SettingsScreen( description = stringResource(R.string.ui_settings_sections_video_description), ) VideoRecorderBitrateTile(settings = settings) - VideoRecorderFrameRate(settings = settings) + VideoRecorderFrameRateTile(settings = settings) } Divider( modifier = Modifier From e7989e2eba93dce7e0a7a7d810d38dd11e0d7b25 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 18:23:15 +0100 Subject: [PATCH 037/176] feat: Add VideoRecorderQualityTile --- .../java/app/myzel394/alibi/db/AppSettings.kt | 24 +++- .../Tiles/VideoRecorderQualityTile.kt | 118 ++++++++++++++++++ .../alibi/ui/screens/SettingsScreen.kt | 8 +- app/src/main/res/values/strings.xml | 7 ++ 4 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderQualityTile.kt diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 1bc97bfff..44fe94909 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -379,14 +379,21 @@ data class VideoRecorderSettings( return copy(targetedVideoBitRate = bitRate) } - fun setQuality(quality: String): VideoRecorderSettings { - return copy(quality = quality) + fun setQuality(quality: Quality?): VideoRecorderSettings { + val invertedMap = QUALITY_NAME_QUALITY_MAP.entries.associateBy({ it.value }, { it.key }) + + return copy(quality = quality?.let { invertedMap[it] }) } fun setTargetFrameRate(frameRate: Int?): VideoRecorderSettings { return copy(targetFrameRate = frameRate) } + fun getQuality(): Quality? = + quality?.let { + QUALITY_NAME_QUALITY_MAP[it]!! + } + fun getQualitySelector(): QualitySelector? = quality?.let { QualitySelector.from( @@ -428,6 +435,19 @@ data class VideoRecorderSettings( 120, 240, ) + + val AVAILABLE_QUALITIES = listOf( + Quality.HIGHEST, + Quality.UHD, + Quality.FHD, + Quality.HD, + Quality.SD, + Quality.LOWEST, + ) + + val EXAMPLE_QUALITY_VALUES = listOf( + null, + ) + AVAILABLE_QUALITIES } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderQualityTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderQualityTile.kt new file mode 100644 index 000000000..f872d05de --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderQualityTile.kt @@ -0,0 +1,118 @@ +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles + +import android.media.MediaRecorder +import androidx.camera.video.Quality +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.HighQuality +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import app.myzel394.alibi.R +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.db.VideoRecorderSettings +import app.myzel394.alibi.ui.components.atoms.ExampleListRoulette +import app.myzel394.alibi.ui.components.atoms.SettingsTile +import app.myzel394.alibi.ui.utils.IconResource +import com.maxkeppeker.sheets.core.models.base.Header +import com.maxkeppeker.sheets.core.models.base.IconSource +import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState +import com.maxkeppeler.sheets.input.models.InputHeader +import com.maxkeppeler.sheets.list.ListDialog +import com.maxkeppeler.sheets.list.models.ListOption +import com.maxkeppeler.sheets.list.models.ListSelection +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun VideoRecorderQualityTile( + settings: AppSettings, +) { + val QUALITY_NAME_TEXT_MAP = mapOf( + Quality.HIGHEST to stringResource(R.string.ui_settings_value_videoQuality_values_highest), + Quality.UHD to stringResource(R.string.ui_settings_value_videoQuality_values_uhd), + Quality.FHD to stringResource(R.string.ui_settings_value_videoQuality_values_fhd), + Quality.HD to stringResource(R.string.ui_settings_value_videoQuality_values_hd), + Quality.SD to stringResource(R.string.ui_settings_value_videoQuality_values_sd), + Quality.LOWEST to stringResource(R.string.ui_settings_value_videoQuality_values_lowest), + ) + + val scope = rememberCoroutineScope() + val showDialog = rememberUseCaseState() + val dataStore = LocalContext.current.dataStore + + fun updateValue(quality: Quality?) { + scope.launch { + dataStore.updateData { + it.setVideoRecorderSettings( + it.videoRecorderSettings.setQuality(quality) + ) + } + } + } + + ListDialog( + state = showDialog, + header = Header.Default( + title = stringResource(R.string.ui_settings_option_videoQualityTile_title), + icon = IconSource( + painter = IconResource.fromImageVector(Icons.Default.HighQuality) + .asPainterResource(), + contentDescription = null, + ), + ), + selection = ListSelection.Single( + showRadioButtons = true, + options = VideoRecorderSettings.AVAILABLE_QUALITIES.map { quality -> + ListOption( + titleText = QUALITY_NAME_TEXT_MAP[quality]!!, + selected = settings.videoRecorderSettings.quality == quality.toString(), + ) + }.toList() + ) { index, _ -> + val quality = VideoRecorderSettings.AVAILABLE_QUALITIES[index] + + updateValue(quality) + }, + ) + SettingsTile( + title = stringResource(R.string.ui_settings_option_videoQualityTile_title), + leading = { + Icon( + Icons.Default.HighQuality, + contentDescription = null, + ) + }, + trailing = { + Button( + onClick = showDialog::show, + colors = ButtonDefaults.filledTonalButtonColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant, + ), + shape = MaterialTheme.shapes.medium, + ) { + Text( + QUALITY_NAME_TEXT_MAP[settings.videoRecorderSettings.getQuality()] + ?: stringResource( + R.string.ui_settings_value_auto_label + ) + ) + } + }, + extra = { + ExampleListRoulette( + items = listOf(null), + onItemSelected = ::updateValue, + ) { + Text(stringResource(R.string.ui_settings_value_auto_label)) + } + }, + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index 7772a658b..61afec25a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -38,11 +38,12 @@ import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AboutTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderEncoderTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderFrameRateTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.CustomNotificationTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DeleteRecordingsImmediatelyTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.DividerTitle -import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderEncoderTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderQualityTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.ImportExport import app.myzel394.alibi.ui.components.SettingsScreen.atoms.InAppLanguagePicker import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.IntervalDurationTile @@ -158,13 +159,11 @@ fun SettingsScreen( DividerTitle( modifier = Modifier - .fillMaxWidth() - .padding(16.dp), + .fillMaxWidth(), title = stringResource(R.string.ui_settings_sections_audio_title), description = stringResource(R.string.ui_settings_sections_audio_description), ) AudioRecorderShowAllMicrophonesTile(settings = settings) - VideoRecorderFrameRateTile(settings = settings) AudioRecorderSamplingRateTile(settings = settings) AudioRecorderEncoderTile( snackbarHostState = snackbarHostState, @@ -176,6 +175,7 @@ fun SettingsScreen( title = stringResource(R.string.ui_settings_sections_video_title), description = stringResource(R.string.ui_settings_sections_video_description), ) + VideoRecorderQualityTile(settings = settings) VideoRecorderBitrateTile(settings = settings) VideoRecorderFrameRateTile(settings = settings) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bc51404d6..3b1280ad6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -131,4 +131,11 @@ Targeted Frame Rate How many frames per second should be recorded. The actual frame rate may be different. This can for example happen if the device is not able to record with the specified frame rate. + Quality + Highest + Ultra HD + Full HD + HD + Standard + Lowest \ No newline at end of file From 261753ad7509dccf1add43a49bcd20fcfe5576a9 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 18:45:25 +0100 Subject: [PATCH 038/176] feat: Add AudioRecordingStart and VideoRecordingStart --- .../java/app/myzel394/alibi/ui/Navigation.kt | 6 +- .../atoms/AudioRecordingStart.kt | 90 ++++++++++++++++++ .../atoms/VideoRecordingStart.kt | 92 +++++++++++++++++++ .../AudioRecorder/molecules/StartRecording.kt | 80 +++------------- ...dioRecorderScreen.kt => RecorderScreen.kt} | 8 +- app/src/main/res/values/strings.xml | 3 +- 6 files changed, 207 insertions(+), 72 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/AudioRecordingStart.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecordingStart.kt rename app/src/main/java/app/myzel394/alibi/ui/screens/{AudioRecorderScreen.kt => RecorderScreen.kt} (97%) diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index 8a947526f..beff2e4f4 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -23,9 +23,8 @@ import app.myzel394.alibi.ui.enums.Screen import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.models.VideoRecorderModel import app.myzel394.alibi.ui.screens.AboutScreen -import app.myzel394.alibi.ui.screens.AudioRecorderScreen +import app.myzel394.alibi.ui.screens.RecorderScreen import app.myzel394.alibi.ui.screens.CustomRecordingNotificationsScreen -import app.myzel394.alibi.ui.screens.POCVideo import app.myzel394.alibi.ui.screens.SettingsScreen import app.myzel394.alibi.ui.screens.WelcomeScreen @@ -73,9 +72,10 @@ fun Navigation( scaleOut(targetScale = SCALE_IN) + fadeOut(tween(durationMillis = 150)) } ) { - AudioRecorderScreen( + RecorderScreen( navController = navController, audioRecorder = audioRecorder, + videoRecorder = videoRecorder, ) } composable( diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/AudioRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/AudioRecordingStart.kt new file mode 100644 index 000000000..79704174e --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/AudioRecordingStart.kt @@ -0,0 +1,90 @@ +package app.myzel394.alibi.ui.components.AudioRecorder.atoms + +import android.Manifest +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Mic +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.dp +import app.myzel394.alibi.R +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.ui.components.atoms.PermissionRequester +import app.myzel394.alibi.ui.models.AudioRecorderModel + +@Composable +fun AudioRecordingStart( + audioRecorder: AudioRecorderModel, + appSettings: AppSettings, +) { + val context = LocalContext.current + + // We can't get the current `notificationDetails` inside the + // `onPermissionAvailable` function. We'll instead use this hack + // with `LaunchedEffect` to get the current value. + var startRecording by rememberSaveable { mutableStateOf(false) } + LaunchedEffect(startRecording) { + if (startRecording) { + startRecording = false + + audioRecorder.startRecording(context, appSettings) + } + } + + PermissionRequester( + permission = Manifest.permission.RECORD_AUDIO, + icon = Icons.Default.Mic, + onPermissionAvailable = { + startRecording = true + } + ) { trigger -> + val label = stringResource(R.string.ui_audioRecorder_action_start_label) + + Button( + onClick = trigger, + modifier = Modifier + .semantics { + contentDescription = label + } + .size(200.dp) + .clip(shape = CircleShape), + colors = ButtonDefaults.outlinedButtonColors(), + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + Icons.Default.Mic, + contentDescription = null, + modifier = Modifier + .size(80.dp), + ) + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + label, + style = MaterialTheme.typography.titleSmall, + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecordingStart.kt new file mode 100644 index 000000000..7f1fe5b30 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecordingStart.kt @@ -0,0 +1,92 @@ +package app.myzel394.alibi.ui.components.AudioRecorder.atoms + +import android.Manifest +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CameraAlt +import androidx.compose.material.icons.filled.Mic +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.dp +import app.myzel394.alibi.R +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.ui.components.atoms.PermissionRequester +import app.myzel394.alibi.ui.models.AudioRecorderModel +import app.myzel394.alibi.ui.models.VideoRecorderModel + +@Composable +fun VideoRecordingStart( + videoRecorder: VideoRecorderModel, + appSettings: AppSettings, +) { + val context = LocalContext.current + + // We can't get the current `notificationDetails` inside the + // `onPermissionAvailable` function. We'll instead use this hack + // with `LaunchedEffect` to get the current value. + var startRecording by rememberSaveable { mutableStateOf(false) } + LaunchedEffect(startRecording) { + if (startRecording) { + startRecording = false + + videoRecorder.startRecording(context, appSettings) + } + } + + PermissionRequester( + permission = Manifest.permission.RECORD_AUDIO, + icon = Icons.Default.Mic, + onPermissionAvailable = { + startRecording = true + } + ) { trigger -> + val label = stringResource(R.string.ui_videoRecorder_action_start_label) + + Button( + onClick = trigger, + modifier = Modifier + .semantics { + contentDescription = label + } + .size(200.dp) + .clip(shape = CircleShape), + colors = ButtonDefaults.outlinedButtonColors(), + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + Icons.Default.CameraAlt, + contentDescription = null, + modifier = Modifier + .size(80.dp), + ) + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + label, + style = MaterialTheme.typography.titleSmall, + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt index 7289b2f5d..75b4b0625 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt @@ -1,6 +1,5 @@ package app.myzel394.alibi.ui.components.AudioRecorder.molecules -import android.Manifest import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -11,9 +10,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Mic import androidx.compose.material.icons.filled.Save import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults @@ -21,15 +18,8 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme 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.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription @@ -37,18 +27,20 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import app.myzel394.alibi.R -import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE -import app.myzel394.alibi.ui.components.atoms.PermissionRequester +import app.myzel394.alibi.ui.components.AudioRecorder.atoms.AudioRecordingStart +import app.myzel394.alibi.ui.components.AudioRecorder.atoms.VideoRecordingStart import app.myzel394.alibi.ui.effects.rememberForceUpdateOnLifeCycleChange import app.myzel394.alibi.ui.models.AudioRecorderModel +import app.myzel394.alibi.ui.models.VideoRecorderModel import java.time.format.DateTimeFormatter import java.time.format.FormatStyle @Composable fun StartRecording( audioRecorder: AudioRecorderModel, + videoRecorder: VideoRecorderModel, // Loading this from parent, because if we load it ourselves // and permissions have already been granted, initial // settings will be used, instead of the actual settings. @@ -57,70 +49,26 @@ fun StartRecording( ) { val context = LocalContext.current - // We can't get the current `notificationDetails` inside the - // `onPermissionAvailable` function. We'll instead use this hack - // with `LaunchedEffect` to get the current value. - var startRecording by rememberSaveable { mutableStateOf(false) } - LaunchedEffect(startRecording) { - if (startRecording) { - startRecording = false - - audioRecorder.startRecording(context, appSettings) - } - } - Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.SpaceBetween, horizontalAlignment = Alignment.CenterHorizontally, ) { Spacer(modifier = Modifier.weight(1f)) - PermissionRequester( - permission = Manifest.permission.RECORD_AUDIO, - icon = Icons.Default.Mic, - onPermissionAvailable = { - startRecording = true - }, - ) { trigger -> - val label = stringResource(R.string.ui_audioRecorder_action_start_label) - Button( - onClick = trigger, - modifier = Modifier - .semantics { - contentDescription = label - } - .size(200.dp) - .clip(shape = CircleShape), - colors = ButtonDefaults.outlinedButtonColors(), - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Icon( - Icons.Default.Mic, - contentDescription = null, - modifier = Modifier - .size(80.dp), - ) - Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) - Text( - label, - style = MaterialTheme.typography.titleSmall, - ) - } - } - } - val settings = LocalContext - .current - .dataStore - .data - .collectAsState(initial = AppSettings.getDefaultInstance()) - .value + + AudioRecordingStart( + audioRecorder = audioRecorder, + appSettings = appSettings, + ) + VideoRecordingStart( + videoRecorder = videoRecorder, + appSettings = appSettings, + ) Text( stringResource( R.string.ui_audioRecorder_action_start_description, - settings.maxDuration / 1000 / 60 + appSettings.maxDuration / 1000 / 60 ), style = MaterialTheme.typography.bodySmall.copy( color = MaterialTheme.colorScheme.onSurfaceVariant, diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt similarity index 97% rename from app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt rename to app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index 677e4fc8f..18168e2ef 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -48,14 +48,16 @@ import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.ui.effects.rememberSettings import app.myzel394.alibi.ui.models.AudioRecorderModel +import app.myzel394.alibi.ui.models.VideoRecorderModel import kotlinx.coroutines.delay import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable -fun AudioRecorderScreen( +fun RecorderScreen( navController: NavController, audioRecorder: AudioRecorderModel, + videoRecorder: VideoRecorderModel, ) { val snackbarHostState = remember { SnackbarHostState() } val context = LocalContext.current @@ -299,7 +301,9 @@ fun AudioRecorderScreen( RecordingStatus(audioRecorder = audioRecorder) else StartRecording( - audioRecorder = audioRecorder, appSettings = appSettings, + audioRecorder = audioRecorder, + videoRecorder = videoRecorder, + appSettings = appSettings, onSaveLastRecording = ::saveRecording, ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3b1280ad6..2f0e95501 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,7 +18,7 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. - Start Recording + Start Audio Recording Save Recording from %s Delete Delete Recording? @@ -138,4 +138,5 @@ HD Standard Lowest + Start Video Recording \ No newline at end of file From e7e7505592c6b8718eb33ec1809104d49f523cba Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 2 Dec 2023 19:10:44 +0100 Subject: [PATCH 039/176] current stand --- .../atoms/VideoRecorderPreparationSheet.kt | 11 +++++++++++ .../{atoms => molecules}/AudioRecordingStart.kt | 2 +- .../{atoms => molecules}/VideoRecordingStart.kt | 2 +- .../{molecules => organisms}/StartRecording.kt | 6 +++--- .../app/myzel394/alibi/ui/screens/RecorderScreen.kt | 2 +- .../java/app/myzel394/alibi/ui/utils/camera-info.kt | 2 ++ .../{available-microphones.kt => microphone-info.kt} | 0 7 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecorderPreparationSheet.kt rename app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/{atoms => molecules}/AudioRecordingStart.kt (97%) rename app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/{atoms => molecules}/VideoRecordingStart.kt (98%) rename app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/{molecules => organisms}/StartRecording.kt (95%) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt rename app/src/main/java/app/myzel394/alibi/ui/utils/{available-microphones.kt => microphone-info.kt} (100%) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecorderPreparationSheet.kt new file mode 100644 index 000000000..0cb9b13a2 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecorderPreparationSheet.kt @@ -0,0 +1,11 @@ +package app.myzel394.alibi.ui.components.AudioRecorder.atoms + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun VideoRecorderPreparationSheet() { + val sheetState = rememberModalBottomSheetState() +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/AudioRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/AudioRecordingStart.kt similarity index 97% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/AudioRecordingStart.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/AudioRecordingStart.kt index 79704174e..7528c1ede 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/AudioRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/AudioRecordingStart.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.AudioRecorder.molecules import android.Manifest import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt similarity index 98% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecordingStart.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt index 7f1fe5b30..c1e9f6456 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.AudioRecorder.molecules import android.Manifest import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/StartRecording.kt similarity index 95% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/StartRecording.kt index 75b4b0625..f02e301a4 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/StartRecording.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.molecules +package app.myzel394.alibi.ui.components.AudioRecorder.organisms import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -29,8 +29,8 @@ import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.AudioRecordingStart -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.VideoRecordingStart +import app.myzel394.alibi.ui.components.AudioRecorder.molecules.AudioRecordingStart +import app.myzel394.alibi.ui.components.AudioRecorder.molecules.VideoRecordingStart import app.myzel394.alibi.ui.effects.rememberForceUpdateOnLifeCycleChange import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.models.VideoRecorderModel diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index 18168e2ef..0810bf29d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -38,7 +38,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController import app.myzel394.alibi.ui.components.AudioRecorder.organisms.RecordingStatus -import app.myzel394.alibi.ui.components.AudioRecorder.molecules.StartRecording +import app.myzel394.alibi.ui.components.AudioRecorder.organisms.StartRecording import app.myzel394.alibi.ui.enums.Screen import app.myzel394.alibi.ui.utils.rememberFileSaverDialog import app.myzel394.alibi.R diff --git a/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt b/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt new file mode 100644 index 000000000..1aebb0fad --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt @@ -0,0 +1,2 @@ +package app.myzel394.alibi.ui.utils + diff --git a/app/src/main/java/app/myzel394/alibi/ui/utils/available-microphones.kt b/app/src/main/java/app/myzel394/alibi/ui/utils/microphone-info.kt similarity index 100% rename from app/src/main/java/app/myzel394/alibi/ui/utils/available-microphones.kt rename to app/src/main/java/app/myzel394/alibi/ui/utils/microphone-info.kt From 817e9d96d00eee5b8f2d2f6687e2d6739922b937 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:37:04 +0100 Subject: [PATCH 040/176] feat: Add VideoRecorderPreparationSheet --- .../java/app/myzel394/alibi/ui/Navigation.kt | 6 + .../atoms/CameraSelectionButton.kt | 103 +++++++++++++++++ .../atoms/VideoRecorderPreparationSheet.kt | 11 -- .../molecules/CamerasSelection.kt | 73 ++++++++++++ .../VideoRecorderPreparationSheet.kt | 109 ++++++++++++++++++ .../molecules/VideoRecordingStart.kt | 24 ++-- .../ui/models/VideoRecorderSettingsModel.kt | 15 +++ .../myzel394/alibi/ui/utils/camera-info.kt | 42 +++++++ app/src/main/res/values/strings.xml | 8 ++ 9 files changed, 367 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraSelectionButton.kt delete mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecorderPreparationSheet.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/CamerasSelection.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecorderPreparationSheet.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index beff2e4f4..122425725 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -1,5 +1,9 @@ package app.myzel394.alibi.ui +import android.content.Context +import android.hardware.camera2.CameraCharacteristics +import android.hardware.camera2.CameraManager +import androidx.camera.core.CameraX import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -11,6 +15,7 @@ import androidx.compose.foundation.background import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -27,6 +32,7 @@ import app.myzel394.alibi.ui.screens.RecorderScreen import app.myzel394.alibi.ui.screens.CustomRecordingNotificationsScreen import app.myzel394.alibi.ui.screens.SettingsScreen import app.myzel394.alibi.ui.screens.WelcomeScreen +import app.myzel394.alibi.ui.utils.CameraInfo const val SCALE_IN = 1.25f diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraSelectionButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraSelectionButton.kt new file mode 100644 index 000000000..d8a332ad5 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraSelectionButton.kt @@ -0,0 +1,103 @@ +package app.myzel394.alibi.ui.components.AudioRecorder.atoms + +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Camera +import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.Videocam +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.myzel394.alibi.R +import app.myzel394.alibi.ui.utils.CameraInfo + +@Composable +fun CameraSelectionButton( + cameraID: CameraInfo.Lens, + selected: Boolean, + onSelected: () -> Unit, + label: String, + description: String? = null, +) { + val backgroundColor by animateColorAsState( + targetValue = if (selected) MaterialTheme.colorScheme.secondaryContainer.copy( + alpha = 0.2f + ) else Color.Transparent, + // Make animation about 0.5x faster than default + animationSpec = spring( + stiffness = Spring.StiffnessLow, + dampingRatio = Spring.DampingRatioNoBouncy, + ), + label = "backgroundColor" + ) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .clip(MaterialTheme.shapes.medium) + .clickable(onClick = onSelected) + .background(backgroundColor) + .padding(vertical = 8.dp, horizontal = 12.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + RadioButton( + selected = selected, + onClick = onSelected, + ) + if (description == null) { + Text( + label, + style = MaterialTheme.typography.labelLarge, + ) + } else { + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + label, + style = MaterialTheme.typography.labelLarge, + ) + Text( + description, + style = MaterialTheme.typography.bodySmall, + ) + } + } + } + Icon( + CAMERA_LENS_ICON_MAP[cameraID]!!, + contentDescription = null, + modifier = Modifier + .size(24.dp), + ) + } +} + +val CAMERA_LENS_ICON_MAP = mapOf( + CameraInfo.Lens.BACK to Icons.Default.Camera, + CameraInfo.Lens.FRONT to Icons.Default.Person, + CameraInfo.Lens.EXTERNAL to Icons.Default.Videocam, +) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecorderPreparationSheet.kt deleted file mode 100644 index 0cb9b13a2..000000000 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/VideoRecorderPreparationSheet.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms - -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.rememberModalBottomSheetState -import androidx.compose.runtime.Composable - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun VideoRecorderPreparationSheet() { - val sheetState = rememberModalBottomSheetState() -} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/CamerasSelection.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/CamerasSelection.kt new file mode 100644 index 000000000..527da4774 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/CamerasSelection.kt @@ -0,0 +1,73 @@ +package app.myzel394.alibi.ui.components.AudioRecorder.molecules + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Camera +import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.Videocam +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.myzel394.alibi.R +import app.myzel394.alibi.ui.components.AudioRecorder.atoms.CameraSelectionButton +import app.myzel394.alibi.ui.models.VideoRecorderSettingsModel +import app.myzel394.alibi.ui.utils.CameraInfo + +@Composable +fun CamerasSelection( + cameras: Iterable, + videoSettings: VideoRecorderSettingsModel +) { + val CAMERA_LENS_TEXT_MAP = mapOf( + CameraInfo.Lens.BACK to stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_back_label), + CameraInfo.Lens.FRONT to stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_front_label), + CameraInfo.Lens.EXTERNAL to stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_external_label), + ) + + Column { + if (cameras.count() == 2 && cameras.elementAt(0).id == 0 && cameras.elementAt(1).id == 1) { + CameraSelectionButton( + cameraID = CameraInfo.Lens.BACK, + label = stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_back_label), + selected = videoSettings.cameraID == 0, + onSelected = { + videoSettings.cameraID = 0 + }, + ) + CameraSelectionButton( + cameraID = CameraInfo.Lens.FRONT, + label = stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_front_label), + selected = videoSettings.cameraID == 1, + onSelected = { + videoSettings.cameraID = 1 + }, + ) + } else { + cameras.forEach { camera -> + CameraSelectionButton( + cameraID = CameraInfo.CAMERA_INT_TO_LENS_MAP[camera.id]!!, + selected = videoSettings.cameraID == camera.id, + onSelected = { + videoSettings.cameraID = camera.id + }, + label = stringResource( + R.string.ui_videoRecorder_action_start_settings_cameraLens_label, + camera.id + ), + description = CAMERA_LENS_TEXT_MAP[camera.lens]!!, + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecorderPreparationSheet.kt new file mode 100644 index 000000000..eb96d692a --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecorderPreparationSheet.kt @@ -0,0 +1,109 @@ +package app.myzel394.alibi.ui.components.AudioRecorder.molecules + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Camera +import androidx.compose.material.icons.filled.CameraAlt +import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.Videocam +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import app.myzel394.alibi.R +import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE +import app.myzel394.alibi.ui.components.atoms.GlobalSwitch +import app.myzel394.alibi.ui.models.VideoRecorderSettingsModel +import app.myzel394.alibi.ui.utils.CameraInfo + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun VideoRecorderPreparationSheet( + onDismiss: () -> Unit, + videoSettings: VideoRecorderSettingsModel = viewModel() +) { + val sheetState = rememberModalBottomSheetState(true) + + val context = LocalContext.current + val cameras = CameraInfo.queryAvailableCameras(context) + + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = sheetState, + ) { + Column( + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(30.dp), + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Icon( + Icons.Default.CameraAlt, + contentDescription = null, + modifier = Modifier + .size(80.dp), + ) + Text( + stringResource(R.string.ui_videoRecorder_action_start_settings_label), + style = MaterialTheme.typography.labelLarge, + ) + } + GlobalSwitch( + label = stringResource(R.string.ui_videoRecorder_action_start_settings_enableAudio_label), + checked = videoSettings.enableAudio, + onCheckedChange = { + videoSettings.enableAudio = it + } + ) + + Text( + stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_selection_label), + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Start, + modifier = Modifier.fillMaxWidth() + ) + CamerasSelection( + cameras = cameras, + videoSettings = videoSettings, + ) + + val label = stringResource(R.string.ui_videoRecorder_action_start_settings_start_label) + Button( + onClick = {}, + modifier = Modifier + .padding(16.dp) + .fillMaxWidth() + .height(BIG_PRIMARY_BUTTON_SIZE) + .semantics { + contentDescription = label + } + ) { + Text(label) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt index c1e9f6456..5e2be6231 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt @@ -15,7 +15,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable @@ -31,7 +30,6 @@ import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.components.atoms.PermissionRequester -import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.models.VideoRecorderModel @Composable @@ -41,24 +39,24 @@ fun VideoRecordingStart( ) { val context = LocalContext.current - // We can't get the current `notificationDetails` inside the - // `onPermissionAvailable` function. We'll instead use this hack - // with `LaunchedEffect` to get the current value. - var startRecording by rememberSaveable { mutableStateOf(false) } - LaunchedEffect(startRecording) { - if (startRecording) { - startRecording = false + var showSheet by rememberSaveable { + mutableStateOf(false) + } - videoRecorder.startRecording(context, appSettings) - } + if (showSheet) { + VideoRecorderPreparationSheet( + onDismiss = { + showSheet = false + }, + ) } PermissionRequester( permission = Manifest.permission.RECORD_AUDIO, icon = Icons.Default.Mic, onPermissionAvailable = { - startRecording = true - } + showSheet = true + }, ) { trigger -> val label = stringResource(R.string.ui_videoRecorder_action_start_label) diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt new file mode 100644 index 000000000..fd376d505 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt @@ -0,0 +1,15 @@ +package app.myzel394.alibi.ui.models + +import android.graphics.Camera +import android.hardware.camera2.CameraManager +import androidx.camera.core.CameraSelector +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel + +class VideoRecorderSettingsModel : ViewModel() { + var enableAudio by mutableStateOf(true) + var cameraID by mutableIntStateOf(0) +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt b/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt index 1aebb0fad..050c4674f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt @@ -1,2 +1,44 @@ package app.myzel394.alibi.ui.utils +import android.content.Context +import android.hardware.camera2.CameraCharacteristics +import android.hardware.camera2.CameraManager +import android.hardware.camera2.CameraMetadata + +data class CameraInfo( + val lens: Lens, + val id: Int, +) { + enum class Lens(val androidValue: Int) { + BACK(CameraMetadata.LENS_FACING_BACK), + FRONT(CameraMetadata.LENS_FACING_FRONT), + EXTERNAL(CameraMetadata.LENS_FACING_EXTERNAL), + } + + companion object { + val CAMERA_INT_TO_LENS_MAP = mapOf( + 0 to Lens.BACK, + 1 to Lens.FRONT, + 2 to Lens.EXTERNAL, + ) + + fun queryAvailableCameras(context: Context): List { + val camera = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager + + return camera.cameraIdList.map { id -> + val lensFacing = + camera.getCameraCharacteristics(id).get(CameraCharacteristics.LENS_FACING) + ?: return@map null + + fromCameraId(id, lensFacing) + }.filterNotNull() + } + + fun fromCameraId(cameraId: String, lensFacing: Int): CameraInfo { + return CameraInfo( + lens = CAMERA_INT_TO_LENS_MAP[lensFacing]!!, + id = cameraId.toInt(), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2f0e95501..c8e98dd00 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -139,4 +139,12 @@ Standard Lowest Start Video Recording + Record with Audio + Back facing + Front facing + External Camera + Camera %s + Start Recording + Prepare your recording + Select camera \ No newline at end of file From 40eee79aa3ab1fe1daea8f6d73586bdb3dcf88b3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 3 Dec 2023 22:16:09 +0100 Subject: [PATCH 041/176] feat: Adding camera preview functionality --- .../java/app/myzel394/alibi/MainActivity.kt | 1 + .../AudioRecorder/atoms/CameraPreview.kt | 57 ++++++ .../VideoRecorderPreparationSheet.kt | 180 +++++++++++++----- .../molecules/VideoRecordingStart.kt | 6 + .../AudioRecorder/organisms/StartRecording.kt | 15 +- .../alibi/ui/screens/RecorderScreen.kt | 48 +++-- 6 files changed, 244 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraPreview.kt diff --git a/app/src/main/java/app/myzel394/alibi/MainActivity.kt b/app/src/main/java/app/myzel394/alibi/MainActivity.kt index 5b817295f..664b96ee0 100644 --- a/app/src/main/java/app/myzel394/alibi/MainActivity.kt +++ b/app/src/main/java/app/myzel394/alibi/MainActivity.kt @@ -2,6 +2,7 @@ package app.myzel394.alibi import android.content.Context import android.os.Bundle +import android.view.MotionEvent import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraPreview.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraPreview.kt new file mode 100644 index 000000000..494db9191 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraPreview.kt @@ -0,0 +1,57 @@ +package app.myzel394.alibi.ui.components.AudioRecorder.atoms + +import android.view.MotionEvent +import android.view.ViewGroup +import androidx.camera.core.CameraSelector +import androidx.camera.core.Preview +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.camera.view.PreviewView +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.viewinterop.AndroidView +import kotlinx.coroutines.launch + +@Composable +fun CameraPreview( + modifier: Modifier = Modifier, + scaleType: PreviewView.ScaleType = PreviewView.ScaleType.FILL_CENTER, + cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA +) { + val coroutineScope = rememberCoroutineScope() + val lifecycleOwner = LocalLifecycleOwner.current + AndroidView( + modifier = modifier, + factory = { context -> + val previewView = PreviewView(context).apply { + this.scaleType = scaleType + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + } + + // CameraX Preview UseCase + val previewUseCase = Preview.Builder() + .build() + .also { + it.setSurfaceProvider(previewView.surfaceProvider) + } + + coroutineScope.launch { + val cameraProvider = ProcessCameraProvider.getInstance(context).get() + try { + // Must unbind the use-cases before rebinding them. + cameraProvider.unbindAll() + cameraProvider.bindToLifecycle( + lifecycleOwner, cameraSelector, previewUseCase + ) + } catch (ex: Exception) { + } + } + + previewView + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecorderPreparationSheet.kt index eb96d692a..46bdb18e8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecorderPreparationSheet.kt @@ -1,12 +1,27 @@ package app.myzel394.alibi.ui.components.AudioRecorder.molecules +import android.graphics.Point +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.gestures.awaitEachGesture +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.gestures.forEachGesture +import androidx.compose.foundation.gestures.waitForUpOrCancellation +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsPressedAsState 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.fillMaxHeight +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.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Camera import androidx.compose.material.icons.filled.CameraAlt @@ -21,88 +36,167 @@ import androidx.compose.material3.RadioButton import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.clip +import androidx.compose.ui.input.pointer.PointerEvent +import androidx.compose.ui.input.pointer.PointerInputChange +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Popup import androidx.lifecycle.viewmodel.compose.viewModel import app.myzel394.alibi.R import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE +import app.myzel394.alibi.ui.components.AudioRecorder.atoms.CameraPreview import app.myzel394.alibi.ui.components.atoms.GlobalSwitch import app.myzel394.alibi.ui.models.VideoRecorderSettingsModel import app.myzel394.alibi.ui.utils.CameraInfo -@OptIn(ExperimentalMaterial3Api::class) +@OptIn( + ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class, + ExperimentalComposeUiApi::class +) @Composable fun VideoRecorderPreparationSheet( onDismiss: () -> Unit, - videoSettings: VideoRecorderSettingsModel = viewModel() + videoSettings: VideoRecorderSettingsModel = viewModel(), + onPreviewVisible: () -> Unit, + onPreviewHidden: () -> Unit, + showPreview: Boolean, ) { val sheetState = rememberModalBottomSheetState(true) val context = LocalContext.current val cameras = CameraInfo.queryAvailableCameras(context) - ModalBottomSheet( - onDismissRequest = onDismiss, - sheetState = sheetState, - ) { - Column( + if (showPreview) + CameraPreview( modifier = Modifier - .padding(horizontal = 16.dp, vertical = 24.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(30.dp), + .fillMaxWidth() + .fillMaxHeight() + ) + else { + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = sheetState, ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp), + Box( ) { - Icon( - Icons.Default.CameraAlt, - contentDescription = null, + Column( modifier = Modifier - .size(80.dp), - ) - Text( - stringResource(R.string.ui_videoRecorder_action_start_settings_label), - style = MaterialTheme.typography.labelLarge, - ) - } - GlobalSwitch( - label = stringResource(R.string.ui_videoRecorder_action_start_settings_enableAudio_label), - checked = videoSettings.enableAudio, - onCheckedChange = { - videoSettings.enableAudio = it + .padding(horizontal = 16.dp, vertical = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(30.dp), + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Icon( + Icons.Default.CameraAlt, + contentDescription = null, + modifier = Modifier + .size(80.dp), + ) + Text( + stringResource(R.string.ui_videoRecorder_action_start_settings_label), + style = MaterialTheme.typography.labelLarge, + ) + } + GlobalSwitch( + label = stringResource(R.string.ui_videoRecorder_action_start_settings_enableAudio_label), + checked = videoSettings.enableAudio, + onCheckedChange = { + videoSettings.enableAudio = it + } + ) + + Text( + stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_selection_label), + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Start, + modifier = Modifier.fillMaxWidth() + ) + CamerasSelection( + cameras = cameras, + videoSettings = videoSettings, + ) + + Box( + modifier = Modifier + .fillMaxWidth() + .height(BIG_PRIMARY_BUTTON_SIZE) + ) } - ) - Text( - stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_selection_label), - style = MaterialTheme.typography.labelLarge, - textAlign = TextAlign.Start, - modifier = Modifier.fillMaxWidth() - ) - CamerasSelection( - cameras = cameras, - videoSettings = videoSettings, - ) + } + } + } + + Popup( + alignment = Alignment.BottomCenter, + ) { + val label = + stringResource(R.string.ui_videoRecorder_action_start_settings_start_label) + + Box( + modifier = Modifier + .pointerInput(Unit) { + awaitEachGesture { + while (true) { + val event = awaitPointerEvent() + // consume all changes - val label = stringResource(R.string.ui_videoRecorder_action_start_settings_start_label) - Button( - onClick = {}, + if (!event.changes.elementAt(0).pressed) { + onPreviewHidden() + break + } + } + } + } + .let { + if (showPreview) it.alpha(0.2f) else it + } + ) { + Row( modifier = Modifier .padding(16.dp) .fillMaxWidth() .height(BIG_PRIMARY_BUTTON_SIZE) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.primary) + .padding(16.dp) .semantics { contentDescription = label } + .pointerInput(Unit) { + detectTapGestures( + onLongPress = { + onPreviewVisible() + } + ) + }, + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, ) { - Text(label) + Text( + label, + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onPrimary, + ) } } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt index 5e2be6231..6abb1d95b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt @@ -36,6 +36,9 @@ import app.myzel394.alibi.ui.models.VideoRecorderModel fun VideoRecordingStart( videoRecorder: VideoRecorderModel, appSettings: AppSettings, + onHideAudioRecording: () -> Unit, + onShowAudioRecording: () -> Unit, + showPreview: Boolean, ) { val context = LocalContext.current @@ -48,6 +51,9 @@ fun VideoRecordingStart( onDismiss = { showSheet = false }, + onPreviewVisible = onHideAudioRecording, + onPreviewHidden = onShowAudioRecording, + showPreview = showPreview, ) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/StartRecording.kt index f02e301a4..77c1b30f0 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/StartRecording.kt @@ -46,6 +46,9 @@ fun StartRecording( // settings will be used, instead of the actual settings. appSettings: AppSettings, onSaveLastRecording: () -> Unit, + onHideTopBar: () -> Unit, + onShowTopBar: () -> Unit, + showAudioRecorder: Boolean, ) { val context = LocalContext.current @@ -56,13 +59,17 @@ fun StartRecording( ) { Spacer(modifier = Modifier.weight(1f)) - AudioRecordingStart( - audioRecorder = audioRecorder, - appSettings = appSettings, - ) + if (showAudioRecorder) + AudioRecordingStart( + audioRecorder = audioRecorder, + appSettings = appSettings, + ) VideoRecordingStart( videoRecorder = videoRecorder, appSettings = appSettings, + onHideAudioRecording = onHideTopBar, + onShowAudioRecording = onShowTopBar, + showPreview = !showAudioRecorder, ) Text( diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index 0810bf29d..69dc3b119 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -253,6 +253,14 @@ fun RecorderScreen( } } ) + + + // TopAppBar and AudioRecordingStart should be hidden when + // the video preview is visible. + // We need to preview the video inline to + // be able to capture the touch release event. + var topBarVisible by remember { mutableStateOf(true) } + Scaffold( snackbarHost = { SnackbarHost( @@ -270,23 +278,24 @@ fun RecorderScreen( ) }, topBar = { - TopAppBar( - title = { - Text(stringResource(R.string.app_name)) - }, - actions = { - IconButton( - onClick = { - navController.navigate(Screen.Settings.route) - }, - ) { - Icon( - Icons.Default.Settings, - contentDescription = null - ) + if (topBarVisible) + return@Scaffold TopAppBar( + title = { + Text(stringResource(R.string.app_name)) + }, + actions = { + IconButton( + onClick = { + navController.navigate(Screen.Settings.route) + }, + ) { + Icon( + Icons.Default.Settings, + contentDescription = null + ) + } } - } - ) + ) }, ) { padding -> Box( @@ -305,6 +314,13 @@ fun RecorderScreen( videoRecorder = videoRecorder, appSettings = appSettings, onSaveLastRecording = ::saveRecording, + showAudioRecorder = topBarVisible, + onHideTopBar = { + topBarVisible = false + }, + onShowTopBar = { + topBarVisible = true + }, ) } } From 22876c3be5eb4d8846b819f792f1a3db8fa0c265 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 4 Dec 2023 08:44:29 +0100 Subject: [PATCH 042/176] refactor: Rename package AudioRecorder -> RecorderScreen --- .../atoms/AudioVisualizer.kt | 4 +-- .../atoms/CameraPreview.kt | 3 +-- .../atoms/CameraSelectionButton.kt | 4 +-- .../atoms/ConfirmDeletionDialog.kt | 2 +- .../atoms/DeleteButton.kt | 8 +----- .../atoms/MicrophoneDisconnectedDialog.kt | 2 +- .../atoms/MicrophoneReconnectedDialog.kt | 3 +-- .../atoms/MicrophoneSelectionButton.kt | 8 +----- .../atoms/MicrophoneTypeIcon.kt | 6 +---- .../atoms/PauseResumeButton.kt | 4 +-- .../atoms/RealTimeAudioVisualizer.kt | 3 +-- .../atoms/RecordingTime.kt | 3 +-- .../atoms/SaveButton.kt | 7 +----- .../molecules/AudioRecordingStart.kt | 2 +- .../molecules/CamerasSelection.kt | 20 ++------------- .../molecules/MicrophoneSelection.kt | 9 +++---- .../molecules/MicrophoneStatus.kt | 12 +++------ .../VideoRecorderPreparationSheet.kt | 25 ++----------------- .../molecules/VideoRecordingStart.kt | 2 +- .../organisms/RecordingStatus.kt | 22 ++++++---------- .../organisms/StartRecording.kt | 6 ++--- .../alibi/ui/screens/RecorderScreen.kt | 4 +-- 22 files changed, 38 insertions(+), 121 deletions(-) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/AudioVisualizer.kt (92%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/CameraPreview.kt (95%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/CameraSelectionButton.kt (96%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/ConfirmDeletionDialog.kt (97%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/DeleteButton.kt (81%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/MicrophoneDisconnectedDialog.kt (96%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/MicrophoneReconnectedDialog.kt (94%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/MicrophoneSelectionButton.kt (90%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/MicrophoneTypeIcon.kt (80%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/PauseResumeButton.kt (86%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/RealTimeAudioVisualizer.kt (97%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/RecordingTime.kt (92%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/atoms/SaveButton.kt (82%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/molecules/AudioRecordingStart.kt (97%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/molecules/CamerasSelection.kt (72%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/molecules/MicrophoneSelection.kt (95%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/molecules/MicrophoneStatus.kt (78%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/molecules/VideoRecorderPreparationSheet.kt (84%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/molecules/VideoRecordingStart.kt (97%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/organisms/RecordingStatus.kt (81%) rename app/src/main/java/app/myzel394/alibi/ui/components/{AudioRecorder => RecorderScreen}/organisms/StartRecording.kt (95%) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/AudioVisualizer.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/AudioVisualizer.kt similarity index 92% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/AudioVisualizer.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/AudioVisualizer.kt index 6ec7f0c0d..771c5acc0 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/AudioVisualizer.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/AudioVisualizer.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.fillMaxWidth @@ -29,7 +29,7 @@ fun AudioVisualizer( val width = this.size.width val boxWidth = width / amplitudes.size - amplitudes.forEachIndexed {index, amplitude -> + amplitudes.forEachIndexed { index, amplitude -> val x = boxWidth * index val amplitudePercentage = (amplitude.toFloat() / MAX_AMPLITUDE).coerceAtMost(1f) val boxHeight = height * amplitudePercentage diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraPreview.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/CameraPreview.kt similarity index 95% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraPreview.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/CameraPreview.kt index 494db9191..d0530ac9b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraPreview.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/CameraPreview.kt @@ -1,6 +1,5 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms -import android.view.MotionEvent import android.view.ViewGroup import androidx.camera.core.CameraSelector import androidx.camera.core.Preview diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraSelectionButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/CameraSelectionButton.kt similarity index 96% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraSelectionButton.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/CameraSelectionButton.kt index d8a332ad5..7a13c3c95 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/CameraSelectionButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/CameraSelectionButton.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Spring @@ -25,9 +25,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import app.myzel394.alibi.R import app.myzel394.alibi.ui.utils.CameraInfo @Composable diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/ConfirmDeletionDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/ConfirmDeletionDialog.kt similarity index 97% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/ConfirmDeletionDialog.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/ConfirmDeletionDialog.kt index e57ae51b2..f43a657a2 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/ConfirmDeletionDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/ConfirmDeletionDialog.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.size diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/DeleteButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/DeleteButton.kt similarity index 81% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/DeleteButton.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/DeleteButton.kt index 9c4b4df29..24e54638f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/DeleteButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/DeleteButton.kt @@ -1,13 +1,7 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Delete import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneDisconnectedDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneDisconnectedDialog.kt similarity index 96% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneDisconnectedDialog.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneDisconnectedDialog.kt index e15cba5d7..a844b0fc8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneDisconnectedDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneDisconnectedDialog.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MicOff diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneReconnectedDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneReconnectedDialog.kt similarity index 94% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneReconnectedDialog.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneReconnectedDialog.kt index b8825ac3e..ab30290b9 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneReconnectedDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneReconnectedDialog.kt @@ -1,7 +1,6 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.MicOff import androidx.compose.material.icons.filled.Star import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneSelectionButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneSelectionButton.kt similarity index 90% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneSelectionButton.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneSelectionButton.kt index 47b0ca2b6..35500132f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneSelectionButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneSelectionButton.kt @@ -1,20 +1,14 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms import android.os.Build -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MicExternalOn -import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneTypeIcon.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneTypeIcon.kt similarity index 80% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneTypeIcon.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneTypeIcon.kt index e54a5a11d..76042bdcc 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/MicrophoneTypeIcon.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneTypeIcon.kt @@ -1,14 +1,10 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms -import android.R import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.BluetoothAudio -import androidx.compose.material.icons.filled.Memory import androidx.compose.material.icons.filled.Mic import androidx.compose.material.icons.filled.MicExternalOn -import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Smartphone -import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/PauseResumeButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/PauseResumeButton.kt similarity index 86% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/PauseResumeButton.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/PauseResumeButton.kt index 653645c28..b7ef23784 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/PauseResumeButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/PauseResumeButton.kt @@ -1,12 +1,10 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Pause import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon -import androidx.compose.material3.LargeFloatingActionButton -import androidx.compose.material3.SmallFloatingActionButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/RealTimeAudioVisualizer.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RealTimeAudioVisualizer.kt similarity index 97% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/RealTimeAudioVisualizer.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RealTimeAudioVisualizer.kt index 7e9a2f7f2..52cb54276 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/RealTimeAudioVisualizer.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RealTimeAudioVisualizer.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.LinearEasing @@ -19,7 +19,6 @@ import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp -import app.myzel394.alibi.services.RecorderService import app.myzel394.alibi.ui.MAX_AMPLITUDE import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.utils.clamp diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/RecordingTime.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingTime.kt similarity index 92% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/RecordingTime.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingTime.kt index 3e3a21f46..8bf5e1680 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/RecordingTime.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingTime.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -17,7 +17,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import app.myzel394.alibi.ui.components.atoms.Pulsating -import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.utils.formatDuration @Composable diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/SaveButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt similarity index 82% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/SaveButton.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt index 0a65bcc51..7304bf998 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/atoms/SaveButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt @@ -1,9 +1,6 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.atoms import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons @@ -18,9 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.unit.dp import app.myzel394.alibi.R -import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE @Composable fun SaveButton( diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/AudioRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt similarity index 97% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/AudioRecordingStart.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt index 7528c1ede..6eebedd6c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/AudioRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.molecules +package app.myzel394.alibi.ui.components.RecorderScreen.molecules import android.Manifest import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/CamerasSelection.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt similarity index 72% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/CamerasSelection.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt index 527da4774..764498e71 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/CamerasSelection.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt @@ -1,26 +1,10 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.molecules +package app.myzel394.alibi.ui.components.RecorderScreen.molecules -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Camera -import androidx.compose.material.icons.filled.Person -import androidx.compose.material.icons.filled.Videocam -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.RadioButton -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import app.myzel394.alibi.R -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.CameraSelectionButton +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.CameraSelectionButton import app.myzel394.alibi.ui.models.VideoRecorderSettingsModel import app.myzel394.alibi.ui.utils.CameraInfo diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/MicrophoneSelection.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt similarity index 95% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/MicrophoneSelection.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt index df055ce15..9250e0722 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/MicrophoneSelection.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt @@ -1,13 +1,10 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.molecules +package app.myzel394.alibi.ui.components.RecorderScreen.molecules 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.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.layout.size import androidx.compose.foundation.layout.width @@ -38,8 +35,8 @@ import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneSelectionButton -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneTypeInfo +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.MicrophoneSelectionButton +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.MicrophoneTypeInfo import app.myzel394.alibi.ui.components.atoms.MessageBox import app.myzel394.alibi.ui.components.atoms.MessageType import app.myzel394.alibi.ui.models.AudioRecorderModel diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/MicrophoneStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneStatus.kt similarity index 78% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/MicrophoneStatus.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneStatus.kt index 89662241b..11513bb89 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/MicrophoneStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneStatus.kt @@ -1,21 +1,15 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.molecules +package app.myzel394.alibi.ui.components.RecorderScreen.molecules -import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.platform.LocalContext -import app.myzel394.alibi.dataStore -import app.myzel394.alibi.db.AppSettings -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneDisconnectedDialog -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneReconnectedDialog +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.MicrophoneDisconnectedDialog +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.MicrophoneReconnectedDialog import app.myzel394.alibi.ui.effects.rememberPrevious import app.myzel394.alibi.ui.models.AudioRecorderModel -import app.myzel394.alibi.ui.utils.MicrophoneInfo @Composable fun MicrophoneStatus( diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt similarity index 84% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecorderPreparationSheet.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index 46bdb18e8..5826aa035 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -1,55 +1,34 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.molecules +package app.myzel394.alibi.ui.components.RecorderScreen.molecules -import android.graphics.Point import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.gestures.forEachGesture -import androidx.compose.foundation.gestures.waitForUpOrCancellation -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.collectIsPressedAsState 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.fillMaxHeight -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.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Camera import androidx.compose.material.icons.filled.CameraAlt -import androidx.compose.material.icons.filled.Person -import androidx.compose.material.icons.filled.Videocam -import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.RadioButton import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip -import androidx.compose.ui.input.pointer.PointerEvent -import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription @@ -60,7 +39,7 @@ import androidx.compose.ui.window.Popup import androidx.lifecycle.viewmodel.compose.viewModel import app.myzel394.alibi.R import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.CameraPreview +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.CameraPreview import app.myzel394.alibi.ui.components.atoms.GlobalSwitch import app.myzel394.alibi.ui.models.VideoRecorderSettingsModel import app.myzel394.alibi.ui.utils.CameraInfo diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt similarity index 97% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt index 6abb1d95b..897ae2757 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.molecules +package app.myzel394.alibi.ui.components.RecorderScreen.molecules import android.Manifest import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/RecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecordingStatus.kt similarity index 81% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/RecordingStatus.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecordingStatus.kt index d76154d75..dd973a7bf 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/RecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecordingStatus.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.organisms +package app.myzel394.alibi.ui.components.RecorderScreen.organisms import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.tween @@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.width import androidx.compose.material3.LinearProgressIndicator import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -24,21 +23,14 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import app.myzel394.alibi.dataStore -import app.myzel394.alibi.db.AppSettings -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.DeleteButton -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneDisconnectedDialog -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.MicrophoneReconnectedDialog -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.PauseResumeButton -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.RealtimeAudioVisualizer -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.RecordingTime -import app.myzel394.alibi.ui.components.AudioRecorder.atoms.SaveButton -import app.myzel394.alibi.ui.components.AudioRecorder.molecules.MicrophoneSelection -import app.myzel394.alibi.ui.components.AudioRecorder.molecules.MicrophoneStatus -import app.myzel394.alibi.ui.effects.rememberPrevious +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.DeleteButton +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.PauseResumeButton +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RealtimeAudioVisualizer +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingTime +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveButton +import app.myzel394.alibi.ui.components.RecorderScreen.molecules.MicrophoneStatus import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.utils.KeepScreenOn -import app.myzel394.alibi.ui.utils.MicrophoneInfo import kotlinx.coroutines.delay import java.time.LocalDateTime diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt similarity index 95% rename from app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/StartRecording.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt index 77c1b30f0..e6f7f5046 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/organisms/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.AudioRecorder.organisms +package app.myzel394.alibi.ui.components.RecorderScreen.organisms import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -29,8 +29,8 @@ import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE -import app.myzel394.alibi.ui.components.AudioRecorder.molecules.AudioRecordingStart -import app.myzel394.alibi.ui.components.AudioRecorder.molecules.VideoRecordingStart +import app.myzel394.alibi.ui.components.RecorderScreen.molecules.AudioRecordingStart +import app.myzel394.alibi.ui.components.RecorderScreen.molecules.VideoRecordingStart import app.myzel394.alibi.ui.effects.rememberForceUpdateOnLifeCycleChange import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.models.VideoRecorderModel diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index 69dc3b119..36e4b2523 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -37,8 +37,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import app.myzel394.alibi.ui.components.AudioRecorder.organisms.RecordingStatus -import app.myzel394.alibi.ui.components.AudioRecorder.organisms.StartRecording +import app.myzel394.alibi.ui.components.RecorderScreen.organisms.RecordingStatus +import app.myzel394.alibi.ui.components.RecorderScreen.organisms.StartRecording import app.myzel394.alibi.ui.enums.Screen import app.myzel394.alibi.ui.utils.rememberFileSaverDialog import app.myzel394.alibi.R From 7f757f46f36dcfba6eea7e9d7fc880067cbb412a Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 4 Dec 2023 08:52:54 +0100 Subject: [PATCH 043/176] feat: Add permission check to camera and audio --- .../VideoRecorderPreparationSheet.kt | 30 ++++++++++++++----- .../molecules/VideoRecordingStart.kt | 4 +-- .../ui/models/VideoRecorderSettingsModel.kt | 11 ++++++- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index 5826aa035..ea7954830 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -1,5 +1,6 @@ package app.myzel394.alibi.ui.components.RecorderScreen.molecules +import android.Manifest import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.gestures.awaitEachGesture @@ -16,6 +17,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt +import androidx.compose.material.icons.filled.Mic import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -23,6 +25,7 @@ import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier @@ -41,6 +44,7 @@ import app.myzel394.alibi.R import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE import app.myzel394.alibi.ui.components.RecorderScreen.atoms.CameraPreview import app.myzel394.alibi.ui.components.atoms.GlobalSwitch +import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.models.VideoRecorderSettingsModel import app.myzel394.alibi.ui.utils.CameraInfo @@ -61,6 +65,10 @@ fun VideoRecorderPreparationSheet( val context = LocalContext.current val cameras = CameraInfo.queryAvailableCameras(context) + LaunchedEffect(Unit) { + videoSettings.init(context) + } + if (showPreview) CameraPreview( modifier = Modifier @@ -95,13 +103,21 @@ fun VideoRecorderPreparationSheet( style = MaterialTheme.typography.labelLarge, ) } - GlobalSwitch( - label = stringResource(R.string.ui_videoRecorder_action_start_settings_enableAudio_label), - checked = videoSettings.enableAudio, - onCheckedChange = { - videoSettings.enableAudio = it - } - ) + PermissionRequester( + permission = Manifest.permission.RECORD_AUDIO, + icon = Icons.Default.Mic, + onPermissionAvailable = { + videoSettings.enableAudio = !videoSettings.enableAudio + }, + ) { trigger -> + GlobalSwitch( + label = stringResource(R.string.ui_videoRecorder_action_start_settings_enableAudio_label), + checked = videoSettings.enableAudio, + onCheckedChange = { + trigger() + } + ) + } Text( stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_selection_label), diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt index 897ae2757..b90e6579d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt @@ -58,8 +58,8 @@ fun VideoRecordingStart( } PermissionRequester( - permission = Manifest.permission.RECORD_AUDIO, - icon = Icons.Default.Mic, + permission = Manifest.permission.CAMERA, + icon = Icons.Default.CameraAlt, onPermissionAvailable = { showSheet = true }, diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt index fd376d505..d755c5b75 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt @@ -1,5 +1,8 @@ package app.myzel394.alibi.ui.models +import android.Manifest +import android.Manifest.permission.RECORD_AUDIO +import android.content.Context import android.graphics.Camera import android.hardware.camera2.CameraManager import androidx.camera.core.CameraSelector @@ -8,8 +11,14 @@ import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel +import app.myzel394.alibi.ui.utils.PermissionHelper class VideoRecorderSettingsModel : ViewModel() { var enableAudio by mutableStateOf(true) - var cameraID by mutableIntStateOf(0) + var cameraID by mutableIntStateOf(CameraSelector.LENS_FACING_BACK) + + fun init(context: Context) { + enableAudio = PermissionHelper.hasGranted(context, Manifest.permission.RECORD_AUDIO) + cameraID = CameraSelector.LENS_FACING_BACK + } } \ No newline at end of file From 8a3bfcae4d2d171faf10bc29151bc39d7b5196e4 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:27:28 +0100 Subject: [PATCH 044/176] fix: Improve camera-info.kt and CamerasSelection --- .../atoms/CameraSelectionButton.kt | 4 +- .../molecules/CamerasSelection.kt | 15 +- .../VideoRecorderPreparationSheet.kt | 168 +++++++++--------- .../ui/models/VideoRecorderSettingsModel.kt | 8 +- .../myzel394/alibi/ui/utils/camera-info.kt | 21 ++- app/src/main/res/values/strings.xml | 1 + 6 files changed, 117 insertions(+), 100 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/CameraSelectionButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/CameraSelectionButton.kt index 7a13c3c95..47e340b4d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/CameraSelectionButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/CameraSelectionButton.kt @@ -1,5 +1,4 @@ -package app.myzel394.alibi.ui.components.RecorderScreen.atoms - +import androidx.camera.core.ExperimentalLensFacing import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring @@ -28,6 +27,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import app.myzel394.alibi.ui.utils.CameraInfo + @Composable fun CameraSelectionButton( cameraID: CameraInfo.Lens, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt index 764498e71..d25bf00ac 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt @@ -1,10 +1,13 @@ package app.myzel394.alibi.ui.components.RecorderScreen.molecules +import CameraSelectionButton +import androidx.annotation.OptIn +import androidx.camera.core.CameraSelector +import androidx.camera.core.ExperimentalLensFacing import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import app.myzel394.alibi.R -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.CameraSelectionButton import app.myzel394.alibi.ui.models.VideoRecorderSettingsModel import app.myzel394.alibi.ui.utils.CameraInfo @@ -24,23 +27,23 @@ fun CamerasSelection( CameraSelectionButton( cameraID = CameraInfo.Lens.BACK, label = stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_back_label), - selected = videoSettings.cameraID == 0, + selected = videoSettings.cameraID == CameraInfo.Lens.BACK.androidValue, onSelected = { - videoSettings.cameraID = 0 + videoSettings.cameraID = CameraInfo.Lens.BACK.androidValue }, ) CameraSelectionButton( cameraID = CameraInfo.Lens.FRONT, label = stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_front_label), - selected = videoSettings.cameraID == 1, + selected = videoSettings.cameraID == CameraInfo.Lens.FRONT.androidValue, onSelected = { - videoSettings.cameraID = 1 + videoSettings.cameraID = CameraInfo.Lens.FRONT.androidValue }, ) } else { cameras.forEach { camera -> CameraSelectionButton( - cameraID = CameraInfo.CAMERA_INT_TO_LENS_MAP[camera.id]!!, + cameraID = camera.lens, selected = videoSettings.cameraID == camera.id, onSelected = { videoSettings.cameraID = camera.id diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index ea7954830..7dd9754e0 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -1,6 +1,7 @@ package app.myzel394.alibi.ui.components.RecorderScreen.molecules import android.Manifest +import androidx.camera.core.CameraSelector import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.gestures.awaitEachGesture @@ -10,6 +11,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -26,6 +28,10 @@ import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier @@ -45,12 +51,12 @@ import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE import app.myzel394.alibi.ui.components.RecorderScreen.atoms.CameraPreview import app.myzel394.alibi.ui.components.atoms.GlobalSwitch import app.myzel394.alibi.ui.components.atoms.PermissionRequester +import app.myzel394.alibi.ui.effects.rememberPrevious import app.myzel394.alibi.ui.models.VideoRecorderSettingsModel import app.myzel394.alibi.ui.utils.CameraInfo @OptIn( - ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class, - ExperimentalComposeUiApi::class + ExperimentalMaterial3Api::class, ) @Composable fun VideoRecorderPreparationSheet( @@ -69,19 +75,34 @@ fun VideoRecorderPreparationSheet( videoSettings.init(context) } - if (showPreview) - CameraPreview( + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = sheetState, + dragHandle = if (showPreview) { + null + } else null, + ) { + Box( modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - ) - else { - ModalBottomSheet( - onDismissRequest = onDismiss, - sheetState = sheetState, + .pointerInput(Unit) { + awaitEachGesture { + while (true) { + val event = awaitPointerEvent() + if (!event.changes.elementAt(0).pressed) { + onPreviewHidden() + break + } + } + } + } ) { - Box( - ) { + if (showPreview) { + CameraPreview( + modifier = Modifier + .fillMaxSize(), + cameraSelector = videoSettings.cameraSelector, + ) + } else Column( modifier = Modifier .padding(horizontal = 16.dp, vertical = 24.dp), @@ -119,80 +140,63 @@ fun VideoRecorderPreparationSheet( ) } - Text( - stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_selection_label), - style = MaterialTheme.typography.labelLarge, - textAlign = TextAlign.Start, - modifier = Modifier.fillMaxWidth() - ) - CamerasSelection( - cameras = cameras, - videoSettings = videoSettings, - ) - - Box( - modifier = Modifier - .fillMaxWidth() - .height(BIG_PRIMARY_BUTTON_SIZE) - ) - } - - } - } - } - - Popup( - alignment = Alignment.BottomCenter, - ) { - val label = - stringResource(R.string.ui_videoRecorder_action_start_settings_start_label) + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_selection_label), + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Start, + modifier = Modifier.fillMaxWidth() + ) + CamerasSelection( + cameras = cameras, + videoSettings = videoSettings, + ) + } - Box( - modifier = Modifier - .pointerInput(Unit) { - awaitEachGesture { - while (true) { - val event = awaitPointerEvent() - // consume all changes + val label = + stringResource(R.string.ui_videoRecorder_action_start_settings_start_label) - if (!event.changes.elementAt(0).pressed) { - onPreviewHidden() - break - } + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .height(BIG_PRIMARY_BUTTON_SIZE) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.primary) + .padding(16.dp) + .semantics { + contentDescription = label + } + .pointerInput(Unit) { + detectTapGestures( + onLongPress = { + onPreviewVisible() + } + ) + }, + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + label, + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onPrimary, + ) } + Text( + stringResource( + R.string.ui_videoRecorder_action_preview_label + ), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) } } - .let { - if (showPreview) it.alpha(0.2f) else it - } - ) { - Row( - modifier = Modifier - .padding(16.dp) - .fillMaxWidth() - .height(BIG_PRIMARY_BUTTON_SIZE) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primary) - .padding(16.dp) - .semantics { - contentDescription = label - } - .pointerInput(Unit) { - detectTapGestures( - onLongPress = { - onPreviewVisible() - } - ) - }, - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - label, - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onPrimary, - ) - } } } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt index d755c5b75..f2c2e2558 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt @@ -11,14 +11,18 @@ import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel +import app.myzel394.alibi.ui.utils.CameraInfo import app.myzel394.alibi.ui.utils.PermissionHelper class VideoRecorderSettingsModel : ViewModel() { var enableAudio by mutableStateOf(true) - var cameraID by mutableIntStateOf(CameraSelector.LENS_FACING_BACK) + var cameraID by mutableIntStateOf(CameraInfo.Lens.BACK.androidValue) + + val cameraSelector: CameraSelector + get() = CameraSelector.Builder().requireLensFacing(cameraID).build() fun init(context: Context) { enableAudio = PermissionHelper.hasGranted(context, Manifest.permission.RECORD_AUDIO) - cameraID = CameraSelector.LENS_FACING_BACK + cameraID = CameraInfo.Lens.BACK.androidValue } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt b/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt index 050c4674f..5e6be2fed 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt @@ -4,22 +4,28 @@ import android.content.Context import android.hardware.camera2.CameraCharacteristics import android.hardware.camera2.CameraManager import android.hardware.camera2.CameraMetadata +import androidx.annotation.OptIn +import androidx.camera.core.CameraSelector +import androidx.camera.core.ExperimentalLensFacing +@OptIn(ExperimentalLensFacing::class) data class CameraInfo( - val lens: Lens, val id: Int, ) { enum class Lens(val androidValue: Int) { - BACK(CameraMetadata.LENS_FACING_BACK), - FRONT(CameraMetadata.LENS_FACING_FRONT), - EXTERNAL(CameraMetadata.LENS_FACING_EXTERNAL), + BACK(CameraSelector.LENS_FACING_BACK), + FRONT(CameraSelector.LENS_FACING_FRONT), + EXTERNAL(CameraSelector.LENS_FACING_EXTERNAL), } + val lens: Lens + get() = CAMERA_INT_TO_LENS_MAP[id]!! + companion object { val CAMERA_INT_TO_LENS_MAP = mapOf( - 0 to Lens.BACK, - 1 to Lens.FRONT, - 2 to Lens.EXTERNAL, + CameraSelector.LENS_FACING_BACK to Lens.BACK, + CameraSelector.LENS_FACING_FRONT to Lens.FRONT, + CameraSelector.LENS_FACING_EXTERNAL to Lens.EXTERNAL, ) fun queryAvailableCameras(context: Context): List { @@ -36,7 +42,6 @@ data class CameraInfo( fun fromCameraId(cameraId: String, lensFacing: Int): CameraInfo { return CameraInfo( - lens = CAMERA_INT_TO_LENS_MAP[lensFacing]!!, id = cameraId.toInt(), ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c8e98dd00..475505b1a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -147,4 +147,5 @@ Start Recording Prepare your recording Select camera + Hold down on button to preview your camera \ No newline at end of file From 60f53f3649ad0552c25d1e0c1400e422b6c42da0 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 4 Dec 2023 16:23:59 +0100 Subject: [PATCH 045/176] current stand --- .../molecules/VideoRecorderPreparationSheet.kt | 17 ++++++++++++----- .../molecules/VideoRecordingStart.kt | 3 +++ .../myzel394/alibi/ui/screens/RecorderScreen.kt | 8 ++++---- app/src/main/res/values/strings.xml | 2 +- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index 7dd9754e0..2aff0f670 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material.icons.filled.Mic +import androidx.compose.material3.BottomSheetDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -65,8 +66,11 @@ fun VideoRecorderPreparationSheet( onPreviewVisible: () -> Unit, onPreviewHidden: () -> Unit, showPreview: Boolean, + onStartRecording: () -> Unit, ) { - val sheetState = rememberModalBottomSheetState(true) + val sheetState = rememberModalBottomSheetState(true) { sheetValue -> + false + } val context = LocalContext.current val cameras = CameraInfo.queryAvailableCameras(context) @@ -78,9 +82,12 @@ fun VideoRecorderPreparationSheet( ModalBottomSheet( onDismissRequest = onDismiss, sheetState = sheetState, - dragHandle = if (showPreview) { - null - } else null, + dragHandle = { + if (showPreview) + Unit + else + BottomSheetDefaults.DragHandle() + } ) { Box( modifier = Modifier @@ -176,7 +183,7 @@ fun VideoRecorderPreparationSheet( detectTapGestures( onLongPress = { onPreviewVisible() - } + }, ) }, horizontalArrangement = Arrangement.Center, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt index b90e6579d..c7473e0ac 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt @@ -54,6 +54,9 @@ fun VideoRecordingStart( onPreviewVisible = onHideAudioRecording, onPreviewHidden = onShowAudioRecording, showPreview = showPreview, + onStartRecording = { + videoRecorder.startRecording(context, appSettings) + }, ) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index 36e4b2523..9e97cc51b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -82,7 +82,7 @@ fun RecorderScreen( } } - var isProcessingAudio by remember { mutableStateOf(false) } + var isProcessing by remember { mutableStateOf(false) } var showRecorderError by remember { mutableStateOf(false) } fun saveAsLastRecording() { @@ -122,7 +122,7 @@ fun RecorderScreen( fun saveRecording() { scope.launch { - isProcessingAudio = true + isProcessing = true // Give the user some time to see the processing dialog delay(100) @@ -166,7 +166,7 @@ fun RecorderScreen( } catch (error: Exception) { Log.getStackTraceString(error) } finally { - isProcessingAudio = false + isProcessing = false } } } @@ -190,7 +190,7 @@ fun RecorderScreen( } } - if (isProcessingAudio) + if (isProcessing) AlertDialog( onDismissRequest = { }, icon = { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 475505b1a..408d8105f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,7 +28,7 @@ Save Recording Alibi will continue recording in the background and store the last %s minutes at your request Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing your recording, do not close Alibi! You will automatically be prompted to save the file once it\'s ready Recording Audio Alibi keeps recording in the background From add9c8cde53d03e26c34407b5318f482b9ece636 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 4 Dec 2023 18:56:29 +0100 Subject: [PATCH 046/176] feat: Add cameraID and enableAudio pass from UI to VideoRecorderService --- .../alibi/services/VideoRecorderService.kt | 25 ++++++++++++-- .../molecules/CamerasSelection.kt | 4 +-- .../VideoRecorderPreparationSheet.kt | 14 +++++--- .../molecules/VideoRecordingStart.kt | 5 +-- .../alibi/ui/models/BaseRecorderModel.kt | 9 +++-- .../alibi/ui/models/VideoRecorderModel.kt | 33 +++++++++++++++++++ .../ui/models/VideoRecorderSettingsModel.kt | 28 ---------------- 7 files changed, 78 insertions(+), 40 deletions(-) delete mode 100644 app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 67aa8019d..13cfe164a 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -1,8 +1,10 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint +import android.content.Intent import android.util.Range import androidx.camera.core.Camera +import androidx.camera.core.CameraControl import androidx.camera.core.CameraSelector import androidx.camera.core.TorchState import androidx.camera.lifecycle.ProcessCameraProvider @@ -27,6 +29,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull +import kotlin.properties.Delegates const val CAMERA_CLOSE_TIMEOUT = 20000L @@ -49,11 +52,23 @@ class VideoRecorderService : // Absolute last completer that can be awaited to ensure that the camera is closed private var _cameraCloserListener = CompletableDeferred() - private var selectedCamera: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA + private lateinit var selectedCamera: CameraSelector + private var enableAudio by Delegates.notNull() var cameraControl: CameraControl? = null private set + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + if (intent?.action == "init") { + selectedCamera = CameraSelector.Builder().requireLensFacing( + intent.getIntExtra("cameraID", CameraSelector.LENS_FACING_BACK) + ).build() + enableAudio = intent.getBooleanExtra("enableAudio", true) + } + + return super.onStartCommand(intent, flags, startId) + } + override fun start() { super.start() @@ -189,7 +204,13 @@ class VideoRecorderService : private fun prepareVideoRecording() = videoCapture!!.output .prepareRecording(this, settings.getOutputOptions(this)) - .withAudioEnabled() + .run { + if (enableAudio) { + return@run withAudioEnabled() + } + + this + } override fun getRecordingInformation(): RecordingInformation = RecordingInformation( folderPath = batchesFolder.exportFolderForSettings(), diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt index d25bf00ac..b7ad8a0d0 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt @@ -8,13 +8,13 @@ import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import app.myzel394.alibi.R -import app.myzel394.alibi.ui.models.VideoRecorderSettingsModel +import app.myzel394.alibi.ui.models.VideoRecorderModel import app.myzel394.alibi.ui.utils.CameraInfo @Composable fun CamerasSelection( cameras: Iterable, - videoSettings: VideoRecorderSettingsModel + videoSettings: VideoRecorderModel, ) { val CAMERA_LENS_TEXT_MAP = mapOf( CameraInfo.Lens.BACK to stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_back_label), diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index 2aff0f670..b3bd5b38c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -38,7 +38,9 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip +import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.input.pointer.positionChange import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription @@ -53,19 +55,20 @@ import app.myzel394.alibi.ui.components.RecorderScreen.atoms.CameraPreview import app.myzel394.alibi.ui.components.atoms.GlobalSwitch import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.effects.rememberPrevious -import app.myzel394.alibi.ui.models.VideoRecorderSettingsModel +import app.myzel394.alibi.ui.models.VideoRecorderModel import app.myzel394.alibi.ui.utils.CameraInfo +import kotlin.math.abs @OptIn( ExperimentalMaterial3Api::class, ) @Composable fun VideoRecorderPreparationSheet( + showPreview: Boolean, + videoSettings: VideoRecorderModel, onDismiss: () -> Unit, - videoSettings: VideoRecorderSettingsModel = viewModel(), onPreviewVisible: () -> Unit, onPreviewHidden: () -> Unit, - showPreview: Boolean, onStartRecording: () -> Unit, ) { val sheetState = rememberModalBottomSheetState(true) { sheetValue -> @@ -87,7 +90,7 @@ fun VideoRecorderPreparationSheet( Unit else BottomSheetDefaults.DragHandle() - } + }, ) { Box( modifier = Modifier @@ -184,6 +187,9 @@ fun VideoRecorderPreparationSheet( onLongPress = { onPreviewVisible() }, + onTap = { + onStartRecording() + } ) }, horizontalArrangement = Arrangement.Center, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt index c7473e0ac..2e4bd5048 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt -import androidx.compose.material.icons.filled.Mic import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon @@ -27,6 +26,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.components.atoms.PermissionRequester @@ -48,12 +48,13 @@ fun VideoRecordingStart( if (showSheet) { VideoRecorderPreparationSheet( + showPreview = showPreview, + videoSettings = videoRecorder, onDismiss = { showSheet = false }, onPreviewVisible = onHideAudioRecording, onPreviewHidden = onShowAudioRecording, - showPreview = showPreview, onStartRecording = { videoRecorder.startRecording(context, appSettings) }, diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 9152a30fb..5338526c9 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -90,8 +90,13 @@ abstract class BaseRecorderModel() { override var batchesFolder: VideoBatchesFolder? = null override val intentClass = VideoRecorderService::class.java + var enableAudio by mutableStateOf(true) + var cameraID by mutableIntStateOf(CameraInfo.Lens.BACK.androidValue) + + val cameraSelector: CameraSelector + get() = CameraSelector.Builder().requireLensFacing(cameraID).build() + + fun init(context: Context) { + enableAudio = PermissionHelper.hasGranted(context, Manifest.permission.RECORD_AUDIO) + cameraID = CameraInfo.Lens.BACK.androidValue + } + override fun onServiceConnected(service: VideoRecorderService) { service.settings = VideoRecorderService.Settings.from(settings) @@ -18,4 +40,15 @@ class VideoRecorderModel : recorderState = service.state recordingTime = service.recordingTime } + + override fun handleIntent(intent: Intent) = + intent.apply { + putExtra("cameraID", cameraID) + putExtra("enableAudio", enableAudio) + } + + + override fun startRecording(context: Context, settings: AppSettings) { + super.startRecording(context, settings) + } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt deleted file mode 100644 index f2c2e2558..000000000 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderSettingsModel.kt +++ /dev/null @@ -1,28 +0,0 @@ -package app.myzel394.alibi.ui.models - -import android.Manifest -import android.Manifest.permission.RECORD_AUDIO -import android.content.Context -import android.graphics.Camera -import android.hardware.camera2.CameraManager -import androidx.camera.core.CameraSelector -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel -import app.myzel394.alibi.ui.utils.CameraInfo -import app.myzel394.alibi.ui.utils.PermissionHelper - -class VideoRecorderSettingsModel : ViewModel() { - var enableAudio by mutableStateOf(true) - var cameraID by mutableIntStateOf(CameraInfo.Lens.BACK.androidValue) - - val cameraSelector: CameraSelector - get() = CameraSelector.Builder().requireLensFacing(cameraID).build() - - fun init(context: Context) { - enableAudio = PermissionHelper.hasGranted(context, Manifest.permission.RECORD_AUDIO) - cameraID = CameraInfo.Lens.BACK.androidValue - } -} \ No newline at end of file From 55e00c132a9abe08352b0a88bdcf4d0e3c34a167 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:32:02 +0100 Subject: [PATCH 047/176] fix: Allow sheet to be closed --- .../RecorderScreen/molecules/VideoRecorderPreparationSheet.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index b3bd5b38c..3ad5961ee 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -71,9 +71,7 @@ fun VideoRecorderPreparationSheet( onPreviewHidden: () -> Unit, onStartRecording: () -> Unit, ) { - val sheetState = rememberModalBottomSheetState(true) { sheetValue -> - false - } + val sheetState = rememberModalBottomSheetState(true) val context = LocalContext.current val cameras = CameraInfo.queryAvailableCameras(context) From 699acb5311112ce0d0c7a8eff9932ba594b7c4ee Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Tue, 5 Dec 2023 18:39:05 +0100 Subject: [PATCH 048/176] fix: Destroy recording after stopping it --- .../java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 5338526c9..9ba161370 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -133,6 +133,11 @@ abstract class BaseRecorderModel Date: Tue, 5 Dec 2023 18:40:33 +0100 Subject: [PATCH 049/176] fix: Uncomment stopRecording on AudioRecorderService --- .../organisms/RecordingStatus.kt | 18 +++++++++++++----- .../alibi/ui/screens/RecorderScreen.kt | 1 - 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecordingStatus.kt index dd973a7bf..639c4d25b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecordingStatus.kt @@ -18,6 +18,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -32,6 +33,7 @@ import app.myzel394.alibi.ui.components.RecorderScreen.molecules.MicrophoneStatu import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.utils.KeepScreenOn import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import java.time.LocalDateTime @Composable @@ -40,6 +42,8 @@ fun RecordingStatus( ) { val context = LocalContext.current + val scope = rememberCoroutineScope() + var now by remember { mutableStateOf(LocalDateTime.now()) } LaunchedEffect(Unit) { @@ -97,8 +101,10 @@ fun RecordingStatus( ) { DeleteButton( onDelete = { - //audioRecorder.stopRecording(context) - audioRecorder.batchesFolder!!.deleteRecordings(); + scope.launch { + audioRecorder.stopRecording(context) + audioRecorder.batchesFolder!!.deleteRecordings() + } } ) } @@ -126,10 +132,12 @@ fun RecordingStatus( ) { SaveButton( onSave = { - runCatching { - //audioRecorder.stopRecording(context) + scope.launch { + runCatching { + audioRecorder.stopRecording(context) + } + audioRecorder.onRecordingSave() } - audioRecorder.onRecordingSave() } ) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index 9e97cc51b..db7833aa4 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -180,7 +180,6 @@ fun RecorderScreen( audioRecorder.onError = { saveAsLastRecording() - //audioRecorder.stopRecording(context) showRecorderError = true } From e116c192f6eee8880cade4b788bab46fab521c01 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Tue, 5 Dec 2023 19:01:17 +0100 Subject: [PATCH 050/176] fix: Call onAmplitudeChange on AudioRecorderModel when amplitude changes --- .../java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index 1306dbe1a..6b9bebf53 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -42,6 +42,10 @@ class AudioRecorderModel : service.onMicrophoneReconnected = { microphoneStatus = MicrophoneConnectivityStatus.CONNECTED } + service.onAmplitudeChange = { amps -> + amplitudes = amps + onAmplitudeChange() + } service.settings = AudioRecorderService.Settings.from(settings) From 453ee79b1a27d8354dfde4c48a7e6e0f4eac6b46 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:28:28 +0100 Subject: [PATCH 051/176] fix(ui): Move information down --- .../organisms/StartRecording.kt | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt index e6f7f5046..a1980fcfb 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt @@ -1,6 +1,7 @@ package app.myzel394.alibi.ui.components.RecorderScreen.organisms import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -53,7 +54,9 @@ fun StartRecording( val context = LocalContext.current Column( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .padding(bottom = 32.dp), verticalArrangement = Arrangement.SpaceBetween, horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -72,29 +75,14 @@ fun StartRecording( showPreview = !showAudioRecorder, ) - Text( - stringResource( - R.string.ui_audioRecorder_action_start_description, - appSettings.maxDuration / 1000 / 60 - ), - style = MaterialTheme.typography.bodySmall.copy( - color = MaterialTheme.colorScheme.onSurfaceVariant, - ), - modifier = Modifier - .widthIn(max = 300.dp) - .fillMaxWidth(), - textAlign = TextAlign.Center, - ) - val forceUpdate = rememberForceUpdateOnLifeCycleChange() - if (appSettings.lastRecording?.hasRecordingsAvailable(context) == true) { - Column( - modifier = Modifier - .weight(1f) - .then(forceUpdate), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Bottom, - ) { + Column( + modifier = Modifier + .weight(1f) + .then(forceUpdate), + verticalArrangement = Arrangement.Bottom, + ) { + if (appSettings.lastRecording?.hasRecordingsAvailable(context) == true) { val label = stringResource( R.string.ui_audioRecorder_action_saveOldRecording_label, DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL) @@ -119,8 +107,21 @@ fun StartRecording( Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Text(label) } + } else { + Text( + stringResource( + R.string.ui_audioRecorder_action_start_description, + appSettings.maxDuration / 1000 / 60 + ), + style = MaterialTheme.typography.bodySmall.copy( + color = MaterialTheme.colorScheme.onSurfaceVariant, + ), + modifier = Modifier + .widthIn(max = 300.dp) + .fillMaxWidth(), + textAlign = TextAlign.Center, + ) } - } else - Spacer(modifier = Modifier.weight(1f)) + } } } \ No newline at end of file From 536cba87807d7bb2a8d32ea5f261e046708b6a2d Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:29:59 +0100 Subject: [PATCH 052/176] refactor: Rename RecordingStatus.kt -> AudioRecordingStatus.kt --- .../organisms/{RecordingStatus.kt => AudioRecordingStatus.kt} | 2 +- .../main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/{RecordingStatus.kt => AudioRecordingStatus.kt} (99%) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt similarity index 99% rename from app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecordingStatus.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index 639c4d25b..d88539fd0 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -37,7 +37,7 @@ import kotlinx.coroutines.launch import java.time.LocalDateTime @Composable -fun RecordingStatus( +fun AudioRecordingStatus( audioRecorder: AudioRecorderModel, ) { val context = LocalContext.current diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index db7833aa4..b51d8742f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -37,7 +37,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import app.myzel394.alibi.ui.components.RecorderScreen.organisms.RecordingStatus +import app.myzel394.alibi.ui.components.RecorderScreen.organisms.AudioRecordingStatus import app.myzel394.alibi.ui.components.RecorderScreen.organisms.StartRecording import app.myzel394.alibi.ui.enums.Screen import app.myzel394.alibi.ui.utils.rememberFileSaverDialog @@ -306,7 +306,7 @@ fun RecorderScreen( context.dataStore.data.collectAsState(AppSettings.getDefaultInstance()).value if (audioRecorder.isInRecording) - RecordingStatus(audioRecorder = audioRecorder) + AudioRecordingStatus(audioRecorder = audioRecorder) else StartRecording( audioRecorder = audioRecorder, From 515c43deb5f5eb3f417b5648457677c772812945 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:20:02 +0100 Subject: [PATCH 053/176] feat: Add TorchStatus --- .../alibi/services/VideoRecorderService.kt | 2 + .../RecorderScreen/atoms/TorchStatus.kt | 39 +++++++ .../organisms/VideoRecordingStatus.kt | 109 ++++++++++++++++++ .../alibi/ui/models/BaseRecorderModel.kt | 4 +- .../alibi/ui/models/VideoRecorderModel.kt | 4 + .../alibi/ui/screens/RecorderScreen.kt | 3 + app/src/main/res/values/strings.xml | 2 + 7 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 13cfe164a..7fdd6d86a 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -274,5 +274,7 @@ class VideoRecorderService : fun isTorchEnabled(): Boolean { return camera.cameraInfo.torchState.value == TorchState.ON } + + fun hasTorchAvailable() = camera.cameraInfo.hasFlashUnit() } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt new file mode 100644 index 000000000..099b5f115 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt @@ -0,0 +1,39 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.atoms + +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.FlashlightOff +import androidx.compose.material.icons.filled.FlashlightOn +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.myzel394.alibi.R + +@Composable +fun TorchStatus( + enabled: Boolean, + onChange: () -> Unit, +) { + Button( + onClick = onChange, + colors = if (enabled) ButtonDefaults.filledTonalButtonColors() else ButtonDefaults.outlinedButtonColors(), + ) { + Icon( + if (enabled) Icons.Default.FlashlightOff else Icons.Default.FlashlightOn, + contentDescription = null, + modifier = Modifier.size(ButtonDefaults.IconSize) + ) + Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) + Text( + if (enabled) stringResource(R.string.ui_videoRecorder_action_torch_off) + else stringResource(R.string.ui_videoRecorder_action_torch_on), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt new file mode 100644 index 000000000..ebb76e5d1 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -0,0 +1,109 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.organisms + +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 +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.DeleteButton +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.PauseResumeButton +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveButton +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.TorchStatus +import app.myzel394.alibi.ui.models.VideoRecorderModel +import kotlinx.coroutines.launch + +@Composable +fun VideoRecordingStatus( + videoRecorder: VideoRecorderModel, +) { + val context = LocalContext.current + val scope = rememberCoroutineScope() + + Column( + modifier = Modifier + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween, + ) { + Box {} + + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + contentAlignment = Alignment.Center, + ) { + DeleteButton( + onDelete = { + scope.launch { + videoRecorder.stopRecording(context) + videoRecorder.batchesFolder!!.deleteRecordings() + } + } + ) + } + + Box( + contentAlignment = Alignment.Center, + ) { + PauseResumeButton( + isPaused = videoRecorder.isPaused, + onChange = { + if (videoRecorder.isPaused) { + videoRecorder.resumeRecording() + } else { + videoRecorder.pauseRecording() + } + }, + ) + } + + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + contentAlignment = Alignment.Center, + ) { + SaveButton( + onSave = { + scope.launch { + runCatching { + videoRecorder.stopRecording(context) + } + videoRecorder.onRecordingSave() + } + } + ) + } + } + + val cameraControl = videoRecorder.recorderService!!.cameraControl!! + println("cameraControl.hasTorchAvailable(): ${cameraControl.hasTorchAvailable()}") + println("videoRecorder: ${videoRecorder.recorderService?.cameraControl}") + if (cameraControl.hasTorchAvailable()) { + val isTorchEnabled = cameraControl.isTorchEnabled() + + TorchStatus( + enabled = isTorchEnabled, + onChange = { + if (isTorchEnabled) { + cameraControl.disableTorch() + } else { + cameraControl.enableTorch() + } + }, + ) + } + + Box {} + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 9ba161370..01cfad1d1 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -31,8 +31,8 @@ abstract class BaseRecorderModel(null) protected set - val isInRecording: Boolean - get() = recorderState !== RecorderState.IDLE && recordingTime != null + open val isInRecording: Boolean + get() = recorderState !== RecorderState.IDLE && recordingTime != null && recorderService != null val isPaused: Boolean get() = recorderState === RecorderState.PAUSED diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt index 4f8734b94..7066bba89 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation +import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.VideoBatchesFolder import app.myzel394.alibi.services.VideoRecorderService import app.myzel394.alibi.ui.utils.CameraInfo @@ -23,6 +24,9 @@ class VideoRecorderModel : var enableAudio by mutableStateOf(true) var cameraID by mutableIntStateOf(CameraInfo.Lens.BACK.androidValue) + override val isInRecording: Boolean + get() = super.isInRecording && recorderService!!.cameraControl != null + val cameraSelector: CameraSelector get() = CameraSelector.Builder().requireLensFacing(cameraID).build() diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index b51d8742f..e901428b9 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -46,6 +46,7 @@ import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.BatchesFolder +import app.myzel394.alibi.ui.components.RecorderScreen.organisms.VideoRecordingStatus import app.myzel394.alibi.ui.effects.rememberSettings import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.models.VideoRecorderModel @@ -307,6 +308,8 @@ fun RecorderScreen( if (audioRecorder.isInRecording) AudioRecordingStatus(audioRecorder = audioRecorder) + else if (videoRecorder.isInRecording) + VideoRecordingStatus(videoRecorder = videoRecorder) else StartRecording( audioRecorder = audioRecorder, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 408d8105f..21e8fb979 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -148,4 +148,6 @@ Prepare your recording Select camera Hold down on button to preview your camera + Disable Torch + Enable Torch \ No newline at end of file From 0192af584ca910de06fdf75dd9b89d8240b0f61b Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:45:53 +0100 Subject: [PATCH 054/176] refactor: Improve RecordingStatuses --- .../RecorderScreen/atoms/RecordingProgress.kt | 42 ++++++ .../RecorderScreen/atoms/TorchStatus.kt | 1 - .../molecules/RecordingControl.kt | 51 +++++++ .../molecules/RecordingStatus.kt | 28 ++++ .../organisms/AudioRecordingStatus.kt | 105 ++++---------- .../organisms/VideoRecordingStatus.kt | 131 ++++++++++-------- 6 files changed, 224 insertions(+), 134 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt new file mode 100644 index 000000000..dcf659a87 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt @@ -0,0 +1,42 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.atoms + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween +import androidx.compose.animation.expandHorizontally +import androidx.compose.foundation.layout.width +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun RecordingProgress( + recordingTime: Long, + progress: Float, +) { + // Only show animation when the recording has just started + val recordingJustStarted = recordingTime <= 1000L + var progressVisible by remember { mutableStateOf(!recordingJustStarted) } + + LaunchedEffect(Unit) { + progressVisible = true + } + + AnimatedVisibility( + visible = progressVisible, + enter = expandHorizontally( + tween(1000) + ) + ) { + LinearProgressIndicator( + progress = progress, + modifier = Modifier + .width(300.dp) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt index 099b5f115..cc240469f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt @@ -13,7 +13,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import app.myzel394.alibi.R @Composable diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt new file mode 100644 index 000000000..e6d3ac20a --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt @@ -0,0 +1,51 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.molecules + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.DeleteButton +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.PauseResumeButton +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveButton +import kotlinx.coroutines.launch + +@Composable +fun RecordingControl( + isPaused: Boolean, + onDelete: () -> Unit, + onPauseResume: () -> Unit, + onSave: () -> Unit, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + contentAlignment = Alignment.Center, + ) { + DeleteButton(onDelete = onDelete) + } + + Box( + contentAlignment = Alignment.Center, + ) { + PauseResumeButton( + isPaused = isPaused, + onChange = onPauseResume, + ) + } + + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + contentAlignment = Alignment.Center, + ) { + SaveButton(onSave = onSave) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt new file mode 100644 index 000000000..8cc73427c --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt @@ -0,0 +1,28 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.molecules + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingProgress +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingTime + +@Composable +fun RecordingStatus( + recordingTime: Long, + progress: Float, +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + RecordingTime(recordingTime) + Spacer(modifier = Modifier.height(16.dp)) + RecordingProgress( + recordingTime = recordingTime, + progress = progress, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index d88539fd0..5956c1b1e 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -27,9 +27,12 @@ import androidx.compose.ui.unit.dp import app.myzel394.alibi.ui.components.RecorderScreen.atoms.DeleteButton import app.myzel394.alibi.ui.components.RecorderScreen.atoms.PauseResumeButton import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RealtimeAudioVisualizer +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingProgress import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingTime import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveButton import app.myzel394.alibi.ui.components.RecorderScreen.molecules.MicrophoneStatus +import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingControl +import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingStatus import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.utils.KeepScreenOn import kotlinx.coroutines.delay @@ -53,15 +56,7 @@ fun AudioRecordingStatus( } } - // Only show animation when the recording has just started - val recordingJustStarted = audioRecorder.recordingTime!! <= 1000L - var progressVisible by remember { mutableStateOf(!recordingJustStarted) } - LaunchedEffect(Unit) { - progressVisible = true - } - KeepScreenOn() - Column( modifier = Modifier .fillMaxSize(), @@ -70,78 +65,36 @@ fun AudioRecordingStatus( ) { Box {} RealtimeAudioVisualizer(audioRecorder = audioRecorder) - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - RecordingTime(audioRecorder.recordingTime!!) - Spacer(modifier = Modifier.height(16.dp)) - AnimatedVisibility( - visible = progressVisible, - enter = expandHorizontally( - tween(1000) - ) - ) { - LinearProgressIndicator( - progress = audioRecorder.progress, - modifier = Modifier - .width(300.dp) - ) - } - Spacer(modifier = Modifier.height(32.dp)) - } - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - Box( - modifier = Modifier - .fillMaxWidth() - .weight(1f), - contentAlignment = Alignment.Center, - ) { - DeleteButton( - onDelete = { - scope.launch { - audioRecorder.stopRecording(context) - audioRecorder.batchesFolder!!.deleteRecordings() - } - } - ) - } + RecordingStatus( + recordingTime = audioRecorder.recordingTime!!, + progress = audioRecorder.progress, + ) - Box( - contentAlignment = Alignment.Center, - ) { - PauseResumeButton( - isPaused = audioRecorder.isPaused, - onChange = { - if (audioRecorder.isPaused) { - audioRecorder.resumeRecording() - } else { - audioRecorder.pauseRecording() - } - }, - ) - } - - Box( - modifier = Modifier - .fillMaxWidth() - .weight(1f), - contentAlignment = Alignment.Center, - ) { - SaveButton( - onSave = { - scope.launch { - runCatching { - audioRecorder.stopRecording(context) - } - audioRecorder.onRecordingSave() - } + RecordingControl( + isPaused = audioRecorder.isPaused, + onDelete = { + scope.launch { + audioRecorder.stopRecording(context) + audioRecorder.batchesFolder!!.deleteRecordings() + } + }, + onPauseResume = { + if (audioRecorder.isPaused) { + audioRecorder.resumeRecording() + } else { + audioRecorder.pauseRecording() + } + }, + onSave = { + scope.launch { + runCatching { + audioRecorder.stopRecording(context) } - ) + audioRecorder.onRecordingSave() + } } - } + ) MicrophoneStatus(audioRecorder) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index ebb76e5d1..016c2eafd 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -1,21 +1,45 @@ package app.myzel394.alibi.ui.components.RecorderScreen.organisms +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween +import androidx.compose.animation.expandHorizontally 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.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.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CameraAlt +import androidx.compose.material3.Divider +import androidx.compose.material3.Icon +import androidx.compose.material3.LinearProgressIndicator import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp import app.myzel394.alibi.ui.components.RecorderScreen.atoms.DeleteButton import app.myzel394.alibi.ui.components.RecorderScreen.atoms.PauseResumeButton +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingProgress +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingTime import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveButton import app.myzel394.alibi.ui.components.RecorderScreen.atoms.TorchStatus +import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingControl +import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingStatus import app.myzel394.alibi.ui.models.VideoRecorderModel +import app.myzel394.alibi.ui.utils.KeepScreenOn import kotlinx.coroutines.launch @Composable @@ -25,6 +49,7 @@ fun VideoRecordingStatus( val context = LocalContext.current val scope = rememberCoroutineScope() + KeepScreenOn() Column( modifier = Modifier .fillMaxSize(), @@ -33,77 +58,69 @@ fun VideoRecordingStatus( ) { Box {} - Row( - verticalAlignment = Alignment.CenterVertically, + Column { + Icon( + Icons.Default.CameraAlt, + contentDescription = null, + modifier = Modifier.size(64.dp) + ) + } + + RecordingStatus( + recordingTime = videoRecorder.recordingTime!!, + progress = videoRecorder.progress, + ) + + Column( + verticalArrangement = Arrangement + .spacedBy(32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(bottom = 32.dp), ) { - Box( - modifier = Modifier - .fillMaxWidth() - .weight(1f), - contentAlignment = Alignment.Center, - ) { - DeleteButton( - onDelete = { - scope.launch { - videoRecorder.stopRecording(context) - videoRecorder.batchesFolder!!.deleteRecordings() - } - } - ) - } + val cameraControl = videoRecorder.recorderService!!.cameraControl!! + println("cameraControl.hasTorchAvailable(): ${cameraControl.hasTorchAvailable()}") + println("videoRecorder: ${videoRecorder.recorderService?.cameraControl}") + if (cameraControl.hasTorchAvailable()) { + val isTorchEnabled = cameraControl.isTorchEnabled() - Box( - contentAlignment = Alignment.Center, - ) { - PauseResumeButton( - isPaused = videoRecorder.isPaused, + TorchStatus( + enabled = isTorchEnabled, onChange = { - if (videoRecorder.isPaused) { - videoRecorder.resumeRecording() + if (isTorchEnabled) { + cameraControl.disableTorch() } else { - videoRecorder.pauseRecording() + cameraControl.enableTorch() } }, ) } - Box( - modifier = Modifier - .fillMaxWidth() - .weight(1f), - contentAlignment = Alignment.Center, - ) { - SaveButton( - onSave = { - scope.launch { - runCatching { - videoRecorder.stopRecording(context) - } - videoRecorder.onRecordingSave() - } - } - ) - } - } - - val cameraControl = videoRecorder.recorderService!!.cameraControl!! - println("cameraControl.hasTorchAvailable(): ${cameraControl.hasTorchAvailable()}") - println("videoRecorder: ${videoRecorder.recorderService?.cameraControl}") - if (cameraControl.hasTorchAvailable()) { - val isTorchEnabled = cameraControl.isTorchEnabled() + Divider() - TorchStatus( - enabled = isTorchEnabled, - onChange = { - if (isTorchEnabled) { - cameraControl.disableTorch() + RecordingControl( + isPaused = videoRecorder.isPaused, + onDelete = { + scope.launch { + videoRecorder.stopRecording(context) + videoRecorder.batchesFolder!!.deleteRecordings() + } + }, + onPauseResume = { + if (videoRecorder.isPaused) { + videoRecorder.resumeRecording() } else { - cameraControl.enableTorch() + videoRecorder.pauseRecording() } }, + onSave = { + scope.launch { + runCatching { + videoRecorder.stopRecording(context) + } + videoRecorder.onRecordingSave() + } + } ) } - - Box {} } } \ No newline at end of file From ce50ed1d68c0debe9d6ad1db7ad10115e7b3042a Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:52:14 +0100 Subject: [PATCH 055/176] feat(ui): Add more space to SectionTitle.kt --- .../ui/components/SettingsScreen/Tiles/SectionTitle.kt | 8 +++++++- .../java/app/myzel394/alibi/ui/screens/SettingsScreen.kt | 2 -- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SectionTitle.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SectionTitle.kt index 7880076cd..b490c87a1 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SectionTitle.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SectionTitle.kt @@ -4,7 +4,11 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Mic import androidx.compose.material3.Divider +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -14,7 +18,9 @@ import androidx.compose.ui.unit.dp @Composable fun DividerTitle( - modifier: Modifier = Modifier, + modifier: Modifier = Modifier + .fillMaxWidth() + .padding(vertical = 32.dp), title: String, description: String, ) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index 61afec25a..471ee2f0f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -158,8 +158,6 @@ fun SettingsScreen( SaveFolderTile(settings = settings) DividerTitle( - modifier = Modifier - .fillMaxWidth(), title = stringResource(R.string.ui_settings_sections_audio_title), description = stringResource(R.string.ui_settings_sections_audio_description), ) From 3cba3382f3f5c62ba5a5767da7fc0d80cac89c2b Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 6 Dec 2023 21:57:04 +0100 Subject: [PATCH 056/176] refactor: Improving Recorder events handling --- .../alibi/services/VideoRecorderService.kt | 4 +- .../atoms/RecorderErrorDialog.kt | 50 ++++ .../atoms/RecorderEventsHandler.kt | 213 +++++++++++++++++ .../atoms/RecorderProcessingDialog.kt | 47 ++++ .../organisms/AudioRecordingStatus.kt | 8 +- .../organisms/VideoRecordingStatus.kt | 10 +- .../alibi/ui/models/VideoRecorderModel.kt | 5 - .../alibi/ui/screens/RecorderScreen.kt | 220 +----------------- 8 files changed, 328 insertions(+), 229 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 7fdd6d86a..539b44725 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -253,9 +253,7 @@ class VideoRecorderService : targetVideoBitRate = appSettings.videoRecorderSettings.targetedVideoBitRate, targetFrameRate = appSettings.videoRecorderSettings.targetFrameRate, quality = appSettings.videoRecorderSettings.getQualitySelector() - ?: QualitySelector.from( - Quality.HIGHEST - ), + ?: QualitySelector.from(Quality.HIGHEST), ) } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt new file mode 100644 index 000000000..374dda86f --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt @@ -0,0 +1,50 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.atoms + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import app.myzel394.alibi.R + +@Composable +fun RecorderErrorDialog( + onClose: () -> Unit, + onSave: () -> Unit, +) { + AlertDialog( + onDismissRequest = onClose, + icon = { + Icon( + Icons.Default.Warning, + contentDescription = null, + ) + }, + title = { + Text(stringResource(R.string.ui_audioRecorder_error_recording_title)) + }, + text = { + Text(stringResource(R.string.ui_audioRecorder_error_recording_description)) + }, + dismissButton = { + Button( + onClick = onClose, + colors = ButtonDefaults.textButtonColors(), + ) { + Text(stringResource(R.string.dialog_close_cancel_label)) + } + }, + confirmButton = { + Button( + onClick = onSave, + colors = ButtonDefaults.textButtonColors(), + ) { + Text(stringResource(R.string.ui_audioRecorder_action_save_label)) + } + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt new file mode 100644 index 000000000..7a95e6b28 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt @@ -0,0 +1,213 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.atoms + +import android.content.Intent +import android.net.Uri +import android.util.Log +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.SnackbarResult +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import app.myzel394.alibi.R +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.db.RecordingInformation +import app.myzel394.alibi.helpers.AudioBatchesFolder +import app.myzel394.alibi.helpers.BatchesFolder +import app.myzel394.alibi.services.IntervalRecorderService +import app.myzel394.alibi.ui.models.AudioRecorderModel +import app.myzel394.alibi.ui.models.BaseRecorderModel +import app.myzel394.alibi.ui.models.VideoRecorderModel +import app.myzel394.alibi.ui.utils.rememberFileSaverDialog +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +typealias RecorderModel = BaseRecorderModel< + IntervalRecorderService.Settings, + RecordingInformation, + IntervalRecorderService, + BatchesFolder? + > + +@Composable +fun RecorderEventsHandler( + settings: AppSettings, + snackbarHostState: SnackbarHostState, + audioRecorder: AudioRecorderModel, + videoRecorder: VideoRecorderModel, +) { + val scope = rememberCoroutineScope() + val context = LocalContext.current + val dataStore = context.dataStore + + var isProcessing by remember { mutableStateOf(false) } + var showRecorderError by remember { mutableStateOf(false) } + + val saveFile = rememberFileSaverDialog( + settings.audioRecorderSettings.getMimeType() + ) { + if (settings.deleteRecordingsImmediately) { + audioRecorder.batchesFolder!!.deleteRecordings() + videoRecorder.batchesFolder!!.deleteRecordings() + } + + if (!audioRecorder.batchesFolder!!.hasRecordingsAvailable() + || !videoRecorder.batchesFolder!!.hasRecordingsAvailable() + ) { + scope.launch { + dataStore.updateData { + it.setLastRecording(null) + } + } + } + } + + fun saveAsLastRecording( + recorder: RecorderModel + ) { + if (!settings.deleteRecordingsImmediately) { + scope.launch { + dataStore.updateData { + it.setLastRecording( + recorder.recorderService!!.getRecordingInformation() + ) + } + } + } + } + + val successMessage = stringResource(R.string.ui_audioRecorder_action_save_success) + val openMessage = stringResource(R.string.ui_audioRecorder_action_save_openFolder) + + fun openFolder(uri: Uri) { + val intent = Intent(Intent.ACTION_VIEW, uri) + + context.startActivity(intent) + } + + fun showSnackbar(uri: Uri) { + scope.launch { + val result = snackbarHostState.showSnackbar( + message = successMessage, + actionLabel = openMessage, + duration = SnackbarDuration.Short, + ) + + if (result == SnackbarResult.ActionPerformed) { + openFolder(uri) + } + } + } + + fun saveRecording(recorder: RecorderModel) { + scope.launch { + isProcessing = true + + // Give the user some time to see the processing dialog + delay(100) + + try { + + val recording = + // When new recording created + recorder.recorderService?.getRecordingInformation() + // When recording is loaded from lastRecording + ?: settings.lastRecording + ?: throw Exception("No recording information available") + val batchesFolder = + AudioBatchesFolder.importFromFolder(recording.folderPath, context) + + batchesFolder.concatenate( + recording.recordingStart, + recording.fileExtension, + ) + + // Save file + val name = batchesFolder.getName( + recording.recordingStart, + recording.fileExtension, + ) + + when (batchesFolder.type) { + BatchesFolder.BatchType.INTERNAL -> { + saveFile( + batchesFolder.asInternalGetOutputFile( + recording.recordingStart, + recording.fileExtension, + ), name + ) + } + + BatchesFolder.BatchType.CUSTOM -> { + showSnackbar(batchesFolder.customFolder!!.uri) + + if (settings.deleteRecordingsImmediately) { + batchesFolder.deleteRecordings() + } + } + } + } catch (error: Exception) { + Log.getStackTraceString(error) + } finally { + isProcessing = false + } + } + } + + // Register audio recorder events + DisposableEffect(key1 = audioRecorder, key2 = settings) { + audioRecorder.onRecordingSave = { + saveAsLastRecording(audioRecorder as RecorderModel) + + saveRecording(audioRecorder) + } + audioRecorder.onError = { + saveAsLastRecording(audioRecorder as RecorderModel) + + showRecorderError = true + } + + onDispose { + audioRecorder.onRecordingSave = {} + audioRecorder.onError = {} + } + } + + // Register video recorder events + DisposableEffect(key1 = videoRecorder, key2 = settings) { + videoRecorder.onRecordingSave = { + saveAsLastRecording(videoRecorder as RecorderModel) + + saveRecording(videoRecorder) + } + videoRecorder.onError = { + saveAsLastRecording(videoRecorder as RecorderModel) + + showRecorderError = true + } + + onDispose { + videoRecorder.onRecordingSave = {} + videoRecorder.onError = {} + } + } + + if (isProcessing) + RecorderProcessingDialog() + + if (showRecorderError) + RecorderErrorDialog( + onClose = { + showRecorderError = false + }, + onSave = { + }, + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt new file mode 100644 index 000000000..d8756ccaf --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt @@ -0,0 +1,47 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.atoms + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Memory +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Icon +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.myzel394.alibi.R + +@Composable +fun RecorderProcessingDialog() { + AlertDialog( + onDismissRequest = { }, + icon = { + Icon( + Icons.Default.Memory, + contentDescription = null, + ) + }, + title = { + Text( + stringResource(R.string.ui_audioRecorder_action_save_processing_dialog_title), + ) + }, + text = { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + stringResource(R.string.ui_audioRecorder_action_save_processing_dialog_description), + ) + Spacer(modifier = Modifier.height(32.dp)) + LinearProgressIndicator() + } + }, + confirmButton = {} + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index 5956c1b1e..71d55e885 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -75,7 +75,9 @@ fun AudioRecordingStatus( isPaused = audioRecorder.isPaused, onDelete = { scope.launch { - audioRecorder.stopRecording(context) + runCatching { + audioRecorder.stopRecording(context) + } audioRecorder.batchesFolder!!.deleteRecordings() } }, @@ -88,9 +90,7 @@ fun AudioRecordingStatus( }, onSave = { scope.launch { - runCatching { - audioRecorder.stopRecording(context) - } + audioRecorder.stopRecording(context) audioRecorder.onRecordingSave() } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index 016c2eafd..2905152e4 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -78,8 +78,6 @@ fun VideoRecordingStatus( modifier = Modifier.padding(bottom = 32.dp), ) { val cameraControl = videoRecorder.recorderService!!.cameraControl!! - println("cameraControl.hasTorchAvailable(): ${cameraControl.hasTorchAvailable()}") - println("videoRecorder: ${videoRecorder.recorderService?.cameraControl}") if (cameraControl.hasTorchAvailable()) { val isTorchEnabled = cameraControl.isTorchEnabled() @@ -101,7 +99,9 @@ fun VideoRecordingStatus( isPaused = videoRecorder.isPaused, onDelete = { scope.launch { - videoRecorder.stopRecording(context) + runCatching { + videoRecorder.stopRecording(context) + } videoRecorder.batchesFolder!!.deleteRecordings() } }, @@ -114,9 +114,7 @@ fun VideoRecordingStatus( }, onSave = { scope.launch { - runCatching { - videoRecorder.stopRecording(context) - } + videoRecorder.stopRecording(context) videoRecorder.onRecordingSave() } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt index 7066bba89..1738bd6ff 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt @@ -50,9 +50,4 @@ class VideoRecorderModel : putExtra("cameraID", cameraID) putExtra("enableAudio", enableAudio) } - - - override fun startRecording(context: Context, settings: AppSettings) { - super.startRecording(context, settings) - } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index e901428b9..4cf8c96c5 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -1,57 +1,37 @@ package app.myzel394.alibi.ui.screens -import android.content.Intent -import android.net.Uri -import android.util.Log import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Memory import androidx.compose.material.icons.filled.Settings -import androidx.compose.material.icons.filled.Warning -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api 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.Snackbar -import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.* -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import androidx.navigation.NavController import app.myzel394.alibi.ui.components.RecorderScreen.organisms.AudioRecordingStatus import app.myzel394.alibi.ui.components.RecorderScreen.organisms.StartRecording import app.myzel394.alibi.ui.enums.Screen -import app.myzel394.alibi.ui.utils.rememberFileSaverDialog import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings -import app.myzel394.alibi.helpers.AudioBatchesFolder -import app.myzel394.alibi.helpers.BatchesFolder +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecorderEventsHandler import app.myzel394.alibi.ui.components.RecorderScreen.organisms.VideoRecordingStatus import app.myzel394.alibi.ui.effects.rememberSettings import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.models.VideoRecorderModel -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -63,197 +43,14 @@ fun RecorderScreen( val snackbarHostState = remember { SnackbarHostState() } val context = LocalContext.current - val dataStore = context.dataStore val settings = rememberSettings() - val scope = rememberCoroutineScope() - - val saveFile = rememberFileSaverDialog( - settings.audioRecorderSettings.getMimeType() - ) { - if (settings.deleteRecordingsImmediately) { - audioRecorder.batchesFolder!!.deleteRecordings() - } - - if (!audioRecorder.batchesFolder!!.hasRecordingsAvailable()) { - scope.launch { - dataStore.updateData { - it.setLastRecording(null) - } - } - } - } - - var isProcessing by remember { mutableStateOf(false) } - var showRecorderError by remember { mutableStateOf(false) } - - fun saveAsLastRecording() { - if (!settings.deleteRecordingsImmediately) { - scope.launch { - dataStore.updateData { - it.setLastRecording( - audioRecorder.recorderService!!.getRecordingInformation() - ) - } - } - } - } - - val successMessage = stringResource(R.string.ui_audioRecorder_action_save_success) - val openMessage = stringResource(R.string.ui_audioRecorder_action_save_openFolder) - - fun openFolder(uri: Uri) { - val intent = Intent(Intent.ACTION_VIEW, uri) - - context.startActivity(intent) - } - - fun showSnackbar(uri: Uri) { - scope.launch { - val result = snackbarHostState.showSnackbar( - message = successMessage, - actionLabel = openMessage, - duration = SnackbarDuration.Short, - ) - - if (result == SnackbarResult.ActionPerformed) { - openFolder(uri) - } - } - } - - fun saveRecording() { - scope.launch { - isProcessing = true - - // Give the user some time to see the processing dialog - delay(100) - - try { - val recording = audioRecorder.recorderService?.getRecordingInformation() - ?: settings.lastRecording - ?: throw Exception("No recording information available") - val batchesFolder = - AudioBatchesFolder.importFromFolder(recording.folderPath, context) - - batchesFolder.concatenate( - recording.recordingStart, - recording.fileExtension, - ) - - // Save file - val name = batchesFolder.getName( - recording.recordingStart, - recording.fileExtension, - ) - - when (batchesFolder.type) { - BatchesFolder.BatchType.INTERNAL -> { - saveFile( - batchesFolder.asInternalGetOutputFile( - recording.recordingStart, - recording.fileExtension, - ), name - ) - } - - BatchesFolder.BatchType.CUSTOM -> { - showSnackbar(batchesFolder.customFolder!!.uri) - - if (settings.deleteRecordingsImmediately) { - batchesFolder.deleteRecordings() - } - } - } - } catch (error: Exception) { - Log.getStackTraceString(error) - } finally { - isProcessing = false - } - } - } - - DisposableEffect(key1 = audioRecorder, key2 = settings) { - audioRecorder.onRecordingSave = onRecordingSave@{ - saveAsLastRecording() - - saveRecording() - } - audioRecorder.onError = { - saveAsLastRecording() - - showRecorderError = true - } - - onDispose { - audioRecorder.onRecordingSave = {} - audioRecorder.onError = {} - } - } - - if (isProcessing) - AlertDialog( - onDismissRequest = { }, - icon = { - Icon( - Icons.Default.Memory, - contentDescription = null, - ) - }, - title = { - Text( - stringResource(R.string.ui_audioRecorder_action_save_processing_dialog_title), - ) - }, - text = { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text( - stringResource(R.string.ui_audioRecorder_action_save_processing_dialog_description), - ) - Spacer(modifier = Modifier.height(32.dp)) - LinearProgressIndicator() - } - }, - confirmButton = {} - ) - if (showRecorderError) - AlertDialog( - onDismissRequest = { showRecorderError = false }, - icon = { - Icon( - Icons.Default.Warning, - contentDescription = null, - ) - }, - title = { - Text(stringResource(R.string.ui_audioRecorder_error_recording_title)) - }, - text = { - Text(stringResource(R.string.ui_audioRecorder_error_recording_description)) - }, - dismissButton = { - Button( - onClick = { showRecorderError = false }, - colors = ButtonDefaults.textButtonColors(), - ) { - Text(stringResource(R.string.dialog_close_cancel_label)) - } - }, - confirmButton = { - Button( - onClick = { - showRecorderError = false - - saveRecording() - }, - colors = ButtonDefaults.textButtonColors(), - ) { - Text(stringResource(R.string.ui_audioRecorder_action_save_label)) - } - } - ) + RecorderEventsHandler( + settings = settings, + snackbarHostState = snackbarHostState, + audioRecorder = audioRecorder, + videoRecorder = videoRecorder, + ) // TopAppBar and AudioRecordingStart should be hidden when // the video preview is visible. @@ -315,7 +112,8 @@ fun RecorderScreen( audioRecorder = audioRecorder, videoRecorder = videoRecorder, appSettings = appSettings, - onSaveLastRecording = ::saveRecording, + onSaveLastRecording = { + }, showAudioRecorder = topBarVisible, onHideTopBar = { topBarVisible = false From d79aabab509b9c2adc9d45eb930077ef6d71a001 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 9 Dec 2023 10:37:45 +0100 Subject: [PATCH 057/176] fix: Properly cleanup recordings --- .../atoms/RecorderEventsHandler.kt | 12 ++-- .../app/myzel394/alibi/ui/screens/POCVideo.kt | 64 ------------------- 2 files changed, 8 insertions(+), 68 deletions(-) delete mode 100644 app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt index 7a95e6b28..bae9c787a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt @@ -54,12 +54,16 @@ fun RecorderEventsHandler( settings.audioRecorderSettings.getMimeType() ) { if (settings.deleteRecordingsImmediately) { - audioRecorder.batchesFolder!!.deleteRecordings() - videoRecorder.batchesFolder!!.deleteRecordings() + runCatching { + audioRecorder.batchesFolder?.deleteRecordings() + } + runCatching { + videoRecorder.batchesFolder?.deleteRecordings() + } } - if (!audioRecorder.batchesFolder!!.hasRecordingsAvailable() - || !videoRecorder.batchesFolder!!.hasRecordingsAvailable() + if (audioRecorder.batchesFolder?.hasRecordingsAvailable() == false + && videoRecorder.batchesFolder?.hasRecordingsAvailable() == false ) { scope.launch { dataStore.updateData { diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt deleted file mode 100644 index 670c4d1ab..000000000 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/POCVideo.kt +++ /dev/null @@ -1,64 +0,0 @@ -package app.myzel394.alibi.ui.screens - -import android.annotation.SuppressLint -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.dp -import app.myzel394.alibi.db.AppSettings -import app.myzel394.alibi.ui.models.VideoRecorderModel -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch - -@SuppressLint("NewApi") -@Composable -fun POCVideo( - videoRecorder: VideoRecorderModel, - settings: AppSettings, -) { - val context = LocalContext.current - - var started by rememberSaveable { - mutableStateOf(false) - } - - val scope = rememberCoroutineScope() - - Box( - modifier = Modifier - .fillMaxSize() - .padding(64.dp) - ) { - Button(onClick = { - if (!started) { - videoRecorder.startRecording(context, settings) - } else { - scope.launch { - val information = videoRecorder.recorderService!!.getRecordingInformation() - val batchesFolder = videoRecorder.batchesFolder!! - videoRecorder.stopRecording(context) - - batchesFolder.concatenate( - recordingStart = information.recordingStart, - extension = information.fileExtension, - disableCache = true, - ) - } - } - - started = !started - }) { - Text("Start") - } - } -} \ No newline at end of file From df78952cfb837131cbdcbd2ad894c6c1b22ffcef Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:20:53 +0100 Subject: [PATCH 058/176] fix: Set state after action to avoid coroutine going out of scope --- .../java/app/myzel394/alibi/services/RecorderService.kt | 6 +++++- .../app/myzel394/alibi/services/VideoRecorderService.kt | 8 +++++--- .../app/myzel394/alibi/ui/models/BaseRecorderModel.kt | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index f69c7928e..7a42cedd1 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -32,6 +32,9 @@ abstract class RecorderService : LifecycleService() { var state = RecorderState.IDLE private set + protected var _newState = RecorderState.IDLE + private set + var onStateChange: ((RecorderState) -> Unit)? = null var onError: () -> Unit = {} @@ -168,8 +171,9 @@ abstract class RecorderService : LifecycleService() { } suspend fun stopRecording() { - changeState(RecorderState.IDLE) + _newState = RecorderState.IDLE stop() + changeState(RecorderState.IDLE) } override fun onDestroy() { diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 539b44725..349aa787f 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -105,11 +105,11 @@ class VideoRecorderService : super.startNewCycle() fun action() { - activeRecording?.stop() + stopActiveRecording() val newRecording = prepareVideoRecording() activeRecording = newRecording.start(ContextCompat.getMainExecutor(this)) { event -> - if (event is VideoRecordEvent.Finalize && this@VideoRecorderService.state == RecorderState.IDLE) { + if (event is VideoRecordEvent.Finalize && this@VideoRecorderService._newState == RecorderState.IDLE) { _videoFinalizerListener.complete(Unit) } } @@ -197,7 +197,9 @@ class VideoRecorderService : // `resume` override not needed as `startNewCycle` is called by `IntervalRecorderService` private fun stopActiveRecording() { - activeRecording?.stop() + runCatching { + activeRecording?.stop() + } } @SuppressLint("MissingPermission") diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 01cfad1d1..3e51e1e6e 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -136,7 +136,7 @@ abstract class BaseRecorderModel Date: Thu, 14 Dec 2023 17:29:15 +0100 Subject: [PATCH 059/176] fix: Save audio and video using appropriate file saver --- .../java/app/myzel394/alibi/db/AppSettings.kt | 2 + .../atoms/RecorderEventsHandler.kt | 47 ++++++++++++++----- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 44fe94909..2e0ff560a 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -401,6 +401,8 @@ data class VideoRecorderSettings( ) } + fun getMimeType() = "video/mp4" + companion object { fun getDefaultInstance() = VideoRecorderSettings() diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt index bae9c787a..434cd99a8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt @@ -21,6 +21,7 @@ import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.BatchesFolder +import app.myzel394.alibi.helpers.VideoBatchesFolder import app.myzel394.alibi.services.IntervalRecorderService import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.models.BaseRecorderModel @@ -50,21 +51,29 @@ fun RecorderEventsHandler( var isProcessing by remember { mutableStateOf(false) } var showRecorderError by remember { mutableStateOf(false) } - val saveFile = rememberFileSaverDialog( - settings.audioRecorderSettings.getMimeType() - ) { + val saveAudioFile = rememberFileSaverDialog(settings.audioRecorderSettings.getMimeType()) { if (settings.deleteRecordingsImmediately) { runCatching { audioRecorder.batchesFolder?.deleteRecordings() } + } + + if (audioRecorder.batchesFolder?.hasRecordingsAvailable() == false) { + scope.launch { + dataStore.updateData { + it.setLastRecording(null) + } + } + } + } + val saveVideoFile = rememberFileSaverDialog(settings.videoRecorderSettings.getMimeType()) { + if (settings.deleteRecordingsImmediately) { runCatching { videoRecorder.batchesFolder?.deleteRecordings() } } - if (audioRecorder.batchesFolder?.hasRecordingsAvailable() == false - && videoRecorder.batchesFolder?.hasRecordingsAvailable() == false - ) { + if (videoRecorder.batchesFolder?.hasRecordingsAvailable() == false) { scope.launch { dataStore.updateData { it.setLastRecording(null) @@ -126,7 +135,10 @@ fun RecorderEventsHandler( ?: settings.lastRecording ?: throw Exception("No recording information available") val batchesFolder = - AudioBatchesFolder.importFromFolder(recording.folderPath, context) + if (recorder.javaClass == AudioRecorderModel::class.java) + AudioBatchesFolder.importFromFolder(recording.folderPath, context) + else + VideoBatchesFolder.importFromFolder(recording.folderPath, context) batchesFolder.concatenate( recording.recordingStart, @@ -141,12 +153,21 @@ fun RecorderEventsHandler( when (batchesFolder.type) { BatchesFolder.BatchType.INTERNAL -> { - saveFile( - batchesFolder.asInternalGetOutputFile( - recording.recordingStart, - recording.fileExtension, - ), name - ) + if (batchesFolder is AudioBatchesFolder) { + saveAudioFile( + batchesFolder.asInternalGetOutputFile( + recording.recordingStart, + recording.fileExtension, + ), name + ) + } else if (batchesFolder is VideoBatchesFolder) { + saveVideoFile( + batchesFolder.asInternalGetOutputFile( + recording.recordingStart, + recording.fileExtension, + ), name + ) + } } BatchesFolder.BatchType.CUSTOM -> { From de30f681e820d35196fdd477a38e7f9f26d45ac8 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 14 Dec 2023 18:04:02 +0100 Subject: [PATCH 060/176] docs: Add docs for services --- app/src/main/java/app/myzel394/alibi/services/README.md | 4 ++++ app/src/main/java/app/myzel394/alibi/services/model.svg | 1 + 2 files changed, 5 insertions(+) create mode 100644 app/src/main/java/app/myzel394/alibi/services/README.md create mode 100644 app/src/main/java/app/myzel394/alibi/services/model.svg diff --git a/app/src/main/java/app/myzel394/alibi/services/README.md b/app/src/main/java/app/myzel394/alibi/services/README.md new file mode 100644 index 000000000..bac59f30f --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/services/README.md @@ -0,0 +1,4 @@ +I found it a bit confusing on how to properly handle the services, so I made this diagram +to help me understand it better. I hope it helps you too. + +![Diagram](model.svg) \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/services/model.svg b/app/src/main/java/app/myzel394/alibi/services/model.svg new file mode 100644 index 000000000..4cf153444 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/services/model.svg @@ -0,0 +1 @@ +UserUserModelModelRecordingRecordingStateStateUser starts recordingModel creates new serviceModel binds to RecordingState is set to `RECORDING` to indicate that the recording is activeState mirrored to ModelUser pauses the recordingCommands Recording to pauseState is set to `PAUSED` to indicate that the recording is pausedActually pauses the recordingUser continues the recordingCommands Recording to continueState is set to `RECORDING` to indicate that the recording is activeActually continues with recordingUser stops recordingCommands Recording to stopState is set to `IDLE` to indicate that the recording will be destroyedRunning cleanupUnbinds from RecordingDestroys model \ No newline at end of file From 3d355df5227ed2a0c33b88b9ded83c43e994ebc4 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 15 Dec 2023 18:44:47 +0100 Subject: [PATCH 061/176] current stand --- .../app/myzel394/alibi/enums/RecorderState.kt | 5 +- .../alibi/services/AudioRecorderService.kt | 21 +++- .../alibi/services/IntervalRecorderService.kt | 1 + .../services/RecorderNotificationHelper.kt | 2 +- .../alibi/services/RecorderService.kt | 107 ++++++++---------- .../alibi/services/VideoRecorderService.kt | 21 +++- .../alibi/ui/models/BaseRecorderModel.kt | 29 ++--- .../alibi/ui/screens/RecorderScreen.kt | 1 + 8 files changed, 107 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/enums/RecorderState.kt b/app/src/main/java/app/myzel394/alibi/enums/RecorderState.kt index ed3cb1286..2642e6c31 100644 --- a/app/src/main/java/app/myzel394/alibi/enums/RecorderState.kt +++ b/app/src/main/java/app/myzel394/alibi/enums/RecorderState.kt @@ -1,7 +1,10 @@ package app.myzel394.alibi.enums enum class RecorderState { - IDLE, + STOPPED, RECORDING, PAUSED, + + // Only used by the model to indicate that the service is not running + IDLE } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index 170ab5304..4de23007a 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -1,6 +1,7 @@ package app.myzel394.alibi.services import android.content.Context +import android.content.pm.ServiceInfo import android.media.AudioDeviceCallback import android.media.AudioDeviceInfo import android.media.AudioManager @@ -9,6 +10,8 @@ import android.media.MediaRecorder.OnErrorListener import android.os.Build import android.os.Handler import android.os.Looper +import androidx.core.app.ServiceCompat +import app.myzel394.alibi.NotificationHelper import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AudioRecorderSettings import app.myzel394.alibi.db.RecordingInformation @@ -71,11 +74,10 @@ class AudioRecorderService : } override suspend fun stop() { - super.stop() - resetRecorder() - selectedMicrophone = null unregisterMicrophoneListener() + + super.stop() } override fun resume() { @@ -83,6 +85,19 @@ class AudioRecorderService : createAmplitudesTimer() } + override fun startForegroundService() { + ServiceCompat.startForeground( + this, + NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID, + getNotificationHelper().buildStartingNotification(), + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE + } else { + 0 + }, + ) + } + // ==== Amplitude related ==== private fun getAmplitudeAmount(): Int = amplitudesAmount diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index 238830860..7fb46f3e1 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -57,6 +57,7 @@ abstract class IntervalRecorderService override suspend fun stop() { cycleTimer.shutdown() + super.stop() } fun clearAllRecordings() { diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt index 86ad9bb4d..2464083a5 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt @@ -112,7 +112,7 @@ data class RecorderNotificationHelper( .addAction( R.drawable.ic_cancel, context.getString(R.string.ui_audioRecorder_action_delete_label), - getNotificationChangeStateIntent(RecorderState.IDLE, 1), + getNotificationChangeStateIntent(RecorderState.STOPPED, 1), ) .addAction( R.drawable.ic_pause, diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index 7a42cedd1..146dc650c 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -2,7 +2,6 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint import android.app.Notification -import android.app.Service import android.content.Intent import android.content.pm.ServiceInfo import android.os.Binder @@ -25,29 +24,60 @@ abstract class RecorderService : LifecycleService() { private val binder = RecorderBinder() private var isPaused: Boolean = false - lateinit var recordingStart: LocalDateTime private set + private lateinit var recordingTimeTimer: ScheduledExecutorService + private var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null - var state = RecorderState.IDLE - private set - - protected var _newState = RecorderState.IDLE + var state = RecorderState.STOPPED private set var onStateChange: ((RecorderState) -> Unit)? = null var onError: () -> Unit = {} + var onRecordingTimeChange: ((Long) -> Unit)? = null var recordingTime = 0L private set - private lateinit var recordingTimeTimer: ScheduledExecutorService - var onRecordingTimeChange: ((Long) -> Unit)? = null - var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null protected abstract fun start() + protected abstract fun pause() + + // TODO: Move pause / recording here protected abstract fun resume() - protected abstract suspend fun stop() + protected open suspend fun stop() { + } + + override fun onDestroy() { + NotificationManagerCompat.from(this) + .cancel(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID) + stopForeground(STOP_FOREGROUND_REMOVE) + stopSelf() + super.onDestroy() + } + + protected abstract fun startForegroundService() + + fun startRecording() { + recordingStart = LocalDateTime.now() + + startForegroundService() + changeState(RecorderState.RECORDING) + start() + } + + suspend fun stopRecording() { + changeState(RecorderState.STOPPED) + stop() + } + + fun pauseRecording() { + changeState(RecorderState.PAUSED) + } + + fun resumeRecording() { + changeState(RecorderState.RECORDING) + } override fun onBind(intent: Intent): IBinder? { super.onBind(intent) @@ -68,7 +98,7 @@ abstract class RecorderService : LifecycleService() { "changeState" -> { val newState = intent.getStringExtra("newState")?.let { RecorderState.valueOf(it) - } ?: RecorderState.IDLE + } ?: RecorderState.STOPPED changeState(newState) } } @@ -94,14 +124,9 @@ abstract class RecorderService : LifecycleService() { } } - protected fun _changeStateValue(newState: RecorderState) { - state = newState - - onStateChange?.invoke(newState) - } - // Used to change the state of the service // will internally call start() / pause() / resume() / stop() + // Immediately after creating the service make sure to call `changeState(RecorderState.RECORDING)` @SuppressLint("MissingPermission") fun changeState(newState: RecorderState) { if (state == newState) { @@ -114,23 +139,24 @@ abstract class RecorderService : LifecycleService() { if (isPaused) { resume() isPaused = false - } else { - start() } + // `start` is handled by `startRecording` createRecordingTimeTimer() } RecorderState.PAUSED -> { - pause() isPaused = true recordingTimeTimer.shutdown() + pause() } - RecorderState.IDLE -> { + RecorderState.STOPPED -> { recordingTimeTimer.shutdown() } + + else -> {} } // Update notification @@ -148,48 +174,13 @@ abstract class RecorderService : LifecycleService() { ) } - _changeStateValue(newState) - } - - // Must be immediately called after creating the service! - fun startRecording() { - recordingStart = LocalDateTime.now() - - ServiceCompat.startForeground( - this, - NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID, - getNotificationHelper().buildStartingNotification(), - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE - } else { - 0 - }, - ) - - // Start - changeState(RecorderState.RECORDING) - } - - suspend fun stopRecording() { - _newState = RecorderState.IDLE - stop() - changeState(RecorderState.IDLE) - } - - override fun onDestroy() { - super.onDestroy() - - stopForeground(STOP_FOREGROUND_REMOVE) - NotificationManagerCompat.from(this) - .cancel(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID) - stopSelf() + onStateChange?.invoke(newState) } - private fun getNotificationHelper(): RecorderNotificationHelper { + protected fun getNotificationHelper(): RecorderNotificationHelper { return RecorderNotificationHelper(this, notificationDetails) } - private fun buildNotification(): Notification { val notificationHelper = getNotificationHelper() diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 349aa787f..92645e842 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -2,9 +2,10 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint import android.content.Intent +import android.content.pm.ServiceInfo +import android.os.Build import android.util.Range import androidx.camera.core.Camera -import androidx.camera.core.CameraControl import androidx.camera.core.CameraSelector import androidx.camera.core.TorchState import androidx.camera.lifecycle.ProcessCameraProvider @@ -15,7 +16,9 @@ import androidx.camera.video.Recorder import androidx.camera.video.Recording import androidx.camera.video.VideoCapture import androidx.camera.video.VideoRecordEvent +import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat +import app.myzel394.alibi.NotificationHelper import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState @@ -25,7 +28,6 @@ import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull @@ -100,6 +102,19 @@ class VideoRecorderService : stopActiveRecording() } + override fun startForegroundService() { + ServiceCompat.startForeground( + this, + NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID, + getNotificationHelper().buildStartingNotification(), + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE + } else { + 0 + }, + ) + } + @SuppressLint("MissingPermission") override fun startNewCycle() { super.startNewCycle() @@ -109,7 +124,7 @@ class VideoRecorderService : val newRecording = prepareVideoRecording() activeRecording = newRecording.start(ContextCompat.getMainExecutor(this)) { event -> - if (event is VideoRecordEvent.Finalize && this@VideoRecorderService._newState == RecorderState.IDLE) { + if (event is VideoRecordEvent.Finalize && this@VideoRecorderService.state == RecorderState.STOPPED) { _videoFinalizerListener.complete(Unit) } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 3e51e1e6e..1f5415b94 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -4,22 +4,19 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection -import android.net.Uri import android.os.IBinder import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.core.content.ContextCompat -import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.ViewModel import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.BatchesFolder -import app.myzel394.alibi.services.AudioRecorderService import app.myzel394.alibi.services.IntervalRecorderService import app.myzel394.alibi.services.RecorderNotificationHelper import app.myzel394.alibi.services.RecorderService -import kotlinx.coroutines.delay import kotlinx.serialization.json.Json abstract class BaseRecorderModel, B : BatchesFolder?> : @@ -28,19 +25,19 @@ abstract class BaseRecorderModel(null) + var recordingTime by mutableLongStateOf(0) protected set open val isInRecording: Boolean - get() = recorderState !== RecorderState.IDLE && recordingTime != null && recorderService != null + get() = recorderService != null val isPaused: Boolean get() = recorderState === RecorderState.PAUSED val progress: Float - get() = (recordingTime!! / recorderService!!.settings.maxDuration).toFloat() + get() = (recordingTime / recorderService!!.settings.maxDuration).toFloat() - var recorderService: T? = null + var recorderService by mutableStateOf(null) protected set var onRecordingSave: () -> Unit = {} @@ -80,14 +77,14 @@ abstract class BaseRecorderModel context.bindService(intent, connection, 0) diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index 4cf8c96c5..a6e072162 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -113,6 +113,7 @@ fun RecorderScreen( videoRecorder = videoRecorder, appSettings = appSettings, onSaveLastRecording = { + // TODO: Improve onSave! }, showAudioRecorder = topBarVisible, onHideTopBar = { From 4be2fc52e23f51b532551ec41eb093061d1bd8c3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 15 Dec 2023 19:40:46 +0100 Subject: [PATCH 062/176] fix: Fix recorder states --- .../alibi/services/IntervalRecorderService.kt | 3 ++ .../alibi/services/RecorderService.kt | 33 ++++++++----------- .../alibi/services/VideoRecorderService.kt | 7 ++-- .../organisms/VideoRecordingStatus.kt | 28 ++++++++-------- .../alibi/ui/models/BaseRecorderModel.kt | 11 +++++-- .../alibi/ui/models/VideoRecorderModel.kt | 5 ++- 6 files changed, 49 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index 7fb46f3e1..35139b272 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -38,6 +38,7 @@ abstract class IntervalRecorderService } override fun start() { + super.start() batchesFolder.initFolders() if (!batchesFolder.checkIfFolderIsAccessible()) { onCustomOutputFolderNotAccessible() @@ -48,10 +49,12 @@ abstract class IntervalRecorderService } override fun pause() { + super.pause() cycleTimer.shutdown() } override fun resume() { + super.resume() createTimer() } diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index 146dc650c..cb5517790 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -39,21 +39,27 @@ abstract class RecorderService : LifecycleService() { var recordingTime = 0L private set - protected abstract fun start() + protected open fun start() { + createRecordingTimeTimer() + } - protected abstract fun pause() + protected open fun pause() { + isPaused = true - // TODO: Move pause / recording here - protected abstract fun resume() - protected open suspend fun stop() { + recordingTimeTimer.shutdown() + } + + protected open fun resume() { + createRecordingTimeTimer() } - override fun onDestroy() { + protected open suspend fun stop() { + recordingTimeTimer.shutdown() + NotificationManagerCompat.from(this) .cancel(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID) stopForeground(STOP_FOREGROUND_REMOVE) stopSelf() - super.onDestroy() } protected abstract fun startForegroundService() @@ -141,20 +147,9 @@ abstract class RecorderService : LifecycleService() { isPaused = false } // `start` is handled by `startRecording` - - createRecordingTimeTimer() - } - - RecorderState.PAUSED -> { - isPaused = true - - recordingTimeTimer.shutdown() - pause() } - RecorderState.STOPPED -> { - recordingTimeTimer.shutdown() - } + RecorderState.PAUSED -> pause() else -> {} } diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 92645e842..260772002 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -33,8 +33,6 @@ import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull import kotlin.properties.Delegates -const val CAMERA_CLOSE_TIMEOUT = 20000L - class VideoRecorderService : IntervalRecorderService() { override var batchesFolder: BatchesFolder = VideoBatchesFolder.viaInternalFolder(this) @@ -238,7 +236,10 @@ class VideoRecorderService : type = RecordingInformation.Type.VIDEO, ) - // TODO: Save camera selector as it doesn't make sense to change the camera midway + companion object { + const val CAMERA_CLOSE_TIMEOUT = 20000L + } + data class Settings( override val maxDuration: Long, override val intervalDuration: Long, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index 2905152e4..6eeab9589 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -77,20 +77,22 @@ fun VideoRecordingStatus( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(bottom = 32.dp), ) { - val cameraControl = videoRecorder.recorderService!!.cameraControl!! - if (cameraControl.hasTorchAvailable()) { - val isTorchEnabled = cameraControl.isTorchEnabled() + if (!videoRecorder.isStartingRecording) { + val cameraControl = videoRecorder.recorderService!!.cameraControl!! + if (cameraControl.hasTorchAvailable()) { + val isTorchEnabled = cameraControl.isTorchEnabled() - TorchStatus( - enabled = isTorchEnabled, - onChange = { - if (isTorchEnabled) { - cameraControl.disableTorch() - } else { - cameraControl.enableTorch() - } - }, - ) + TorchStatus( + enabled = isTorchEnabled, + onChange = { + if (isTorchEnabled) { + cameraControl.disableTorch() + } else { + cameraControl.enableTorch() + } + }, + ) + } } Divider() diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 1f5415b94..b43929c79 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -77,6 +77,8 @@ abstract class BaseRecorderModel Date: Fri, 15 Dec 2023 19:54:42 +0100 Subject: [PATCH 063/176] fix: Differentiate between stopping and destroying -> destroy service after stoppind and saving last recording information --- .../alibi/services/RecorderService.kt | 12 +++-- .../atoms/RecorderEventsHandler.kt | 48 +++++++++++++------ .../organisms/AudioRecordingStatus.kt | 5 +- .../organisms/VideoRecordingStatus.kt | 5 +- .../alibi/ui/models/BaseRecorderModel.kt | 15 ++++-- 5 files changed, 52 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index cb5517790..db2fa7b76 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -55,11 +55,6 @@ abstract class RecorderService : LifecycleService() { protected open suspend fun stop() { recordingTimeTimer.shutdown() - - NotificationManagerCompat.from(this) - .cancel(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID) - stopForeground(STOP_FOREGROUND_REMOVE) - stopSelf() } protected abstract fun startForegroundService() @@ -85,6 +80,13 @@ abstract class RecorderService : LifecycleService() { changeState(RecorderState.RECORDING) } + fun destroy() { + NotificationManagerCompat.from(this) + .cancel(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID) + stopForeground(STOP_FOREGROUND_REMOVE) + stopSelf() + } + override fun onBind(intent: Intent): IBinder? { super.onBind(intent) return binder diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt index 434cd99a8..0862f1a91 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt @@ -82,16 +82,14 @@ fun RecorderEventsHandler( } } - fun saveAsLastRecording( + suspend fun saveAsLastRecording( recorder: RecorderModel ) { if (!settings.deleteRecordingsImmediately) { - scope.launch { - dataStore.updateData { - it.setLastRecording( - recorder.recorderService!!.getRecordingInformation() - ) - } + dataStore.updateData { + it.setLastRecording( + recorder.recorderService!!.getRecordingInformation() + ) } } } @@ -189,14 +187,24 @@ fun RecorderEventsHandler( // Register audio recorder events DisposableEffect(key1 = audioRecorder, key2 = settings) { audioRecorder.onRecordingSave = { - saveAsLastRecording(audioRecorder as RecorderModel) + scope.launch { + audioRecorder.stopRecording(context) - saveRecording(audioRecorder) + kotlin.runCatching { + saveAsLastRecording(audioRecorder as RecorderModel) + + saveRecording(audioRecorder) + } + + audioRecorder.destroyService(context) + } } audioRecorder.onError = { - saveAsLastRecording(audioRecorder as RecorderModel) + scope.launch { + saveAsLastRecording(audioRecorder as RecorderModel) - showRecorderError = true + showRecorderError = true + } } onDispose { @@ -208,14 +216,24 @@ fun RecorderEventsHandler( // Register video recorder events DisposableEffect(key1 = videoRecorder, key2 = settings) { videoRecorder.onRecordingSave = { - saveAsLastRecording(videoRecorder as RecorderModel) + scope.launch { + videoRecorder.stopRecording(context) - saveRecording(videoRecorder) + kotlin.runCatching { + saveAsLastRecording(videoRecorder as RecorderModel) + + saveRecording(videoRecorder) + } + + videoRecorder.destroyService(context) + } } videoRecorder.onError = { - saveAsLastRecording(videoRecorder as RecorderModel) + scope.launch { + saveAsLastRecording(videoRecorder as RecorderModel) - showRecorderError = true + showRecorderError = true + } } onDispose { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index 71d55e885..cb7612a2f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -89,10 +89,7 @@ fun AudioRecordingStatus( } }, onSave = { - scope.launch { - audioRecorder.stopRecording(context) - audioRecorder.onRecordingSave() - } + audioRecorder.onRecordingSave() } ) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index 6eeab9589..6061eea12 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -115,10 +115,7 @@ fun VideoRecordingStatus( } }, onSave = { - scope.launch { - videoRecorder.stopRecording(context) - videoRecorder.onRecordingSave() - } + videoRecorder.onRecordingSave() } ) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index b43929c79..ce7b3dcde 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -136,14 +136,9 @@ abstract class BaseRecorderModel Date: Fri, 15 Dec 2023 20:03:14 +0100 Subject: [PATCH 064/176] fix: Destroy service after stopping it when deleting recording --- .../RecorderScreen/organisms/AudioRecordingStatus.kt | 3 +++ .../RecorderScreen/organisms/VideoRecordingStatus.kt | 3 +++ 2 files changed, 6 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index cb7612a2f..0233368e8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -78,6 +78,9 @@ fun AudioRecordingStatus( runCatching { audioRecorder.stopRecording(context) } + runCatching { + audioRecorder.destroyService(context) + } audioRecorder.batchesFolder!!.deleteRecordings() } }, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index 6061eea12..cb4fcabdb 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -104,6 +104,9 @@ fun VideoRecordingStatus( runCatching { videoRecorder.stopRecording(context) } + runCatching { + videoRecorder.destroyService(context) + } videoRecorder.batchesFolder!!.deleteRecordings() } }, From 5f8abca57a8f885659205a90537846512d61b1d5 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 15 Dec 2023 20:35:10 +0100 Subject: [PATCH 065/176] feat: Add active camera to VideoRecordingStatus --- app/build.gradle | 3 + .../molecules/CamerasSelection.kt | 2 +- .../organisms/VideoRecordingStatus.kt | 55 ++++++++++++++++++- .../myzel394/alibi/ui/utils/camera-info.kt | 4 ++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4d282bba9..4f77945a4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -142,4 +142,7 @@ dependencies { implementation "androidx.camera:camera-view:${camerax_version}" implementation "androidx.camera:camera-extensions:${camerax_version}" + + + implementation "com.valentinilk.shimmer:compose-shimmer:1.2.0" } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt index b7ad8a0d0..510f0f335 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/CamerasSelection.kt @@ -23,7 +23,7 @@ fun CamerasSelection( ) Column { - if (cameras.count() == 2 && cameras.elementAt(0).id == 0 && cameras.elementAt(1).id == 1) { + if (CameraInfo.checkHasNormalCameras(cameras)) { CameraSelectionButton( cameraID = CameraInfo.Lens.BACK, label = stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_back_label), diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index cb4fcabdb..1e9d4cf95 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -3,6 +3,7 @@ package app.myzel394.alibi.ui.components.RecorderScreen.organisms import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.tween import androidx.compose.animation.expandHorizontally +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -19,6 +20,8 @@ import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material3.Divider import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -29,7 +32,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.core.util.TypedValueCompat +import app.myzel394.alibi.R import app.myzel394.alibi.ui.components.RecorderScreen.atoms.DeleteButton import app.myzel394.alibi.ui.components.RecorderScreen.atoms.PauseResumeButton import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingProgress @@ -39,7 +46,9 @@ import app.myzel394.alibi.ui.components.RecorderScreen.atoms.TorchStatus import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingControl import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingStatus import app.myzel394.alibi.ui.models.VideoRecorderModel +import app.myzel394.alibi.ui.utils.CameraInfo import app.myzel394.alibi.ui.utils.KeepScreenOn +import com.valentinilk.shimmer.shimmer import kotlinx.coroutines.launch @Composable @@ -48,6 +57,7 @@ fun VideoRecordingStatus( ) { val context = LocalContext.current val scope = rememberCoroutineScope() + val availableCameras = CameraInfo.queryAvailableCameras(context) KeepScreenOn() Column( @@ -58,16 +68,57 @@ fun VideoRecordingStatus( ) { Box {} - Column { + Column( + verticalArrangement = Arrangement + .spacedBy(24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { Icon( Icons.Default.CameraAlt, contentDescription = null, modifier = Modifier.size(64.dp) ) + + if (videoRecorder.isStartingRecording) { + Box( + modifier = Modifier + .width(128.dp) + .height( + with(LocalDensity.current) { + MaterialTheme.typography.labelMedium.fontSize.toDp() + } + ) + .shimmer() + .background( + MaterialTheme.colorScheme.surfaceVariant, + MaterialTheme.shapes.small + ) + ) + } else { + Text( + stringResource( + R.string.form_value_selected, + if (CameraInfo.checkHasNormalCameras(availableCameras)) { + videoRecorder.cameraID.let { + if (it == CameraInfo.Lens.BACK.androidValue) + stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_back_label) + else + stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_front_label) + } + } else { + stringResource( + R.string.ui_videoRecorder_action_start_settings_cameraLens_label, + videoRecorder.cameraID + ) + } + ), + style = MaterialTheme.typography.labelMedium, + ) + } } RecordingStatus( - recordingTime = videoRecorder.recordingTime!!, + recordingTime = videoRecorder.recordingTime, progress = videoRecorder.progress, ) diff --git a/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt b/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt index 5e6be2fed..df8d59b1d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/utils/camera-info.kt @@ -45,5 +45,9 @@ data class CameraInfo( id = cameraId.toInt(), ) } + + // "normal cameras" means the device has a front and back camera + fun checkHasNormalCameras(cameras: Iterable) = + cameras.count() == 2 && cameras.elementAt(0).id == 0 && cameras.elementAt(1).id == 1 } } \ No newline at end of file From ed8ab1d7b0764edc0272da5dbeeda7a1efdc0443 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 15 Dec 2023 20:40:11 +0100 Subject: [PATCH 066/176] chore: Update dependencies --- app/build.gradle | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4f77945a4..03d67f528 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -95,7 +95,8 @@ dependencies { implementation 'androidx.core:core-ktx:1.12.0' implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0') implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' - implementation 'androidx.activity:activity-compose:1.8.1' + implementation 'androidx.activity:activity-compose:1.8.2' + implementation 'androidx.activity:activity-ktx:1.8.2' implementation platform('androidx.compose:compose-bom:2022.10.00') implementation 'androidx.compose.ui:ui' implementation 'androidx.compose.ui:ui-graphics' @@ -105,6 +106,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.lifecycle:lifecycle-service:2.6.2' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' @@ -113,28 +115,27 @@ dependencies { debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' - implementation "androidx.navigation:navigation-compose:2.7.5" + implementation "androidx.navigation:navigation-compose:2.7.6" implementation 'com.google.dagger:hilt-android:2.48' annotationProcessor 'com.google.dagger:hilt-compiler:2.46.1' implementation "androidx.hilt:hilt-navigation-compose:1.1.0" + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' + implementation 'com.arthenica:ffmpeg-kit-full-gpl:5.1' implementation "androidx.datastore:datastore-preferences:1.0.0" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1" - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' implementation 'com.maxkeppeler.sheets-compose-dialogs:core:1.2.0' implementation 'com.maxkeppeler.sheets-compose-dialogs:duration:1.2.0' implementation 'com.maxkeppeler.sheets-compose-dialogs:list:1.2.0' implementation 'com.maxkeppeler.sheets-compose-dialogs:input:1.2.0' - implementation 'androidx.activity:activity-ktx:1.8.1' - - def camerax_version = "1.3.0" + def camerax_version = "1.3.1" implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-lifecycle:${camerax_version}" From 1791c67518b284ab69e2efadf0790b7093690c03 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 15 Dec 2023 21:08:08 +0100 Subject: [PATCH 067/176] feat: Start recording with default params on click and show config on long press --- .../molecules/AudioRecordingStart.kt | 2 +- .../molecules/VideoRecordingStart.kt | 70 +++++++++++++------ app/src/main/res/values/strings.xml | 1 + 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt index 6eebedd6c..12feaece9 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt @@ -66,7 +66,7 @@ fun AudioRecordingStart( .semantics { contentDescription = label } - .size(200.dp) + .size(250.dp) .clip(shape = CircleShape), colors = ButtonDefaults.outlinedButtonColors(), ) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt index 2e4bd5048..883e8420f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt @@ -1,13 +1,25 @@ package app.myzel394.alibi.ui.components.RecorderScreen.molecules import android.Manifest +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.indication +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt +import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon @@ -16,11 +28,13 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription @@ -29,9 +43,11 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.models.VideoRecorderModel +@OptIn(ExperimentalFoundationApi::class) @Composable fun VideoRecordingStart( videoRecorder: VideoRecorderModel, @@ -70,31 +86,45 @@ fun VideoRecordingStart( ) { trigger -> val label = stringResource(R.string.ui_videoRecorder_action_start_label) - Button( - onClick = trigger, + Column( modifier = Modifier + .size(250.dp) + .clip(CircleShape) .semantics { contentDescription = label } - .size(200.dp) - .clip(shape = CircleShape), - colors = ButtonDefaults.outlinedButtonColors(), + .combinedClickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberRipple(color = MaterialTheme.colorScheme.primary), + onClick = { + videoRecorder.startRecording(context, appSettings) + }, + onLongClick = { + showSheet = true + }, + ), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Icon( - Icons.Default.CameraAlt, - contentDescription = null, - modifier = Modifier - .size(80.dp), - ) - Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) - Text( - label, - style = MaterialTheme.typography.titleSmall, - ) - } + Icon( + Icons.Default.CameraAlt, + contentDescription = null, + modifier = Modifier + .size(80.dp), + tint = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + label, + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + stringResource(R.string.ui_videoRecorder_action_configure_label), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) } } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 21e8fb979..1c17b1278 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -150,4 +150,5 @@ Hold down on button to preview your camera Disable Torch Enable Torch + Press long for configuration \ No newline at end of file From c2a9791680593c73dd00c6a8b4f7b30e2f307d76 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 15 Dec 2023 21:24:28 +0100 Subject: [PATCH 068/176] feat: Add save last recording functionality again --- .../myzel394/alibi/helpers/BatchesFolder.kt | 36 ++++---- .../atoms/RecorderEventsHandler.kt | 86 ++++++++++++------- .../organisms/AudioRecordingStatus.kt | 2 +- .../organisms/VideoRecordingStatus.kt | 2 +- .../alibi/ui/models/BaseRecorderModel.kt | 6 +- .../alibi/ui/screens/RecorderScreen.kt | 9 +- 6 files changed, 85 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 0525b101b..85ce60086 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -126,23 +126,25 @@ abstract class BatchesFolder( extension = extension, ) - if (disableCache || !checkIfOutputAlreadyExists(recordingStart, extension)) { - val filePaths = getBatchesForFFmpeg() - - for (parameter in ffmpegParameters) { - Log.i("Concatenation", "Trying parameter $parameter") - onNextParameterTry(parameter) - - try { - concatenateFunction( - filePaths, - outputFile, - parameter, - ).await() - return outputFile - } catch (e: MediaConverter.FFmpegException) { - continue - } + if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) { + return outputFile + } + + val filePaths = getBatchesForFFmpeg() + + for (parameter in ffmpegParameters) { + Log.i("Concatenation", "Trying parameter $parameter") + onNextParameterTry(parameter) + + try { + concatenateFunction( + filePaths, + outputFile, + parameter, + ).await() + return outputFile + } catch (e: MediaConverter.FFmpegException) { + continue } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt index 0862f1a91..877d0635d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt @@ -132,11 +132,19 @@ fun RecorderEventsHandler( // When recording is loaded from lastRecording ?: settings.lastRecording ?: throw Exception("No recording information available") - val batchesFolder = - if (recorder.javaClass == AudioRecorderModel::class.java) - AudioBatchesFolder.importFromFolder(recording.folderPath, context) - else - VideoBatchesFolder.importFromFolder(recording.folderPath, context) + val batchesFolder = when (recorder.javaClass) { + AudioRecorderModel::class.java -> AudioBatchesFolder.importFromFolder( + recording.folderPath, + context + ) + + VideoRecorderModel::class.java -> VideoBatchesFolder.importFromFolder( + recording.folderPath, + context + ) + + else -> throw Exception("Unknown recorder type") + } batchesFolder.concatenate( recording.recordingStart, @@ -151,20 +159,24 @@ fun RecorderEventsHandler( when (batchesFolder.type) { BatchesFolder.BatchType.INTERNAL -> { - if (batchesFolder is AudioBatchesFolder) { - saveAudioFile( - batchesFolder.asInternalGetOutputFile( - recording.recordingStart, - recording.fileExtension, - ), name - ) - } else if (batchesFolder is VideoBatchesFolder) { - saveVideoFile( - batchesFolder.asInternalGetOutputFile( - recording.recordingStart, - recording.fileExtension, - ), name - ) + when (batchesFolder) { + is AudioBatchesFolder -> { + saveAudioFile( + batchesFolder.asInternalGetOutputFile( + recording.recordingStart, + recording.fileExtension, + ), name + ) + } + + is VideoBatchesFolder -> { + saveVideoFile( + batchesFolder.asInternalGetOutputFile( + recording.recordingStart, + recording.fileExtension, + ), name + ) + } } } @@ -186,17 +198,21 @@ fun RecorderEventsHandler( // Register audio recorder events DisposableEffect(key1 = audioRecorder, key2 = settings) { - audioRecorder.onRecordingSave = { + audioRecorder.onRecordingSave = { justSave -> scope.launch { - audioRecorder.stopRecording(context) + if (justSave) { + saveRecording(audioRecorder as RecorderModel) + } else { + audioRecorder.stopRecording(context) - kotlin.runCatching { - saveAsLastRecording(audioRecorder as RecorderModel) + runCatching { + saveAsLastRecording(audioRecorder as RecorderModel) - saveRecording(audioRecorder) - } + saveRecording(audioRecorder) + } - audioRecorder.destroyService(context) + audioRecorder.destroyService(context) + } } } audioRecorder.onError = { @@ -215,17 +231,21 @@ fun RecorderEventsHandler( // Register video recorder events DisposableEffect(key1 = videoRecorder, key2 = settings) { - videoRecorder.onRecordingSave = { + videoRecorder.onRecordingSave = { justSave -> scope.launch { - videoRecorder.stopRecording(context) + if (justSave) { + saveRecording(videoRecorder as RecorderModel) + } else { + videoRecorder.stopRecording(context) - kotlin.runCatching { - saveAsLastRecording(videoRecorder as RecorderModel) + runCatching { + saveAsLastRecording(videoRecorder as RecorderModel) - saveRecording(videoRecorder) - } + saveRecording(videoRecorder) + } - videoRecorder.destroyService(context) + videoRecorder.destroyService(context) + } } } videoRecorder.onError = { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index 0233368e8..13b66a37d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -92,7 +92,7 @@ fun AudioRecordingStatus( } }, onSave = { - audioRecorder.onRecordingSave() + audioRecorder.onRecordingSave(false) } ) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index 1e9d4cf95..2cd2fd2c8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -169,7 +169,7 @@ fun VideoRecordingStatus( } }, onSave = { - videoRecorder.onRecordingSave() + videoRecorder.onRecordingSave(false) } ) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index ce7b3dcde..a33d6e884 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -40,7 +40,9 @@ abstract class BaseRecorderModel(null) protected set - var onRecordingSave: () -> Unit = {} + // If `isSavingAsOldRecording` is true, the user is saving an old recording, + // thus the service is not running and thus doesn't need to be stopped or destroyed + var onRecordingSave: (isSavingAsOldRecording: Boolean) -> Unit = {} var onError: () -> Unit = {} abstract var batchesFolder: B @@ -132,8 +134,6 @@ abstract class BaseRecorderModel + audioRecorder.onRecordingSave(true) + + RecordingInformation.Type.VIDEO -> + videoRecorder.onRecordingSave(true) + } }, showAudioRecorder = topBarVisible, onHideTopBar = { From f462d5ff50f5cfbd52798dbc413c3992495d5b57 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 15 Dec 2023 21:30:08 +0100 Subject: [PATCH 069/176] feat: Add Alibi icon next to explanation --- .../organisms/StartRecording.kt | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt index a1980fcfb..63bc8ebc5 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt @@ -1,8 +1,10 @@ package app.myzel394.alibi.ui.components.RecorderScreen.organisms +import androidx.compose.foundation.Image 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.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -11,6 +13,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Save import androidx.compose.material3.Button @@ -21,7 +24,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics @@ -108,19 +114,31 @@ fun StartRecording( Text(label) } } else { - Text( - stringResource( - R.string.ui_audioRecorder_action_start_description, - appSettings.maxDuration / 1000 / 60 - ), - style = MaterialTheme.typography.bodySmall.copy( - color = MaterialTheme.colorScheme.onSurfaceVariant, - ), - modifier = Modifier - .widthIn(max = 300.dp) - .fillMaxWidth(), - textAlign = TextAlign.Center, - ) + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + painter = painterResource(R.drawable.launcher_monochrome_noopacity), + contentDescription = null, + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant), + modifier = Modifier + .size(24.dp) + ) + + Text( + stringResource( + R.string.ui_audioRecorder_action_start_description, + appSettings.maxDuration / 1000 / 60 + ), + style = MaterialTheme.typography.bodySmall.copy( + color = MaterialTheme.colorScheme.onSurfaceVariant, + ), + modifier = Modifier + .widthIn(max = 300.dp) + .fillMaxWidth(), + textAlign = TextAlign.Center, + ) + } } } } From 39895bcd40db89d5ebcb0f284d33575ed7c92d29 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 15 Dec 2023 22:05:04 +0100 Subject: [PATCH 070/176] feat: Add QuickMaxDurationSelector; closes #57 --- .../molecules/QuickMaxDurationSelector.kt | 95 +++++++++++++++++++ .../organisms/StartRecording.kt | 64 ++++++++++--- app/src/main/res/values/strings.xml | 7 +- 3 files changed, 153 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt new file mode 100644 index 000000000..7330ec1bf --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt @@ -0,0 +1,95 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.molecules + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import app.myzel394.alibi.R +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.db.AudioRecorderSettings.Companion.EXAMPLE_MAX_DURATIONS +import app.myzel394.alibi.ui.utils.formatDuration +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun QuickMaxDurationSelector( + settings: AppSettings, + onDismiss: () -> Unit, +) { + val scope = rememberCoroutineScope() + val dataStore = LocalContext.current.dataStore + + val sheetState = rememberModalBottomSheetState(true) + + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = sheetState, + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth() + ) { + Box( + modifier = Modifier + .widthIn(max = 400.dp) + .padding(16.dp), + ) { + Text( + stringResource(R.string.ui_recorder_action_changeMaxDuration_title), + style = MaterialTheme.typography.headlineMedium, + textAlign = TextAlign.Center, + ) + } + + Column { + for (duration in EXAMPLE_MAX_DURATIONS) { + Button( + onClick = { + scope.launch { + // TODO: Add hide to microphone selection + sheetState.hide() + onDismiss() + } + scope.launch { + dataStore.updateData { + it.setMaxDuration(duration) + } + } + }, + colors = ButtonDefaults.textButtonColors(), + modifier = Modifier + .fillMaxWidth() + .height(64.dp), + ) { + Text( + formatDuration(duration) + ) + } + } + } + + Spacer(modifier = Modifier.height(32.dp)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt index 63bc8ebc5..c2a9d6205 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt @@ -2,7 +2,6 @@ package app.myzel394.alibi.ui.components.RecorderScreen.organisms import androidx.compose.foundation.Image 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.Spacer @@ -13,7 +12,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.verticalScroll +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.ClickableText import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Save import androidx.compose.material3.Button @@ -22,21 +22,28 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE import app.myzel394.alibi.ui.components.RecorderScreen.molecules.AudioRecordingStart +import app.myzel394.alibi.ui.components.RecorderScreen.molecules.QuickMaxDurationSelector import app.myzel394.alibi.ui.components.RecorderScreen.molecules.VideoRecordingStart import app.myzel394.alibi.ui.effects.rememberForceUpdateOnLifeCycleChange import app.myzel394.alibi.ui.models.AudioRecorderModel @@ -125,18 +132,51 @@ fun StartRecording( .size(24.dp) ) - Text( - stringResource( - R.string.ui_audioRecorder_action_start_description, - appSettings.maxDuration / 1000 / 60 - ), - style = MaterialTheme.typography.bodySmall.copy( - color = MaterialTheme.colorScheme.onSurfaceVariant, - ), + val label = stringResource( + R.string.ui_audioRecorder_action_start_description_2, + appSettings.maxDuration / 1000 / 60 + ) + val annotatedDescription = buildAnnotatedString { + append(stringResource(R.string.ui_audioRecorder_action_start_description_1)) + + withStyle(SpanStyle(background = MaterialTheme.colorScheme.surfaceVariant)) { + pushStringAnnotation( + tag = "minutes", + annotation = label, + ) + append(label) + } + + append(stringResource(R.string.ui_audioRecorder_action_start_description_3)) + } + + var showQuickMaxDurationSelector by rememberSaveable { + mutableStateOf(false) + } + + if (showQuickMaxDurationSelector) { + QuickMaxDurationSelector( + settings = appSettings, + onDismiss = { + showQuickMaxDurationSelector = false + }, + ) + } + ClickableText( + text = annotatedDescription, + onClick = { textIndex -> + if (annotatedDescription.getStringAnnotations(textIndex, textIndex) + .firstOrNull()?.tag == "minutes" + ) { + showQuickMaxDurationSelector = true + } + }, modifier = Modifier .widthIn(max = 300.dp) .fillMaxWidth(), - textAlign = TextAlign.Center, + style = MaterialTheme.typography.bodySmall.copy( + color = MaterialTheme.colorScheme.onSurfaceVariant, + ), ) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1c17b1278..12ccdd6ef 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,7 +26,11 @@ Pause Resume Save Recording - Alibi will continue recording in the background and store the last %s minutes at your request + + Alibi will continue recording in the background and store the last\u0020 + %s minutes + \u0020at your request + Processing Processing your recording, do not close Alibi! You will automatically be prompted to save the file once it\'s ready @@ -151,4 +155,5 @@ Disable Torch Enable Torch Press long for configuration + When stopped, save the last... \ No newline at end of file From 101f8d46e91c2ba8dfaf3a3473a2c6a1e1628776 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 15 Dec 2023 22:14:48 +0100 Subject: [PATCH 071/176] fix: Fix sheets coming and leaving --- .../molecules/MicrophoneSelection.kt | 25 +++++++++++++------ .../molecules/QuickMaxDurationSelector.kt | 1 - .../organisms/StartRecording.kt | 1 - 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt index 9250e0722..2c45de87f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -41,6 +42,7 @@ import app.myzel394.alibi.ui.components.atoms.MessageBox import app.myzel394.alibi.ui.components.atoms.MessageType import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.utils.MicrophoneInfo +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -73,11 +75,17 @@ fun MicrophoneSelection( visibleMicrophones } + val scope = rememberCoroutineScope() + fun hideSheet() { + scope.launch { + sheetState.hide() + showSelection = false + } + } + if (showSelection) { ModalBottomSheet( - onDismissRequest = { - showSelection = false - }, + onDismissRequest = ::hideSheet, sheetState = sheetState, ) { Column( @@ -114,7 +122,7 @@ fun MicrophoneSelection( selectedAsFallback = isTryingToReconnect, onSelect = { audioRecorder.changeMicrophone(null) - showSelection = false + hideSheet() } ) } @@ -128,7 +136,7 @@ fun MicrophoneSelection( disabled = isTryingToReconnect && microphone == audioRecorder.selectedMicrophone, onSelect = { audioRecorder.changeMicrophone(microphone) - showSelection = false + hideSheet() } ) } @@ -165,7 +173,7 @@ fun MicrophoneSelection( selected = audioRecorder.selectedMicrophone == microphone, onSelect = { audioRecorder.changeMicrophone(microphone) - showSelection = false + hideSheet() } ) } @@ -181,7 +189,10 @@ fun MicrophoneSelection( if (shownMicrophones.isNotEmpty() || (settings.audioRecorderSettings.showAllMicrophones && hiddenMicrophones.isNotEmpty())) { Button( onClick = { - showSelection = true + scope.launch { + showSelection = true + sheetState.show() + } }, colors = ButtonDefaults.textButtonColors(), ) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt index 7330ec1bf..148170b14 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt @@ -34,7 +34,6 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable fun QuickMaxDurationSelector( - settings: AppSettings, onDismiss: () -> Unit, ) { val scope = rememberCoroutineScope() diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt index c2a9d6205..f71dfe96a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt @@ -156,7 +156,6 @@ fun StartRecording( if (showQuickMaxDurationSelector) { QuickMaxDurationSelector( - settings = appSettings, onDismiss = { showQuickMaxDurationSelector = false }, From 3aa4caf9ed4c9fc280aec3a5ac2e64b846c67de3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 15 Dec 2023 22:17:15 +0100 Subject: [PATCH 072/176] fix: Add some spacing --- .../organisms/StartRecording.kt | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt index f71dfe96a..f29b04938 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt @@ -66,6 +66,36 @@ fun StartRecording( ) { val context = LocalContext.current + val label = stringResource( + R.string.ui_audioRecorder_action_start_description_2, + appSettings.maxDuration / 1000 / 60 + ) + val annotatedDescription = buildAnnotatedString { + append(stringResource(R.string.ui_audioRecorder_action_start_description_1)) + + withStyle(SpanStyle(background = MaterialTheme.colorScheme.surfaceVariant)) { + pushStringAnnotation( + tag = "minutes", + annotation = label, + ) + append(label) + } + + append(stringResource(R.string.ui_audioRecorder_action_start_description_3)) + } + + var showQuickMaxDurationSelector by rememberSaveable { + mutableStateOf(false) + } + + if (showQuickMaxDurationSelector) { + QuickMaxDurationSelector( + onDismiss = { + showQuickMaxDurationSelector = false + }, + ) + } + Column( modifier = Modifier .fillMaxSize() @@ -129,38 +159,11 @@ fun StartRecording( contentDescription = null, colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant), modifier = Modifier - .size(24.dp) - ) - - val label = stringResource( - R.string.ui_audioRecorder_action_start_description_2, - appSettings.maxDuration / 1000 / 60 + .size(ButtonDefaults.IconSize) ) - val annotatedDescription = buildAnnotatedString { - append(stringResource(R.string.ui_audioRecorder_action_start_description_1)) - - withStyle(SpanStyle(background = MaterialTheme.colorScheme.surfaceVariant)) { - pushStringAnnotation( - tag = "minutes", - annotation = label, - ) - append(label) - } - append(stringResource(R.string.ui_audioRecorder_action_start_description_3)) - } - - var showQuickMaxDurationSelector by rememberSaveable { - mutableStateOf(false) - } + Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) - if (showQuickMaxDurationSelector) { - QuickMaxDurationSelector( - onDismiss = { - showQuickMaxDurationSelector = false - }, - ) - } ClickableText( text = annotatedDescription, onClick = { textIndex -> From 750f6dc21297d156b93f6e577a4dca0679f9db7e Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 10:55:52 +0100 Subject: [PATCH 073/176] fix: Use onSurface color --- .../RecorderScreen/molecules/QuickMaxDurationSelector.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt index 148170b14..4d7695e56 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt @@ -66,7 +66,6 @@ fun QuickMaxDurationSelector( Button( onClick = { scope.launch { - // TODO: Add hide to microphone selection sheetState.hide() onDismiss() } @@ -76,14 +75,14 @@ fun QuickMaxDurationSelector( } } }, - colors = ButtonDefaults.textButtonColors(), + colors = ButtonDefaults.textButtonColors( + contentColor = MaterialTheme.colorScheme.onSurface, + ), modifier = Modifier .fillMaxWidth() .height(64.dp), ) { - Text( - formatDuration(duration) - ) + Text(formatDuration(duration)) } } } From b6346ef0e362722c757c3562490318434d4cb75f Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 11:20:12 +0100 Subject: [PATCH 074/176] feat: Add start time info --- .../services/RecorderNotificationHelper.kt | 2 +- .../alibi/services/RecorderService.kt | 6 +-- .../RecorderScreen/atoms/RecordingProgress.kt | 2 +- .../RecorderScreen/atoms/RecordingTime.kt | 2 +- .../molecules/RecordingStatus.kt | 52 ++++++++++++++++++- .../organisms/AudioRecordingStatus.kt | 11 ++-- .../organisms/VideoRecordingStatus.kt | 2 + .../alibi/ui/models/BaseRecorderModel.kt | 8 ++- .../java/app/myzel394/alibi/ui/utils/date.kt | 8 +++ app/src/main/res/values/strings.xml | 3 ++ 10 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/utils/date.kt diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt index 2464083a5..9ff5c99de 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt @@ -105,7 +105,7 @@ data class RecorderNotificationHelper( Date.from( Calendar .getInstance() - .also { it.add(Calendar.MILLISECOND, -recordingTime.toInt()) } + .also { it.add(Calendar.SECOND, -recordingTime.toInt()) } .toInstant() ).time, ) diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index db2fa7b76..0330800b3 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -122,12 +122,12 @@ abstract class RecorderService : LifecycleService() { recordingTimeTimer = Executors.newSingleThreadScheduledExecutor().also { it.scheduleAtFixedRate( { - recordingTime += 1000 + recordingTime += 1 onRecordingTimeChange?.invoke(recordingTime) }, 0, - 1000, - TimeUnit.MILLISECONDS + 1, + TimeUnit.SECONDS ) } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt index dcf659a87..75dd23e15 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt @@ -20,7 +20,7 @@ fun RecordingProgress( progress: Float, ) { // Only show animation when the recording has just started - val recordingJustStarted = recordingTime <= 1000L + val recordingJustStarted = recordingTime <= 1L var progressVisible by remember { mutableStateOf(!recordingJustStarted) } LaunchedEffect(Unit) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingTime.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingTime.kt index 8bf5e1680..236f6d188 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingTime.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingTime.kt @@ -37,7 +37,7 @@ fun RecordingTime( } Spacer(modifier = Modifier.width(16.dp)) Text( - text = formatDuration(time), + text = formatDuration(time * 1000), style = MaterialTheme.typography.headlineLarge, ) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt index 8cc73427c..5f1723a39 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt @@ -1,28 +1,78 @@ package app.myzel394.alibi.ui.components.RecorderScreen.molecules +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import app.myzel394.alibi.R import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingProgress import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingTime +import app.myzel394.alibi.ui.utils.isSameDay +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle +import kotlin.math.min @Composable fun RecordingStatus( recordingTime: Long, progress: Float, + recordingStart: LocalDateTime, + maxDuration: Long, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp), ) { RecordingTime(recordingTime) - Spacer(modifier = Modifier.height(16.dp)) RecordingProgress( recordingTime = recordingTime, progress = progress, ) + + Text( + text = stringResource( + R.string.ui_recorder_info_saveNowTime, + DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT).format( + LocalDateTime.now().minusSeconds( + min( + maxDuration / 1000, + recordingTime + ) + ) + ) + ), + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Center, + ) + + Text( + text = recordingStart.let { + if (isSameDay(it, LocalDateTime.now())) { + stringResource( + R.string.ui_recorder_info_startTime_short, + DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) + .format(it) + ) + } else { + stringResource( + R.string.ui_recorder_info_startTime_full, + DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT) + .format(it) + ) + } + }, + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index 13b66a37d..1fbebc054 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width +import androidx.compose.material3.Divider import androidx.compose.material3.LinearProgressIndicator import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -67,10 +68,16 @@ fun AudioRecordingStatus( RealtimeAudioVisualizer(audioRecorder = audioRecorder) RecordingStatus( - recordingTime = audioRecorder.recordingTime!!, + recordingTime = audioRecorder.recordingTime, progress = audioRecorder.progress, + recordingStart = audioRecorder.recordingStart, + maxDuration = audioRecorder.settings.maxDuration, ) + MicrophoneStatus(audioRecorder) + + Divider() + RecordingControl( isPaused = audioRecorder.isPaused, onDelete = { @@ -95,7 +102,5 @@ fun AudioRecordingStatus( audioRecorder.onRecordingSave(false) } ) - - MicrophoneStatus(audioRecorder) } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index 2cd2fd2c8..95dfa744d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -120,6 +120,8 @@ fun VideoRecordingStatus( RecordingStatus( recordingTime = videoRecorder.recordingTime, progress = videoRecorder.progress, + recordingStart = videoRecorder.recordingStart, + maxDuration = videoRecorder.settings.maxDuration, ) Column( diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index a33d6e884..28de54d7c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -35,11 +35,14 @@ abstract class BaseRecorderModel(null) protected set + val recordingStart + get() = recorderService!!.recordingStart + // If `isSavingAsOldRecording` is true, the user is saving an old recording, // thus the service is not running and thus doesn't need to be stopped or destroyed var onRecordingSave: (isSavingAsOldRecording: Boolean) -> Unit = {} @@ -48,7 +51,8 @@ abstract class BaseRecorderModelEnable Torch Press long for configuration When stopped, save the last... + Recording started at %s + Recording started %s + Saving now will save until %s \ No newline at end of file From 096cf56436982253125eb8431d96c9d067a482f3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 12:48:00 +0100 Subject: [PATCH 075/176] feat: Add more animations to recording start --- .../RecorderScreen/atoms/RecordingProgress.kt | 42 ------ .../RecorderScreen/atoms/RecordingTime.kt | 44 ------ .../molecules/RecordingControl.kt | 80 ++++++++++- .../molecules/RecordingStatus.kt | 133 ++++++++++++------ .../organisms/AudioRecordingStatus.kt | 16 +-- .../organisms/VideoRecordingStatus.kt | 19 +-- .../app/myzel394/alibi/ui/utils/animations.kt | 22 +++ .../java/app/myzel394/alibi/ui/utils/stack.kt | 40 ++++++ 8 files changed, 233 insertions(+), 163 deletions(-) delete mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt delete mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingTime.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/utils/animations.kt create mode 100644 app/src/main/java/app/myzel394/alibi/ui/utils/stack.kt diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt deleted file mode 100644 index 75dd23e15..000000000 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingProgress.kt +++ /dev/null @@ -1,42 +0,0 @@ -package app.myzel394.alibi.ui.components.RecorderScreen.atoms - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.tween -import androidx.compose.animation.expandHorizontally -import androidx.compose.foundation.layout.width -import androidx.compose.material3.LinearProgressIndicator -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp - -@Composable -fun RecordingProgress( - recordingTime: Long, - progress: Float, -) { - // Only show animation when the recording has just started - val recordingJustStarted = recordingTime <= 1L - var progressVisible by remember { mutableStateOf(!recordingJustStarted) } - - LaunchedEffect(Unit) { - progressVisible = true - } - - AnimatedVisibility( - visible = progressVisible, - enter = expandHorizontally( - tween(1000) - ) - ) { - LinearProgressIndicator( - progress = progress, - modifier = Modifier - .width(300.dp) - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingTime.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingTime.kt deleted file mode 100644 index 236f6d188..000000000 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecordingTime.kt +++ /dev/null @@ -1,44 +0,0 @@ -package app.myzel394.alibi.ui.components.RecorderScreen.atoms - -import androidx.compose.foundation.background -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.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import app.myzel394.alibi.ui.components.atoms.Pulsating -import app.myzel394.alibi.ui.utils.formatDuration - -@Composable -fun RecordingTime( - time: Long, -) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center, - ) { - Pulsating { - Box( - modifier = Modifier - .size(16.dp) - .clip(CircleShape) - .background(Color.Red) - ) - } - Spacer(modifier = Modifier.width(16.dp)) - Text( - text = formatDuration(time * 1000), - style = MaterialTheme.typography.headlineLarge, - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt index e6d3ac20a..b01d97ab6 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt @@ -1,30 +1,103 @@ package app.myzel394.alibi.ui.components.RecorderScreen.molecules +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.EaseOutElastic +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.spring +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import app.myzel394.alibi.ui.components.RecorderScreen.atoms.DeleteButton import app.myzel394.alibi.ui.components.RecorderScreen.atoms.PauseResumeButton import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveButton +import app.myzel394.alibi.ui.utils.RandomStack +import app.myzel394.alibi.ui.utils.rememberInitialRecordingAnimation +import kotlinx.coroutines.delay import kotlinx.coroutines.launch @Composable fun RecordingControl( isPaused: Boolean, + recordingTime: Long, onDelete: () -> Unit, onPauseResume: () -> Unit, onSave: () -> Unit, ) { + val animateIn = rememberInitialRecordingAnimation(recordingTime) + + var deleteButtonAlphaIsIn by rememberSaveable { + mutableStateOf(false) + } + val deleteButtonAlpha by animateFloatAsState( + if (deleteButtonAlphaIsIn) 1f else 0f, + label = "deleteButtonAlpha", + animationSpec = tween(durationMillis = 500) + ) + + var pauseButtonAlphaIsIn by rememberSaveable { + mutableStateOf(false) + } + val pauseButtonAlpha by animateFloatAsState( + if (pauseButtonAlphaIsIn) 1f else 0f, + label = "pauseButtonAlpha", + animationSpec = tween(durationMillis = 500) + ) + + var saveButtonAlphaIsIn by rememberSaveable { + mutableStateOf(false) + } + val saveButtonAlpha by animateFloatAsState( + if (saveButtonAlphaIsIn) 1f else 0f, + label = "saveButtonAlpha", + animationSpec = tween(durationMillis = 500) + ) + + LaunchedEffect(animateIn) { + if (animateIn) { + val stack = RandomStack.of(arrayOf(1, 2, 3).asIterable()) + + while (!stack.isEmpty()) { + val next = stack.popRandom() + when (next) { + 1 -> { + deleteButtonAlphaIsIn = true + } + + 2 -> { + pauseButtonAlphaIsIn = true + } + + 3 -> { + saveButtonAlphaIsIn = true + } + } + + delay(250) + } + } + } + Row( verticalAlignment = Alignment.CenterVertically, ) { Box( modifier = Modifier .fillMaxWidth() - .weight(1f), + .weight(1f) + .alpha(deleteButtonAlpha), contentAlignment = Alignment.Center, ) { DeleteButton(onDelete = onDelete) @@ -32,6 +105,8 @@ fun RecordingControl( Box( contentAlignment = Alignment.Center, + modifier = Modifier + .alpha(pauseButtonAlpha), ) { PauseResumeButton( isPaused = isPaused, @@ -42,7 +117,8 @@ fun RecordingControl( Box( modifier = Modifier .fillMaxWidth() - .weight(1f), + .weight(1f) + .alpha(saveButtonAlpha), contentAlignment = Alignment.Center, ) { SaveButton(onSave = onSave) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt index 5f1723a39..b170d6195 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingStatus.kt @@ -1,21 +1,34 @@ package app.myzel394.alibi.ui.components.RecorderScreen.molecules +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween +import androidx.compose.animation.expandHorizontally +import androidx.compose.animation.fadeIn +import androidx.compose.foundation.background 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.Spacer -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import app.myzel394.alibi.R -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingProgress -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingTime +import app.myzel394.alibi.ui.components.atoms.Pulsating +import app.myzel394.alibi.ui.utils.formatDuration import app.myzel394.alibi.ui.utils.isSameDay +import app.myzel394.alibi.ui.utils.rememberInitialRecordingAnimation import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.time.format.FormatStyle @@ -28,51 +41,87 @@ fun RecordingStatus( recordingStart: LocalDateTime, maxDuration: Long, ) { + val animateIn = rememberInitialRecordingAnimation(recordingTime) + Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(16.dp), ) { - RecordingTime(recordingTime) - RecordingProgress( - recordingTime = recordingTime, - progress = progress, - ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + ) { + Pulsating { + Box( + modifier = Modifier + .size(16.dp) + .clip(CircleShape) + .background(Color.Red) + ) + } + Spacer(modifier = Modifier.width(16.dp)) + Text( + text = formatDuration(recordingTime * 1000), + style = MaterialTheme.typography.headlineLarge, + ) + } + + AnimatedVisibility( + visible = animateIn, + enter = expandHorizontally( + tween(1000) + ) + ) { + LinearProgressIndicator( + progress = progress, + modifier = Modifier + .width(300.dp) + ) + } - Text( - text = stringResource( - R.string.ui_recorder_info_saveNowTime, - DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT).format( - LocalDateTime.now().minusSeconds( - min( - maxDuration / 1000, - recordingTime + AnimatedVisibility(visible = animateIn, enter = fadeIn()) { + Text( + text = stringResource( + R.string.ui_recorder_info_saveNowTime, + DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT) + .format( + LocalDateTime.now().minusSeconds( + min( + maxDuration / 1000, + recordingTime + ) + ) ) - ) - ) - ), - style = MaterialTheme.typography.bodySmall, - textAlign = TextAlign.Center, - ) + ), + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Center, + ) + } - Text( - text = recordingStart.let { - if (isSameDay(it, LocalDateTime.now())) { - stringResource( - R.string.ui_recorder_info_startTime_short, - DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) - .format(it) - ) - } else { - stringResource( - R.string.ui_recorder_info_startTime_full, - DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT) - .format(it) - ) - } - }, - style = MaterialTheme.typography.bodySmall, - textAlign = TextAlign.Center, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) + AnimatedVisibility(visible = animateIn, enter = fadeIn()) { + Text( + text = recordingStart.let { + if (isSameDay(it, LocalDateTime.now())) { + stringResource( + R.string.ui_recorder_info_startTime_short, + DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) + .format(it) + ) + } else { + stringResource( + R.string.ui_recorder_info_startTime_full, + DateTimeFormatter.ofLocalizedDateTime( + FormatStyle.MEDIUM, + FormatStyle.SHORT + ) + .format(it) + ) + } + }, + style = MaterialTheme.typography.bodySmall, + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index 1fbebc054..874dd610a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -1,19 +1,10 @@ package app.myzel394.alibi.ui.components.RecorderScreen.organisms -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.tween -import androidx.compose.animation.expandHorizontally 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.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width import androidx.compose.material3.Divider -import androidx.compose.material3.LinearProgressIndicator import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -24,13 +15,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.dp -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.DeleteButton -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.PauseResumeButton import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RealtimeAudioVisualizer -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingProgress -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingTime -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveButton import app.myzel394.alibi.ui.components.RecorderScreen.molecules.MicrophoneStatus import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingControl import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingStatus @@ -80,6 +65,7 @@ fun AudioRecordingStatus( RecordingControl( isPaused = audioRecorder.isPaused, + recordingTime = audioRecorder.recordingTime, onDelete = { scope.launch { runCatching { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index 95dfa744d..cea2cdd7a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -1,16 +1,10 @@ package app.myzel394.alibi.ui.components.RecorderScreen.organisms -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.tween -import androidx.compose.animation.expandHorizontally import androidx.compose.foundation.background 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.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.layout.size @@ -19,29 +13,17 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material3.Divider import androidx.compose.material3.Icon -import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.core.util.TypedValueCompat import app.myzel394.alibi.R -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.DeleteButton -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.PauseResumeButton -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingProgress -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecordingTime -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveButton import app.myzel394.alibi.ui.components.RecorderScreen.atoms.TorchStatus import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingControl import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingStatus @@ -152,6 +134,7 @@ fun VideoRecordingStatus( RecordingControl( isPaused = videoRecorder.isPaused, + recordingTime = videoRecorder.recordingTime, onDelete = { scope.launch { runCatching { diff --git a/app/src/main/java/app/myzel394/alibi/ui/utils/animations.kt b/app/src/main/java/app/myzel394/alibi/ui/utils/animations.kt new file mode 100644 index 000000000..08b875920 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/utils/animations.kt @@ -0,0 +1,22 @@ +package app.myzel394.alibi.ui.utils + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue + +@Composable +fun rememberInitialRecordingAnimation(recordingTime: Long): Boolean { + // Only show animation when the recording has just started + val recordingJustStarted = recordingTime <= 1L + var progressVisible by rememberSaveable { mutableStateOf(!recordingJustStarted) } + + LaunchedEffect(Unit) { + progressVisible = true + } + + return progressVisible +} diff --git a/app/src/main/java/app/myzel394/alibi/ui/utils/stack.kt b/app/src/main/java/app/myzel394/alibi/ui/utils/stack.kt new file mode 100644 index 000000000..1b30fd249 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/utils/stack.kt @@ -0,0 +1,40 @@ +package app.myzel394.alibi.ui.utils + +// A stack that allows you to randomly pop items from it +class RandomStack { + private val stack = mutableListOf() + + fun push(item: T) { + stack.add(item) + } + + fun pop(): T { + val index = stack.size - 1 + val item = stack[index] + + stack.removeAt(index) + + return item + } + + fun popRandom(): T { + val index = (0.. of(items: Iterable): RandomStack { + val stack = RandomStack() + + items.forEach(stack::push) + + return stack + } + } +} \ No newline at end of file From 459a0b18bac81fe294ff745dea8c54d2b3894344 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 21:07:12 +0100 Subject: [PATCH 076/176] feat: Add permission check --- .../VideoRecorderPreparationSheet.kt | 64 +++++++++++-------- .../molecules/VideoRecordingStart.kt | 14 +++- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index 3ad5961ee..062a8abd1 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -170,34 +170,42 @@ fun VideoRecorderPreparationSheet( verticalArrangement = Arrangement.spacedBy(8.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { - Row( - modifier = Modifier - .fillMaxWidth() - .height(BIG_PRIMARY_BUTTON_SIZE) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primary) - .padding(16.dp) - .semantics { - contentDescription = label - } - .pointerInput(Unit) { - detectTapGestures( - onLongPress = { - onPreviewVisible() - }, - onTap = { - onStartRecording() - } - ) - }, - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - label, - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onPrimary, - ) + PermissionRequester( + permission = Manifest.permission.CAMERA, + icon = Icons.Default.CameraAlt, + onPermissionAvailable = { + onStartRecording() + } + ) { trigger -> + Row( + modifier = Modifier + .fillMaxWidth() + .height(BIG_PRIMARY_BUTTON_SIZE) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.primary) + .padding(16.dp) + .semantics { + contentDescription = label + } + .pointerInput(Unit) { + detectTapGestures( + onLongPress = { + onPreviewVisible() + }, + onTap = { + trigger() + } + ) + }, + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + label, + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onPrimary, + ) + } } Text( stringResource( diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt index 883e8420f..60b529fe3 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt @@ -46,6 +46,7 @@ import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.models.VideoRecorderModel +import app.myzel394.alibi.ui.utils.PermissionHelper @OptIn(ExperimentalFoundationApi::class) @Composable @@ -97,7 +98,18 @@ fun VideoRecordingStart( interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple(color = MaterialTheme.colorScheme.primary), onClick = { - videoRecorder.startRecording(context, appSettings) + if (PermissionHelper.hasGranted( + context, + Manifest.permission.CAMERA + ) && PermissionHelper.hasGranted( + context, + Manifest.permission.RECORD_AUDIO + ) + ) { + videoRecorder.startRecording(context, appSettings) + } else { + showSheet = true + } }, onLongClick = { showSheet = true From b5ae6a735d81718d82dd6467363672c18b056a9c Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 21:07:28 +0100 Subject: [PATCH 077/176] fix: Add missing permissions and optional uses-feature --- app/src/main/AndroidManifest.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1e5db5d11..3d59dbe82 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,19 +2,23 @@ + + - + From a0640d13ab34bdcfad351309c41af9d242c61c23 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 21:07:41 +0100 Subject: [PATCH 078/176] fix: Inline variable --- .../ui/components/RecorderScreen/molecules/RecordingControl.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt index b01d97ab6..822027ef9 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt @@ -70,8 +70,7 @@ fun RecordingControl( val stack = RandomStack.of(arrayOf(1, 2, 3).asIterable()) while (!stack.isEmpty()) { - val next = stack.popRandom() - when (next) { + when (stack.popRandom()) { 1 -> { deleteButtonAlphaIsIn = true } From ff8ea3e1f270e785f4db34b7a3fe07fee5509860 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 21:15:00 +0100 Subject: [PATCH 079/176] feat: Add video recorder starting info --- .../organisms/VideoRecordingStatus.kt | 20 +++++++++++++------ .../app/myzel394/alibi/ui/utils/animations.kt | 2 +- app/src/main/res/values/strings.xml | 1 + 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index cea2cdd7a..61d0f689c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Divider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -99,12 +100,19 @@ fun VideoRecordingStatus( } } - RecordingStatus( - recordingTime = videoRecorder.recordingTime, - progress = videoRecorder.progress, - recordingStart = videoRecorder.recordingStart, - maxDuration = videoRecorder.settings.maxDuration, - ) + if (videoRecorder.isStartingRecording) { + Text( + stringResource(R.string.ui_videoRecorder_info_starting), + style = MaterialTheme.typography.labelMedium, + ) + } else { + RecordingStatus( + recordingTime = videoRecorder.recordingTime, + progress = videoRecorder.progress, + recordingStart = videoRecorder.recordingStart, + maxDuration = videoRecorder.settings.maxDuration, + ) + } Column( verticalArrangement = Arrangement diff --git a/app/src/main/java/app/myzel394/alibi/ui/utils/animations.kt b/app/src/main/java/app/myzel394/alibi/ui/utils/animations.kt index 08b875920..693cf766d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/utils/animations.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/utils/animations.kt @@ -11,7 +11,7 @@ import androidx.compose.runtime.setValue @Composable fun rememberInitialRecordingAnimation(recordingTime: Long): Boolean { // Only show animation when the recording has just started - val recordingJustStarted = recordingTime <= 1L + val recordingJustStarted = recordingTime <= 3L var progressVisible by rememberSaveable { mutableStateOf(!recordingJustStarted) } LaunchedEffect(Unit) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 63bea5988..82ea4f6d4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -159,4 +159,5 @@ Recording started at %s Recording started %s Saving now will save until %s + Video Recorder is starting... \ No newline at end of file From cda0b7f1957cdd2745cacb95fe8e3406f3953423 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 22:13:38 +0100 Subject: [PATCH 080/176] fix: Properly reconnect audio on app focus --- .../java/app/myzel394/alibi/db/AppSettings.kt | 18 +++++ .../alibi/services/AudioRecorderService.kt | 61 ++++------------- .../alibi/services/IntervalRecorderService.kt | 5 +- .../alibi/services/VideoRecorderService.kt | 66 ++++++------------- .../atoms/RecorderEventsHandler.kt | 3 +- .../organisms/AudioRecordingStatus.kt | 2 +- .../organisms/VideoRecordingStatus.kt | 2 +- .../alibi/ui/models/AudioRecorderModel.kt | 4 +- .../alibi/ui/models/BaseRecorderModel.kt | 12 +++- .../alibi/ui/models/VideoRecorderModel.kt | 4 +- 10 files changed, 70 insertions(+), 107 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 2e0ff560a..775586e4b 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -3,11 +3,13 @@ package app.myzel394.alibi.db import android.content.Context import android.media.MediaRecorder import android.os.Build +import androidx.camera.video.FileOutputOptions import androidx.camera.video.Quality import androidx.camera.video.QualitySelector import app.myzel394.alibi.R import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder +import app.myzel394.alibi.services.VideoRecorderService import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import java.time.LocalDateTime @@ -260,6 +262,19 @@ data class AudioRecorderSettings( return supportedFormats.contains(outputFormat) } + val fileExtension: String + get() = when (outputFormat) { + MediaRecorder.OutputFormat.AAC_ADTS -> "aac" + MediaRecorder.OutputFormat.THREE_GPP -> "3gp" + MediaRecorder.OutputFormat.MPEG_4 -> "mp4" + MediaRecorder.OutputFormat.MPEG_2_TS -> "ts" + MediaRecorder.OutputFormat.WEBM -> "webm" + MediaRecorder.OutputFormat.AMR_NB -> "amr" + MediaRecorder.OutputFormat.AMR_WB -> "awb" + MediaRecorder.OutputFormat.OGG -> "ogg" + else -> "raw" + } + companion object { fun getDefaultInstance(): AudioRecorderSettings = AudioRecorderSettings() val EXAMPLE_MAX_DURATIONS = listOf( @@ -403,6 +418,9 @@ data class VideoRecorderSettings( fun getMimeType() = "video/mp4" + val fileExtension + get() = "mp4" + companion object { fun getDefaultInstance() = VideoRecorderSettings() diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index 4de23007a..ebcd08fa6 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -13,7 +13,6 @@ import android.os.Looper import androidx.core.app.ServiceCompat import app.myzel394.alibi.NotificationHelper import app.myzel394.alibi.db.AppSettings -import app.myzel394.alibi.db.AudioRecorderSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.AudioBatchesFolder @@ -22,7 +21,7 @@ import app.myzel394.alibi.ui.utils.MicrophoneInfo import java.lang.IllegalStateException class AudioRecorderService : - IntervalRecorderService() { + IntervalRecorderService() { override var batchesFolder: BatchesFolder = AudioBatchesFolder.viaInternalFolder(this) private val handler = Handler(Looper.getMainLooper()) @@ -169,6 +168,8 @@ class AudioRecorderService : } else { MediaRecorder() }.apply { + val audioSettings = settings.audioRecorderSettings + // Audio Source is kinda strange, here are my experimental findings using a Pixel 7 Pro // and Redmi Buds 3 Pro: // - MIC: Uses the bottom microphone of the phone (17) @@ -180,22 +181,25 @@ class AudioRecorderService : when (batchesFolder.type) { BatchesFolder.BatchType.INTERNAL -> { setOutputFile( - batchesFolder.asInternalGetOutputPath(counter, settings.fileExtension) + batchesFolder.asInternalGetOutputPath(counter, audioSettings.fileExtension) ) } BatchesFolder.BatchType.CUSTOM -> { setOutputFile( - batchesFolder.asCustomGetFileDescriptor(counter, settings.fileExtension) + batchesFolder.asCustomGetFileDescriptor( + counter, + audioSettings.fileExtension + ) ) } } - setOutputFormat(settings.outputFormat) + setOutputFormat(audioSettings.getOutputFormat()) - setAudioEncoder(settings.encoder) - setAudioEncodingBitRate(settings.bitRate) - setAudioSamplingRate(settings.samplingRate) + setAudioEncoder(audioSettings.getEncoder()) + setAudioEncodingBitRate(audioSettings.bitRate) + setAudioSamplingRate(audioSettings.getSamplingRate()) setOnErrorListener(OnErrorListener { _, _, _ -> onError() }) @@ -281,47 +285,8 @@ class AudioRecorderService : folderPath = batchesFolder.exportFolderForSettings(), recordingStart = recordingStart, maxDuration = settings.maxDuration, - fileExtension = settings.fileExtension, + fileExtension = settings.audioRecorderSettings.fileExtension, intervalDuration = settings.intervalDuration, type = RecordingInformation.Type.AUDIO, ) - - data class Settings( - override val maxDuration: Long, - override val intervalDuration: Long, - val bitRate: Int, - val samplingRate: Int, - val outputFormat: Int, - val encoder: Int, - val folder: String? = null, - ) : IntervalRecorderService.Settings( - maxDuration = maxDuration, - intervalDuration = intervalDuration - ) { - val fileExtension: String - get() = when (outputFormat) { - MediaRecorder.OutputFormat.AAC_ADTS -> "aac" - MediaRecorder.OutputFormat.THREE_GPP -> "3gp" - MediaRecorder.OutputFormat.MPEG_4 -> "mp4" - MediaRecorder.OutputFormat.MPEG_2_TS -> "ts" - MediaRecorder.OutputFormat.WEBM -> "webm" - MediaRecorder.OutputFormat.AMR_NB -> "amr" - MediaRecorder.OutputFormat.AMR_WB -> "awb" - MediaRecorder.OutputFormat.OGG -> "ogg" - else -> "raw" - } - - companion object { - fun from(appSettings: AppSettings): Settings { - return Settings( - intervalDuration = appSettings.intervalDuration, - maxDuration = appSettings.maxDuration, - bitRate = appSettings.audioRecorderSettings.bitRate, - samplingRate = appSettings.audioRecorderSettings.getSamplingRate(), - outputFormat = appSettings.audioRecorderSettings.getOutputFormat(), - encoder = appSettings.audioRecorderSettings.getEncoder(), - ) - } - } - } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index 35139b272..f5e600a80 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -1,16 +1,17 @@ package app.myzel394.alibi.services +import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.helpers.BatchesFolder import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit -abstract class IntervalRecorderService : +abstract class IntervalRecorderService : RecorderService() { protected var counter = 0L private set - lateinit var settings: S + lateinit var settings: AppSettings private lateinit var cycleTimer: ScheduledExecutorService diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 260772002..1449724f2 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -19,7 +19,6 @@ import androidx.camera.video.VideoRecordEvent import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat import app.myzel394.alibi.NotificationHelper -import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.BatchesFolder @@ -34,7 +33,7 @@ import kotlinx.coroutines.withTimeoutOrNull import kotlin.properties.Delegates class VideoRecorderService : - IntervalRecorderService() { + IntervalRecorderService() { override var batchesFolder: BatchesFolder = VideoBatchesFolder.viaInternalFolder(this) private val job = SupervisorJob() @@ -149,18 +148,22 @@ class VideoRecorderService : } private fun buildRecorder() = Recorder.Builder() - .setQualitySelector(settings.quality) + .setQualitySelector( + settings.videoRecorderSettings.getQualitySelector() + ?: QualitySelector.from(Quality.HIGHEST) + ) .apply { - if (settings.targetVideoBitRate != null) { - setTargetVideoEncodingBitRate(settings.targetVideoBitRate!!) + if (settings.videoRecorderSettings.targetedVideoBitRate != null) { + setTargetVideoEncodingBitRate(settings.videoRecorderSettings.targetedVideoBitRate!!) } } .build() private fun buildVideoCapture(recorder: Recorder) = VideoCapture.Builder(recorder) .apply { - if (settings.targetFrameRate != null) { - setTargetFrameRate(Range(settings.targetFrameRate!!, settings.targetFrameRate!!)) + val frameRate = settings.videoRecorderSettings.targetFrameRate + if (frameRate != null) { + setTargetFrameRate(Range(frameRate, frameRate)) } } .build() @@ -218,7 +221,7 @@ class VideoRecorderService : @SuppressLint("MissingPermission") private fun prepareVideoRecording() = videoCapture!!.output - .prepareRecording(this, settings.getOutputOptions(this)) + .prepareRecording(this, getOutputOptions()) .run { if (enableAudio) { return@run withAudioEnabled() @@ -231,49 +234,22 @@ class VideoRecorderService : folderPath = batchesFolder.exportFolderForSettings(), recordingStart = recordingStart, maxDuration = settings.maxDuration, - fileExtension = settings.fileExtension, + fileExtension = settings.videoRecorderSettings.fileExtension, intervalDuration = settings.intervalDuration, type = RecordingInformation.Type.VIDEO, ) - companion object { - const val CAMERA_CLOSE_TIMEOUT = 20000L - } - - data class Settings( - override val maxDuration: Long, - override val intervalDuration: Long, - val folder: String? = null, - val targetVideoBitRate: Int? = null, - val targetFrameRate: Int? = null, - val quality: QualitySelector = QualitySelector.from(Quality.HIGHEST), - ) : IntervalRecorderService.Settings( - maxDuration = maxDuration, - intervalDuration = intervalDuration - ) { - val fileExtension - get() = "mp4" - - fun getOutputOptions(video: VideoRecorderService): FileOutputOptions { - val fileName = "${video.counter}.$fileExtension" - val file = video.batchesFolder.getInternalFolder().resolve(fileName).apply { - createNewFile() - } - - return FileOutputOptions.Builder(file).build() + fun getOutputOptions(): FileOutputOptions { + val fileName = "${counter}.${settings.videoRecorderSettings.fileExtension}" + val file = batchesFolder.getInternalFolder().resolve(fileName).apply { + createNewFile() } - companion object { - fun from(appSettings: AppSettings) = Settings( - maxDuration = appSettings.maxDuration, - intervalDuration = appSettings.intervalDuration, - folder = appSettings.saveFolder, - targetVideoBitRate = appSettings.videoRecorderSettings.targetedVideoBitRate, - targetFrameRate = appSettings.videoRecorderSettings.targetFrameRate, - quality = appSettings.videoRecorderSettings.getQualitySelector() - ?: QualitySelector.from(Quality.HIGHEST), - ) - } + return FileOutputOptions.Builder(file).build() + } + + companion object { + const val CAMERA_CLOSE_TIMEOUT = 20000L } class CameraControl( diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt index 877d0635d..8e37950d4 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt @@ -31,9 +31,8 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch typealias RecorderModel = BaseRecorderModel< - IntervalRecorderService.Settings, RecordingInformation, - IntervalRecorderService, + IntervalRecorderService, BatchesFolder? > diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index 874dd610a..83f6cdbf5 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -56,7 +56,7 @@ fun AudioRecordingStatus( recordingTime = audioRecorder.recordingTime, progress = audioRecorder.progress, recordingStart = audioRecorder.recordingStart, - maxDuration = audioRecorder.settings.maxDuration, + maxDuration = audioRecorder.settings!!.maxDuration, ) MicrophoneStatus(audioRecorder) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index 61d0f689c..d34ad622c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -110,7 +110,7 @@ fun VideoRecordingStatus( recordingTime = videoRecorder.recordingTime, progress = videoRecorder.progress, recordingStart = videoRecorder.recordingStart, - maxDuration = videoRecorder.settings.maxDuration, + maxDuration = videoRecorder.settings!!.maxDuration, ) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index 6b9bebf53..4d679c7e2 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -13,7 +13,7 @@ import app.myzel394.alibi.services.AudioRecorderService import app.myzel394.alibi.ui.utils.MicrophoneInfo class AudioRecorderModel : - BaseRecorderModel() { + BaseRecorderModel() { override var batchesFolder: AudioBatchesFolder? = null override val intentClass = AudioRecorderService::class.java @@ -46,8 +46,6 @@ class AudioRecorderModel : amplitudes = amps onAmplitudeChange() } - service.settings = - AudioRecorderService.Settings.from(settings) service.clearAllRecordings() service.startRecording() diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 28de54d7c..ad9f1956a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -19,7 +19,7 @@ import app.myzel394.alibi.services.RecorderNotificationHelper import app.myzel394.alibi.services.RecorderService import kotlinx.serialization.json.Json -abstract class BaseRecorderModel, B : BatchesFolder?> : +abstract class BaseRecorderModel, B : BatchesFolder?> : ViewModel() { protected abstract val intentClass: Class @@ -51,7 +51,7 @@ abstract class BaseRecorderModel() { + BaseRecorderModel() { override var batchesFolder: VideoBatchesFolder? = null override val intentClass = VideoRecorderService::class.java @@ -39,8 +39,6 @@ class VideoRecorderModel : } override fun onServiceConnected(service: VideoRecorderService) { - service.settings = VideoRecorderService.Settings.from(settings) - service.clearAllRecordings() service.startRecording() From ee391a914b4beb09d5969d936dd66788ea467740 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 22:28:32 +0100 Subject: [PATCH 081/176] fix: Unify spacing of audio and video status --- .../organisms/AudioRecordingStatus.kt | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index 83f6cdbf5..685dd3485 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.material3.Divider import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -15,6 +16,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RealtimeAudioVisualizer import app.myzel394.alibi.ui.components.RecorderScreen.molecules.MicrophoneStatus import app.myzel394.alibi.ui.components.RecorderScreen.molecules.RecordingControl @@ -45,7 +47,8 @@ fun AudioRecordingStatus( KeepScreenOn() Column( modifier = Modifier - .fillMaxSize(), + .fillMaxSize() + .padding(bottom = 32.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceBetween, ) { @@ -59,34 +62,41 @@ fun AudioRecordingStatus( maxDuration = audioRecorder.settings!!.maxDuration, ) - MicrophoneStatus(audioRecorder) + Column( + verticalArrangement = Arrangement + .spacedBy(32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(bottom = 32.dp), + ) { + MicrophoneStatus(audioRecorder) - Divider() + Divider() - RecordingControl( - isPaused = audioRecorder.isPaused, - recordingTime = audioRecorder.recordingTime, - onDelete = { - scope.launch { - runCatching { - audioRecorder.stopRecording(context) + RecordingControl( + isPaused = audioRecorder.isPaused, + recordingTime = audioRecorder.recordingTime, + onDelete = { + scope.launch { + runCatching { + audioRecorder.stopRecording(context) + } + runCatching { + audioRecorder.destroyService(context) + } + audioRecorder.batchesFolder!!.deleteRecordings() } - runCatching { - audioRecorder.destroyService(context) + }, + onPauseResume = { + if (audioRecorder.isPaused) { + audioRecorder.resumeRecording() + } else { + audioRecorder.pauseRecording() } - audioRecorder.batchesFolder!!.deleteRecordings() - } - }, - onPauseResume = { - if (audioRecorder.isPaused) { - audioRecorder.resumeRecording() - } else { - audioRecorder.pauseRecording() + }, + onSave = { + audioRecorder.onRecordingSave(false) } - }, - onSave = { - audioRecorder.onRecordingSave(false) - } - ) + ) + } } } \ No newline at end of file From b1c77fe16aed2888277ff4a0cbbc9b92eec74691 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 22:44:10 +0100 Subject: [PATCH 082/176] fix: Unify spacing of audio and video status --- .../components/RecorderScreen/organisms/AudioRecordingStatus.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index 685dd3485..eb93d46b1 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -66,7 +66,6 @@ fun AudioRecordingStatus( verticalArrangement = Arrangement .spacedBy(32.dp), horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(bottom = 32.dp), ) { MicrophoneStatus(audioRecorder) From 4f9f65d0b1c648365d36c95c00779800f0bb5016 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 23:09:10 +0100 Subject: [PATCH 083/176] fix: Only start recording if recordingstate is idle --- .../alibi/services/RecorderService.kt | 2 +- .../java/app/myzel394/alibi/ui/Navigation.kt | 2 ++ .../alibi/ui/models/AudioRecorderModel.kt | 10 ++++++-- .../alibi/ui/models/BaseRecorderModel.kt | 25 ++++++++++--------- .../alibi/ui/models/VideoRecorderModel.kt | 9 +++++-- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index 0330800b3..463010d71 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -29,7 +29,7 @@ abstract class RecorderService : LifecycleService() { private lateinit var recordingTimeTimer: ScheduledExecutorService private var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null - var state = RecorderState.STOPPED + var state = RecorderState.IDLE private set var onStateChange: ((RecorderState) -> Unit)? = null diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index 122425725..d9a73ddc8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -51,9 +51,11 @@ fun Navigation( DisposableEffect(Unit) { audioRecorder.bindToService(context) + videoRecorder.bindToService(context) onDispose { audioRecorder.unbindFromService(context) + videoRecorder.unbindFromService(context) } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index 4d679c7e2..aedac2e28 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.setValue import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation +import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.services.AudioRecorderService import app.myzel394.alibi.ui.utils.MicrophoneInfo @@ -47,8 +48,13 @@ class AudioRecorderModel : onAmplitudeChange() } - service.clearAllRecordings() - service.startRecording() + // `onServiceConnected` may be called when reconnecting to the service, + // so we only want to actually start the recording if the service is idle and thus + // not already recording + if (service.state == RecorderState.IDLE) { + service.clearAllRecordings() + service.startRecording() + } recorderState = service.state recordingTime = service.recordingTime diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index ad9f1956a..19c1c231d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -105,6 +105,17 @@ abstract class BaseRecorderModel, B : BatchesF protected open fun handleIntent(intent: Intent) = intent + private fun stopOldServices(context: Context) { + runCatching { + context.unbindService(connection) + } + + val intent = Intent(context, intentClass) + runCatching { + context.stopService(intent) + } + } + // If override, call `super` AFTER setting the settings open fun startRecording( context: Context, @@ -113,10 +124,7 @@ abstract class BaseRecorderModel, B : BatchesF this.settings = settings // Clean up - runCatching { - recorderService?.clearAllRecordings() - context.unbindService(connection) - } + stopOldServices(context) notificationDetails = settings.notificationSettings.let { if (it == null) @@ -147,10 +155,6 @@ abstract class BaseRecorderModel, B : BatchesF suspend fun stopRecording(context: Context) { recorderService!!.stopRecording() - - runCatching { - context.unbindService(connection) - } } fun pauseRecording() { @@ -164,11 +168,8 @@ abstract class BaseRecorderModel, B : BatchesF fun destroyService(context: Context) { recorderService!!.destroy() reset() - val intent = Intent(context, intentClass) - runCatching { - context.stopService(intent) - } + stopOldServices(context) } // Bind functions used to manually bind to the service if the app diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt index 3e29ace0d..986852710 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt @@ -39,8 +39,13 @@ class VideoRecorderModel : } override fun onServiceConnected(service: VideoRecorderService) { - service.clearAllRecordings() - service.startRecording() + // `onServiceConnected` may be called when reconnecting to the service, + // so we only want to actually start the recording if the service is idle and thus + // not already recording + if (service.state == RecorderState.IDLE) { + service.clearAllRecordings() + service.startRecording() + } recorderState = service.state recordingTime = service.recordingTime From df0bb35672ef38fe97ff74803708d6cae06c57bc Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 16 Dec 2023 23:44:28 +0100 Subject: [PATCH 084/176] feat: Don't show RecordingControl until recording ready --- .../organisms/VideoRecordingStatus.kt | 48 ++++++++++--------- .../alibi/ui/models/VideoRecorderModel.kt | 1 - 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index d34ad622c..8c7eb9127 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -140,31 +140,35 @@ fun VideoRecordingStatus( Divider() - RecordingControl( - isPaused = videoRecorder.isPaused, - recordingTime = videoRecorder.recordingTime, - onDelete = { - scope.launch { - runCatching { - videoRecorder.stopRecording(context) + if (!videoRecorder.isStartingRecording) { + RecordingControl( + isPaused = videoRecorder.isPaused, + recordingTime = videoRecorder.recordingTime, + onDelete = { + scope.launch { + runCatching { + videoRecorder.stopRecording(context) + } + runCatching { + videoRecorder.destroyService(context) + } + videoRecorder.batchesFolder!!.deleteRecordings() } - runCatching { - videoRecorder.destroyService(context) + }, + onPauseResume = { + if (videoRecorder.isPaused) { + videoRecorder.resumeRecording() + } else { + videoRecorder.pauseRecording() } - videoRecorder.batchesFolder!!.deleteRecordings() - } - }, - onPauseResume = { - if (videoRecorder.isPaused) { - videoRecorder.resumeRecording() - } else { - videoRecorder.pauseRecording() + }, + onSave = { + videoRecorder.onRecordingSave(false) } - }, - onSave = { - videoRecorder.onRecordingSave(false) - } - ) + ) + } else { + Box {} + } } } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt index 986852710..701faf9e8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt @@ -8,7 +8,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.VideoBatchesFolder From 76752e9004db35050af15f13ecff26481cc1814f Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 17 Dec 2023 21:24:12 +0100 Subject: [PATCH 085/176] fix: Properly save recording --- .../java/app/myzel394/alibi/db/AppSettings.kt | 2 +- .../alibi/services/VideoRecorderService.kt | 3 + .../RecorderEventsHandler.kt | 139 +++++++++--------- .../alibi/ui/models/VideoRecorderModel.kt | 8 +- .../alibi/ui/screens/RecorderScreen.kt | 2 +- 5 files changed, 80 insertions(+), 74 deletions(-) rename app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/{atoms => organisms}/RecorderEventsHandler.kt (67%) diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 775586e4b..845f11cb8 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -263,7 +263,7 @@ data class AudioRecorderSettings( } val fileExtension: String - get() = when (outputFormat) { + get() = when (getOutputFormat()) { MediaRecorder.OutputFormat.AAC_ADTS -> "aac" MediaRecorder.OutputFormat.THREE_GPP -> "3gp" MediaRecorder.OutputFormat.MPEG_4 -> "mp4" diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 1449724f2..92f1d0e85 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -54,6 +54,8 @@ class VideoRecorderService : private lateinit var selectedCamera: CameraSelector private var enableAudio by Delegates.notNull() + var onCameraControlAvailable = {} + var cameraControl: CameraControl? = null private set @@ -186,6 +188,7 @@ class VideoRecorderService : videoCapture ) cameraControl = CameraControl(camera!!) + onCameraControlAvailable() _cameraAvailableListener.complete(Unit) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt similarity index 67% rename from app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt rename to app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt index 8e37950d4..746b16577 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt @@ -1,4 +1,4 @@ -package app.myzel394.alibi.ui.components.RecorderScreen.atoms +package app.myzel394.alibi.ui.components.RecorderScreen.organisms import android.content.Intent import android.net.Uri @@ -23,6 +23,8 @@ import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder import app.myzel394.alibi.services.IntervalRecorderService +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecorderErrorDialog +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecorderProcessingDialog import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.models.BaseRecorderModel import app.myzel394.alibi.ui.models.VideoRecorderModel @@ -116,82 +118,79 @@ fun RecorderEventsHandler( } } - fun saveRecording(recorder: RecorderModel) { - scope.launch { - isProcessing = true - - // Give the user some time to see the processing dialog - delay(100) - - try { - - val recording = - // When new recording created - recorder.recorderService?.getRecordingInformation() - // When recording is loaded from lastRecording - ?: settings.lastRecording - ?: throw Exception("No recording information available") - val batchesFolder = when (recorder.javaClass) { - AudioRecorderModel::class.java -> AudioBatchesFolder.importFromFolder( - recording.folderPath, - context - ) - - VideoRecorderModel::class.java -> VideoBatchesFolder.importFromFolder( - recording.folderPath, - context - ) - - else -> throw Exception("Unknown recorder type") - } - - batchesFolder.concatenate( - recording.recordingStart, - recording.fileExtension, + suspend fun saveRecording(recorder: RecorderModel) { + isProcessing = true + + // Give the user some time to see the processing dialog + delay(100) + + try { + val recording = + // When new recording created + recorder.recorderService?.getRecordingInformation() + // When recording is loaded from lastRecording + ?: settings.lastRecording + ?: throw Exception("No recording information available") + val batchesFolder = when (recorder.javaClass) { + AudioRecorderModel::class.java -> AudioBatchesFolder.importFromFolder( + recording.folderPath, + context ) - // Save file - val name = batchesFolder.getName( - recording.recordingStart, - recording.fileExtension, + VideoRecorderModel::class.java -> VideoBatchesFolder.importFromFolder( + recording.folderPath, + context ) - when (batchesFolder.type) { - BatchesFolder.BatchType.INTERNAL -> { - when (batchesFolder) { - is AudioBatchesFolder -> { - saveAudioFile( - batchesFolder.asInternalGetOutputFile( - recording.recordingStart, - recording.fileExtension, - ), name - ) - } - - is VideoBatchesFolder -> { - saveVideoFile( - batchesFolder.asInternalGetOutputFile( - recording.recordingStart, - recording.fileExtension, - ), name - ) - } + else -> throw Exception("Unknown recorder type") + } + + batchesFolder.concatenate( + recording.recordingStart, + recording.fileExtension, + ) + + // Save file + val name = batchesFolder.getName( + recording.recordingStart, + recording.fileExtension, + ) + + when (batchesFolder.type) { + BatchesFolder.BatchType.INTERNAL -> { + when (batchesFolder) { + is AudioBatchesFolder -> { + saveAudioFile( + batchesFolder.asInternalGetOutputFile( + recording.recordingStart, + recording.fileExtension, + ), name + ) + } + + is VideoBatchesFolder -> { + saveVideoFile( + batchesFolder.asInternalGetOutputFile( + recording.recordingStart, + recording.fileExtension, + ), name + ) } } + } - BatchesFolder.BatchType.CUSTOM -> { - showSnackbar(batchesFolder.customFolder!!.uri) + BatchesFolder.BatchType.CUSTOM -> { + showSnackbar(batchesFolder.customFolder!!.uri) - if (settings.deleteRecordingsImmediately) { - batchesFolder.deleteRecordings() - } + if (settings.deleteRecordingsImmediately) { + batchesFolder.deleteRecordings() } } - } catch (error: Exception) { - Log.getStackTraceString(error) - } finally { - isProcessing = false } + } catch (error: Exception) { + Log.getStackTraceString(error) + } finally { + isProcessing = false } } @@ -208,9 +207,9 @@ fun RecorderEventsHandler( saveAsLastRecording(audioRecorder as RecorderModel) saveRecording(audioRecorder) - } - audioRecorder.destroyService(context) + audioRecorder.destroyService(context) + } } } } @@ -241,9 +240,9 @@ fun RecorderEventsHandler( saveAsLastRecording(videoRecorder as RecorderModel) saveRecording(videoRecorder) - } - videoRecorder.destroyService(context) + videoRecorder.destroyService(context) + } } } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt index 701faf9e8..8428860af 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt @@ -26,8 +26,8 @@ class VideoRecorderModel : override val isInRecording: Boolean get() = super.isInRecording - val isStartingRecording: Boolean - get() = recorderService?.cameraControl == null + var isStartingRecording by mutableStateOf(true) + private set val cameraSelector: CameraSelector get() = CameraSelector.Builder().requireLensFacing(cameraID).build() @@ -46,6 +46,10 @@ class VideoRecorderModel : service.startRecording() } + service.onCameraControlAvailable = { + isStartingRecording = false + } + recorderState = service.state recordingTime = service.recordingTime } diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index 04eaaab69..026d89b5b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -28,7 +28,7 @@ import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation -import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecorderEventsHandler +import app.myzel394.alibi.ui.components.RecorderScreen.organisms.RecorderEventsHandler import app.myzel394.alibi.ui.components.RecorderScreen.organisms.VideoRecordingStatus import app.myzel394.alibi.ui.effects.rememberSettings import app.myzel394.alibi.ui.models.AudioRecorderModel From e719c0f8fbde79a207fe04945b19a38a79686812 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 17 Dec 2023 21:27:47 +0100 Subject: [PATCH 086/176] feat: Add some optional delay to RecordingControl --- .../components/RecorderScreen/molecules/RecordingControl.kt | 3 +++ .../RecorderScreen/organisms/VideoRecordingStatus.kt | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt index 822027ef9..a13d3ff4c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt @@ -30,6 +30,7 @@ import kotlinx.coroutines.launch @Composable fun RecordingControl( + initialDelay: Long = 0L, isPaused: Boolean, recordingTime: Long, onDelete: () -> Unit, @@ -67,6 +68,8 @@ fun RecordingControl( LaunchedEffect(animateIn) { if (animateIn) { + delay(initialDelay) + val stack = RandomStack.of(arrayOf(1, 2, 3).asIterable()) while (!stack.isEmpty()) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt index 8c7eb9127..7f9086db8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/VideoRecordingStatus.kt @@ -142,6 +142,10 @@ fun VideoRecordingStatus( if (!videoRecorder.isStartingRecording) { RecordingControl( + // There may be some edge cases where the app may crash if the + // user stops or pauses the recording too soon, so we simply add a + // small delay to prevent that + initialDelay = 1000L, isPaused = videoRecorder.isPaused, recordingTime = videoRecorder.recordingTime, onDelete = { From 12311d392b2035f46586451d4dacd0029f8fc249 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 17 Dec 2023 21:54:22 +0100 Subject: [PATCH 087/176] fix: Properly set `isStartingRecording` for video recording --- .../java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt index 8428860af..6332d36ff 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt @@ -42,8 +42,12 @@ class VideoRecorderModel : // so we only want to actually start the recording if the service is idle and thus // not already recording if (service.state == RecorderState.IDLE) { + isStartingRecording = true + service.clearAllRecordings() service.startRecording() + } else { + isStartingRecording = false } service.onCameraControlAvailable = { From 3cc34faa0259541f28efd732414acb06c6817193 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 17 Dec 2023 21:54:39 +0100 Subject: [PATCH 088/176] fix: Remove unnecessary `runCatching` --- .../organisms/RecorderEventsHandler.kt | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt index 746b16577..22e24a529 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt @@ -203,13 +203,11 @@ fun RecorderEventsHandler( } else { audioRecorder.stopRecording(context) - runCatching { - saveAsLastRecording(audioRecorder as RecorderModel) + saveAsLastRecording(audioRecorder as RecorderModel) - saveRecording(audioRecorder) + saveRecording(audioRecorder) - audioRecorder.destroyService(context) - } + audioRecorder.destroyService(context) } } } @@ -236,13 +234,11 @@ fun RecorderEventsHandler( } else { videoRecorder.stopRecording(context) - runCatching { - saveAsLastRecording(videoRecorder as RecorderModel) + saveAsLastRecording(videoRecorder as RecorderModel) - saveRecording(videoRecorder) + saveRecording(videoRecorder) - videoRecorder.destroyService(context) - } + videoRecorder.destroyService(context) } } } From 7dfa29856e42e21920762cae61c1e90eed086612 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 21 Dec 2023 12:46:05 +0100 Subject: [PATCH 089/176] feat: Add LockedApp --- .../java/app/myzel394/alibi/MainActivity.kt | 19 +---- .../java/app/myzel394/alibi/ui/LockedApp.kt | 80 +++++++++++++++++++ .../java/app/myzel394/alibi/ui/Navigation.kt | 15 ++++ .../alibi/ui/screens/RecorderScreen.kt | 3 +- app/src/main/res/values/strings.xml | 2 + 5 files changed, 99 insertions(+), 20 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/LockedApp.kt diff --git a/app/src/main/java/app/myzel394/alibi/MainActivity.kt b/app/src/main/java/app/myzel394/alibi/MainActivity.kt index 664b96ee0..7222997a6 100644 --- a/app/src/main/java/app/myzel394/alibi/MainActivity.kt +++ b/app/src/main/java/app/myzel394/alibi/MainActivity.kt @@ -14,6 +14,7 @@ import androidx.core.view.WindowCompat import androidx.datastore.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AppSettingsSerializer +import app.myzel394.alibi.ui.LockedApp import app.myzel394.alibi.ui.Navigation import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.theme.AlibiTheme @@ -31,24 +32,6 @@ class MainActivity : AppCompatActivity() { WindowCompat.setDecorFitsSystemWindows(window, false) setContent { - val dataStore = LocalContext.current.dataStore - val settings = dataStore - .data - .collectAsState(initial = AppSettings.getDefaultInstance()) - .value - - LaunchedEffect(settings.theme) { - if (!SUPPORTS_DARK_MODE_NATIVELY) { - val currentValue = AppCompatDelegate.getDefaultNightMode() - - if (settings.theme == AppSettings.Theme.LIGHT && currentValue != AppCompatDelegate.MODE_NIGHT_NO) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) - } else if (settings.theme == AppSettings.Theme.DARK && currentValue != AppCompatDelegate.MODE_NIGHT_YES) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) - } - } - } - AlibiTheme { Navigation() } diff --git a/app/src/main/java/app/myzel394/alibi/ui/LockedApp.kt b/app/src/main/java/app/myzel394/alibi/ui/LockedApp.kt new file mode 100644 index 000000000..b152ab861 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/LockedApp.kt @@ -0,0 +1,80 @@ +package app.myzel394.alibi.ui + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +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.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Fingerprint +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +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 +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.myzel394.alibi.R + +// After this amount, close the app +const val MAX_TRIES = 5 + +@Composable +fun LockedApp() { + Scaffold { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween, + ) { + Box {} + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Icon( + Icons.Default.Fingerprint, + contentDescription = null, + modifier = Modifier + .size(64.dp) + ) + Text( + text = stringResource(R.string.ui_locked_title), + style = MaterialTheme.typography.bodyLarge, + ) + } + Button( + modifier = Modifier + .fillMaxWidth() + .height(BIG_PRIMARY_BUTTON_SIZE), + onClick = {}, + colors = ButtonDefaults.filledTonalButtonColors(), + ) { + Icon( + Icons.Default.Lock, + contentDescription = null, + modifier = Modifier + .size(ButtonDefaults.IconSize) + ) + Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) + Text( + text = stringResource(R.string.ui_locked_unlocked), + style = MaterialTheme.typography.bodyLarge, + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index d9a73ddc8..4860f10bb 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -3,6 +3,7 @@ package app.myzel394.alibi.ui import android.content.Context import android.hardware.camera2.CameraCharacteristics import android.hardware.camera2.CameraManager +import androidx.appcompat.app.AppCompatDelegate import androidx.camera.core.CameraX import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn @@ -24,6 +25,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.enums.Screen import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.models.VideoRecorderModel @@ -59,6 +61,18 @@ fun Navigation( } } + LaunchedEffect(settings.theme) { + if (!SUPPORTS_DARK_MODE_NATIVELY) { + val currentValue = AppCompatDelegate.getDefaultNightMode() + + if (settings.theme == AppSettings.Theme.LIGHT && currentValue != AppCompatDelegate.MODE_NIGHT_NO) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + } else if (settings.theme == AppSettings.Theme.DARK && currentValue != AppCompatDelegate.MODE_NIGHT_YES) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + } + } + } + NavHost( modifier = Modifier .background(MaterialTheme.colorScheme.background), @@ -84,6 +98,7 @@ fun Navigation( navController = navController, audioRecorder = audioRecorder, videoRecorder = videoRecorder, + settings = settings, ) } composable( diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index 026d89b5b..cb5d969a2 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -40,12 +40,11 @@ fun RecorderScreen( navController: NavController, audioRecorder: AudioRecorderModel, videoRecorder: VideoRecorderModel, + settings: AppSettings, ) { val snackbarHostState = remember { SnackbarHostState() } val context = LocalContext.current - val settings = rememberSettings() - RecorderEventsHandler( settings = settings, snackbarHostState = snackbarHostState, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 82ea4f6d4..eacf75578 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -160,4 +160,6 @@ Recording started %s Saving now will save until %s Video Recorder is starting... + Alibi is locked + Unlock \ No newline at end of file From bf84396a86f5dc724301056c98f6bf67ecd07ef6 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 21 Dec 2023 18:12:46 +0100 Subject: [PATCH 090/176] current stand --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 2 + .../java/app/myzel394/alibi/db/AppSettings.kt | 21 ++++++++- .../java/app/myzel394/alibi/ui/Navigation.kt | 43 +++++++++++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 03d67f528..20252d103 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -146,4 +146,6 @@ dependencies { implementation "com.valentinilk.shimmer:compose-shimmer:1.2.0" + + implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05" } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3d59dbe82..fdab32fc1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,6 +24,8 @@ + + = Build.VERSION_CODES.R) { + val executor = ContextCompat.getMainExecutor(context) + val promptInfo = BiometricPrompt.Builder(context) + .setTitle("Biometric login for my app") + .setSubtitle("Log in using your biometric credential") + .setAllowedAuthenticators( + BIOMETRIC_STRONG or DEVICE_CREDENTIAL + ) + .build() + + // Prompt appears when user clicks "Log in". + // Consider integrating with the keystore to unlock cryptographic operations, + // if needed by your app. + promptInfo.authenticate( + CancellationSignal(), + executor, + object : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + super.onAuthenticationError(errorCode, errString) + } + + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + super.onAuthenticationSucceeded(result) + } + + override fun onAuthenticationFailed() { + super.onAuthenticationFailed() + } + }) + } + } + LaunchedEffect(settings.theme) { if (!SUPPORTS_DARK_MODE_NATIVELY) { val currentValue = AppCompatDelegate.getDefaultNightMode() From a73fc6c48f6dd7d34080c1722522163ba3d85a28 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 23 Dec 2023 19:57:55 +0100 Subject: [PATCH 091/176] feat: Add biometric authentication check --- .../java/app/myzel394/alibi/db/AppSettings.kt | 12 +--- .../myzel394/alibi/helpers/AppLockHelper.kt | 64 +++++++++++++++++++ .../java/app/myzel394/alibi/ui/Navigation.kt | 30 +-------- 3 files changed, 68 insertions(+), 38 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 4a68400b9..6657419d7 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -553,16 +553,8 @@ data class NotificationSettings( @Serializable class AppLockSettings { + // If the object is present, biometric authentication is enabled. + // To disable biometric authentication, set the instance to null. val isEnabled get() = true - - companion object { - fun getDefaultInstance(): AppLockSettings = AppLockSettings() - - fun isSupported(context: Context): Boolean { - val biometricManager = BiometricManager.from(context) - when (biometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL)) { - - } - } } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt b/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt new file mode 100644 index 000000000..b963a14ee --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt @@ -0,0 +1,64 @@ +package app.myzel394.alibi.helpers + +import android.content.Context +import androidx.biometric.BiometricManager +import androidx.core.content.ContextCompat +import androidx.biometric.BiometricPrompt +import androidx.fragment.app.FragmentActivity +import kotlinx.coroutines.CompletableDeferred + +class AppLockHelper { + enum class SupportType { + AVAILABLE, + UNAVAILABLE, + NONE_ENROLLED, + } + + companion object { + fun isSupported(context: Context): SupportType { + val biometricManager = BiometricManager.from(context) + when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)) { + BiometricManager.BIOMETRIC_SUCCESS -> return SupportType.AVAILABLE + BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> return SupportType.NONE_ENROLLED + + else -> return SupportType.UNAVAILABLE + } + } + + fun authenticate( + context: Context, + title: String, + subtitle: String + ): CompletableDeferred { + val deferred = CompletableDeferred() + + val mainExecutor = ContextCompat.getMainExecutor(context) + val biometricPrompt = BiometricPrompt( + context as FragmentActivity, + object : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + deferred.completeExceptionally(Exception(errString.toString())) + } + + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + deferred.complete(Unit) + } + + override fun onAuthenticationFailed() { + deferred.completeExceptionally(Exception("Authentication failed")) + } + } + ) + + val promptInfo = BiometricPrompt.PromptInfo.Builder() + .setTitle(title) + .setSubtitle(subtitle) + .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL) + .build() + + biometricPrompt.authenticate(promptInfo) + + return deferred + } + } +} diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index dfb3418cd..bc1aa2ad0 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -45,6 +45,7 @@ import app.myzel394.alibi.ui.screens.CustomRecordingNotificationsScreen import app.myzel394.alibi.ui.screens.SettingsScreen import app.myzel394.alibi.ui.screens.WelcomeScreen import app.myzel394.alibi.ui.utils.CameraInfo +import app.myzel394.alibi.helpers.AppLockHelper const val SCALE_IN = 1.25f @@ -73,34 +74,7 @@ fun Navigation( LaunchedEffect(Unit) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - val executor = ContextCompat.getMainExecutor(context) - val promptInfo = BiometricPrompt.Builder(context) - .setTitle("Biometric login for my app") - .setSubtitle("Log in using your biometric credential") - .setAllowedAuthenticators( - BIOMETRIC_STRONG or DEVICE_CREDENTIAL - ) - .build() - - // Prompt appears when user clicks "Log in". - // Consider integrating with the keystore to unlock cryptographic operations, - // if needed by your app. - promptInfo.authenticate( - CancellationSignal(), - executor, - object : BiometricPrompt.AuthenticationCallback() { - override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { - super.onAuthenticationError(errorCode, errString) - } - - override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { - super.onAuthenticationSucceeded(result) - } - - override fun onAuthenticationFailed() { - super.onAuthenticationFailed() - } - }) + AppLockHelper.authenticate(context, "Title", "Subtitle") } } From 025a8a3209682cfffb0e8d294088e5eea110952e Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 23 Dec 2023 20:18:46 +0100 Subject: [PATCH 092/176] feat: Add EnableAppLockTile to SettingsScreen --- .../java/app/myzel394/alibi/db/AppSettings.kt | 17 +++- .../myzel394/alibi/helpers/AppLockHelper.kt | 20 ++-- .../SettingsScreen/Tiles/EnableAppLockTile.kt | 92 +++++++++++++++++++ .../alibi/ui/components/atoms/SettingsTile.kt | 2 + .../alibi/ui/screens/SettingsScreen.kt | 2 + app/src/main/res/values/strings.xml | 5 + 6 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/EnableAppLockTile.kt diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 6657419d7..c57ecfef8 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -22,6 +22,8 @@ data class AppSettings( val audioRecorderSettings: AudioRecorderSettings = AudioRecorderSettings.getDefaultInstance(), val videoRecorderSettings: VideoRecorderSettings = VideoRecorderSettings.getDefaultInstance(), + val appLockSettings: AppLockSettings? = null, + val hasSeenOnboarding: Boolean = false, val showAdvancedSettings: Boolean = false, val theme: Theme = Theme.SYSTEM, @@ -97,6 +99,14 @@ data class AppSettings( return copy(saveFolder = saveFolder) } + fun setAppLockSettings(appLockSettings: AppLockSettings?): AppSettings { + return copy(appLockSettings = appLockSettings) + } + + // If the object is present, biometric authentication is enabled. + // To disable biometric authentication, set the instance to null. + fun isAppLockEnabled() = appLockSettings != null + enum class Theme { SYSTEM, LIGHT, @@ -553,8 +563,7 @@ data class NotificationSettings( @Serializable class AppLockSettings { - // If the object is present, biometric authentication is enabled. - // To disable biometric authentication, set the instance to null. - val isEnabled - get() = true + companion object { + fun getDefaultInstance() = AppLockSettings() + } } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt b/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt index b963a14ee..182172ee7 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt @@ -15,13 +15,13 @@ class AppLockHelper { } companion object { - fun isSupported(context: Context): SupportType { + fun getSupportType(context: Context): SupportType { val biometricManager = BiometricManager.from(context) - when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)) { - BiometricManager.BIOMETRIC_SUCCESS -> return SupportType.AVAILABLE - BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> return SupportType.NONE_ENROLLED + return when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)) { + BiometricManager.BIOMETRIC_SUCCESS -> SupportType.AVAILABLE + BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> SupportType.NONE_ENROLLED - else -> return SupportType.UNAVAILABLE + else -> SupportType.UNAVAILABLE } } @@ -29,23 +29,23 @@ class AppLockHelper { context: Context, title: String, subtitle: String - ): CompletableDeferred { - val deferred = CompletableDeferred() + ): CompletableDeferred { + val deferred = CompletableDeferred() val mainExecutor = ContextCompat.getMainExecutor(context) val biometricPrompt = BiometricPrompt( context as FragmentActivity, object : BiometricPrompt.AuthenticationCallback() { override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { - deferred.completeExceptionally(Exception(errString.toString())) + deferred.complete(false) } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { - deferred.complete(Unit) + deferred.complete(true) } override fun onAuthenticationFailed() { - deferred.completeExceptionally(Exception("Authentication failed")) + deferred.complete(false) } } ) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/EnableAppLockTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/EnableAppLockTile.kt new file mode 100644 index 000000000..aeddb5e1f --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/EnableAppLockTile.kt @@ -0,0 +1,92 @@ +package app.myzel394.alibi.ui.components.SettingsScreen.Tiles + +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.magnifier +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Fingerprint +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.myzel394.alibi.R +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppLockSettings +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.helpers.AppLockHelper +import app.myzel394.alibi.ui.components.atoms.SettingsTile +import kotlinx.coroutines.launch + +@Composable +fun EnableAppLockTile( + settings: AppSettings, +) { + val scope = rememberCoroutineScope() + + val context = LocalContext.current + val dataStore = context.dataStore + + val appLockSupport = AppLockHelper.getSupportType(context) + + if (appLockSupport === AppLockHelper.SupportType.UNAVAILABLE) { + return + } + + SettingsTile( + title = stringResource(R.string.ui_settings_option_enableAppLock_title), + description = stringResource(R.string.ui_settings_option_enableAppLock_description), + tertiaryLine = { + if (appLockSupport === AppLockHelper.SupportType.NONE_ENROLLED) { + Text( + stringResource(R.string.ui_settings_option_enableAppLock_enrollmentRequired), + color = Color.Yellow, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(top = 4.dp) + ) + } + }, + leading = { + Icon( + Icons.Default.Fingerprint, + contentDescription = null, + ) + }, + trailing = { + val title = stringResource(R.string.identityVerificationRequired_title) + val subtitle = stringResource(R.string.identityVerificationRequired_subtitle) + + Switch( + checked = settings.isAppLockEnabled(), + enabled = appLockSupport === AppLockHelper.SupportType.AVAILABLE, + onCheckedChange = { + scope.launch { + val authenticationSuccessful = AppLockHelper.authenticate( + context, + title = title, + subtitle = subtitle, + ).await() + + if (!authenticationSuccessful) { + return@launch + } + + dataStore.updateData { + it.setAppLockSettings( + if (it.appLockSettings == null) + AppLockSettings.getDefaultInstance() + else + null + ) + } + } + } + ) + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/SettingsTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/SettingsTile.kt index 729995fa2..2f255feb1 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/SettingsTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/SettingsTile.kt @@ -21,6 +21,7 @@ fun SettingsTile( firstModifier: Modifier = Modifier, title: String, description: String? = null, + tertiaryLine: (@Composable () -> Unit) = {}, leading: @Composable () -> Unit = {}, trailing: @Composable () -> Unit = {}, extra: (@Composable () -> Unit)? = null, @@ -49,6 +50,7 @@ fun SettingsTile( text = description, style = MaterialTheme.typography.bodySmall, ) + tertiaryLine() } Spacer(modifier = Modifier.width(16.dp)) trailing() diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index 471ee2f0f..217641de6 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -52,6 +52,7 @@ import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderOutput import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderSamplingRateTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.SaveFolderTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.AudioRecorderShowAllMicrophonesTile +import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.EnableAppLockTile import app.myzel394.alibi.ui.components.SettingsScreen.Tiles.VideoRecorderBitrateTile import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ThemeSelector import app.myzel394.alibi.ui.components.atoms.GlobalSwitch @@ -138,6 +139,7 @@ fun SettingsScreen( InAppLanguagePicker() DeleteRecordingsImmediatelyTile(settings = settings) CustomNotificationTile(navController = navController, settings = settings) + EnableAppLockTile(settings = settings) GlobalSwitch( label = stringResource(R.string.ui_settings_advancedSettings_label), checked = settings.showAdvancedSettings, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eacf75578..ced0bc158 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -162,4 +162,9 @@ Video Recorder is starting... Alibi is locked Unlock + Enable App Lock + Require your biometric info or your password to open Alibi + Please enroll a password or a biometric unlock method first + Verification required + You need to verify your identity to continue \ No newline at end of file From 6661d457ea9d38bffb31b85f1ec891e978494b75 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 23 Dec 2023 20:41:36 +0100 Subject: [PATCH 093/176] feat: Add AsLockedApp wrapper to require id verification if enabled --- .../java/app/myzel394/alibi/MainActivity.kt | 6 +- .../myzel394/alibi/helpers/AppLockHelper.kt | 12 +++ .../alibi/ui/{LockedApp.kt => AsLockedApp.kt} | 77 ++++++++++++++++++- .../java/app/myzel394/alibi/ui/Navigation.kt | 6 -- 4 files changed, 90 insertions(+), 11 deletions(-) rename app/src/main/java/app/myzel394/alibi/ui/{LockedApp.kt => AsLockedApp.kt} (58%) diff --git a/app/src/main/java/app/myzel394/alibi/MainActivity.kt b/app/src/main/java/app/myzel394/alibi/MainActivity.kt index 7222997a6..45e5c82bd 100644 --- a/app/src/main/java/app/myzel394/alibi/MainActivity.kt +++ b/app/src/main/java/app/myzel394/alibi/MainActivity.kt @@ -14,7 +14,7 @@ import androidx.core.view.WindowCompat import androidx.datastore.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AppSettingsSerializer -import app.myzel394.alibi.ui.LockedApp +import app.myzel394.alibi.ui.AsLockedApp import app.myzel394.alibi.ui.Navigation import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.theme.AlibiTheme @@ -33,7 +33,9 @@ class MainActivity : AppCompatActivity() { setContent { AlibiTheme { - Navigation() + AsLockedApp { + Navigation() + } } } } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt b/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt index 182172ee7..5f1013734 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AppLockHelper.kt @@ -1,11 +1,13 @@ package app.myzel394.alibi.helpers +import android.app.Activity import android.content.Context import androidx.biometric.BiometricManager import androidx.core.content.ContextCompat import androidx.biometric.BiometricPrompt import androidx.fragment.app.FragmentActivity import kotlinx.coroutines.CompletableDeferred +import kotlin.system.exitProcess class AppLockHelper { enum class SupportType { @@ -60,5 +62,15 @@ class AppLockHelper { return deferred } + + fun closeApp(context: Context) { + (context as? Activity)?.let { + it.finishAndRemoveTask() + it.finishAffinity() + it.finish() + } + + exitProcess(0) + } } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/LockedApp.kt b/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt similarity index 58% rename from app/src/main/java/app/myzel394/alibi/ui/LockedApp.kt rename to app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt index b152ab861..48ec3fcd5 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/LockedApp.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt @@ -20,17 +20,88 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold 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.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import app.myzel394.alibi.R +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.helpers.AppLockHelper +import kotlinx.coroutines.launch // After this amount, close the app -const val MAX_TRIES = 5 +const val MAX_TRIES = 10 @Composable -fun LockedApp() { +fun AsLockedApp( + content: (@Composable () -> Unit), +) { + val scope = rememberCoroutineScope() + val context = LocalContext.current + + val settings = context + .dataStore + .data + .collectAsState(initial = null) + .value ?: return + + // -1 = Unlocked, any other value = locked + var tries by remember { mutableIntStateOf(0) } + + LaunchedEffect(settings.isAppLockEnabled()) { + if (!settings.isAppLockEnabled()) { + tries = -1 + } + } + + if (tries == -1) { + return content() + } + + val title = stringResource(R.string.identityVerificationRequired_title) + val subtitle = stringResource(R.string.identityVerificationRequired_subtitle) + + fun openAuthentication() { + if (tries >= MAX_TRIES) { + AppLockHelper.closeApp(context) + return + } + + scope.launch { + val successful = AppLockHelper.authenticate( + context, + title, + subtitle, + ).await() + + if (successful) { + tries = -1 + return@launch + } + + tries++ + + if (tries >= MAX_TRIES) { + AppLockHelper.closeApp(context) + } + } + } + + LaunchedEffect(settings.isAppLockEnabled()) { + if (settings.isAppLockEnabled()) { + openAuthentication() + } + } + Scaffold { paddingValues -> Column( modifier = Modifier @@ -60,7 +131,7 @@ fun LockedApp() { modifier = Modifier .fillMaxWidth() .height(BIG_PRIMARY_BUTTON_SIZE), - onClick = {}, + onClick = ::openAuthentication, colors = ButtonDefaults.filledTonalButtonColors(), ) { Icon( diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index bc1aa2ad0..a35250298 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -72,12 +72,6 @@ fun Navigation( } } - LaunchedEffect(Unit) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - AppLockHelper.authenticate(context, "Title", "Subtitle") - } - } - LaunchedEffect(settings.theme) { if (!SUPPORTS_DARK_MODE_NATIVELY) { val currentValue = AppCompatDelegate.getDefaultNightMode() From 2d22a65506be4963c502428e602032b9220d7e3a Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 25 Dec 2023 11:54:32 +0100 Subject: [PATCH 094/176] feat: Improve AsLockedApp --- .../java/app/myzel394/alibi/MainActivity.kt | 28 ++++++++++------ .../java/app/myzel394/alibi/ui/AsLockedApp.kt | 3 +- .../myzel394/alibi/ui/LockedAppHandlers.kt | 32 +++++++++++++++++++ .../java/app/myzel394/alibi/ui/Navigation.kt | 12 ------- 4 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/LockedAppHandlers.kt diff --git a/app/src/main/java/app/myzel394/alibi/MainActivity.kt b/app/src/main/java/app/myzel394/alibi/MainActivity.kt index 45e5c82bd..79b396652 100644 --- a/app/src/main/java/app/myzel394/alibi/MainActivity.kt +++ b/app/src/main/java/app/myzel394/alibi/MainActivity.kt @@ -2,21 +2,19 @@ package app.myzel394.alibi import android.content.Context import android.os.Bundle -import android.view.MotionEvent -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.app.AppCompatDelegate -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.LocalContext +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.ui.Modifier import androidx.core.view.WindowCompat import androidx.datastore.dataStore -import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AppSettingsSerializer import app.myzel394.alibi.ui.AsLockedApp +import app.myzel394.alibi.ui.LockedAppHandlers import app.myzel394.alibi.ui.Navigation -import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.theme.AlibiTheme const val SETTINGS_FILE = "settings.json" @@ -33,8 +31,18 @@ class MainActivity : AppCompatActivity() { setContent { AlibiTheme { - AsLockedApp { - Navigation() + LockedAppHandlers() + + Box( + modifier = Modifier + .fillMaxSize() + .background( + MaterialTheme.colorScheme.background + ) + ) { + AsLockedApp { + Navigation() + } } } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt b/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt index 48ec3fcd5..634f7a33a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt @@ -39,8 +39,9 @@ import app.myzel394.alibi.helpers.AppLockHelper import kotlinx.coroutines.launch // After this amount, close the app -const val MAX_TRIES = 10 +const val MAX_TRIES = 5 +// Makes sure the app needs to be unlocked first, if app lock is enabled @Composable fun AsLockedApp( content: (@Composable () -> Unit), diff --git a/app/src/main/java/app/myzel394/alibi/ui/LockedAppHandlers.kt b/app/src/main/java/app/myzel394/alibi/ui/LockedAppHandlers.kt new file mode 100644 index 000000000..5db781c79 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/LockedAppHandlers.kt @@ -0,0 +1,32 @@ +package app.myzel394.alibi.ui + +import androidx.appcompat.app.AppCompatDelegate +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.platform.LocalContext +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppSettings + +// Handlers that can safely be run when the app is locked (biometric authentication required) +@Composable +fun LockedAppHandlers() { + val context = LocalContext.current + val settings = context + .dataStore + .data + .collectAsState(initial = null) + .value ?: return + + LaunchedEffect(settings.theme) { + if (!SUPPORTS_DARK_MODE_NATIVELY) { + val currentValue = AppCompatDelegate.getDefaultNightMode() + + if (settings.theme == AppSettings.Theme.LIGHT && currentValue != AppCompatDelegate.MODE_NIGHT_NO) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + } else if (settings.theme == AppSettings.Theme.DARK && currentValue != AppCompatDelegate.MODE_NIGHT_YES) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index a35250298..78a30e0cf 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -72,18 +72,6 @@ fun Navigation( } } - LaunchedEffect(settings.theme) { - if (!SUPPORTS_DARK_MODE_NATIVELY) { - val currentValue = AppCompatDelegate.getDefaultNightMode() - - if (settings.theme == AppSettings.Theme.LIGHT && currentValue != AppCompatDelegate.MODE_NIGHT_NO) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) - } else if (settings.theme == AppSettings.Theme.DARK && currentValue != AppCompatDelegate.MODE_NIGHT_YES) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) - } - } - } - NavHost( modifier = Modifier .background(MaterialTheme.colorScheme.background), From 28864ca264c947c4ef8dbec1c3196ca9630fedd3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:46:41 +0100 Subject: [PATCH 095/176] refactor: Improve BatchesFolder --- .../alibi/helpers/AudioBatchesFolder.kt | 29 +++++++++++- .../myzel394/alibi/helpers/BatchesFolder.kt | 47 +++++++------------ .../alibi/helpers/VideoBatchesFolder.kt | 33 ++++++++++++- 3 files changed, 77 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt index 891a54a4c..da87eb7ad 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -2,9 +2,12 @@ package app.myzel394.alibi.helpers import android.content.Context import android.net.Uri +import android.os.ParcelFileDescriptor import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateAudioFiles +import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles import com.arthenica.ffmpegkit.FFmpegKitConfig +import java.io.FileDescriptor import java.time.LocalDateTime class AudioBatchesFolder( @@ -18,9 +21,11 @@ class AudioBatchesFolder( customFolder, subfolderName, ) { - override val concatenateFunction = ::concatenateAudioFiles + override val concatenationFunction = ::concatenateVideoFiles override val ffmpegParameters = FFMPEG_PARAMETERS + private var customFileFileDescriptor: ParcelFileDescriptor? = null + override fun getOutputFileForFFmpeg( date: LocalDateTime, extension: String, @@ -37,6 +42,28 @@ class AudioBatchesFolder( } } + override fun cleanup() { + runCatching { + customFileFileDescriptor?.close() + } + } + + fun asCustomGetFileDescriptor( + counter: Long, + fileExtension: String, + ): FileDescriptor { + runCatching { + customFileFileDescriptor?.close() + } + + val file = + getCustomDefinedFolder().createFile("audio/$fileExtension", "$counter.$fileExtension")!! + + customFileFileDescriptor = context.contentResolver.openFileDescriptor(file.uri, "w")!! + + return customFileFileDescriptor!!.fileDescriptor + } + companion object { fun viaInternalFolder(context: Context) = AudioBatchesFolder(context, BatchType.INTERNAL) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 85ce60086..892e40aec 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -1,6 +1,7 @@ package app.myzel394.alibi.helpers import android.content.Context +import android.net.Uri import androidx.documentfile.provider.DocumentFile import java.io.File import java.time.LocalDateTime @@ -18,9 +19,7 @@ abstract class BatchesFolder( open val customFolder: DocumentFile? = null, open val subfolderName: String = ".recordings", ) { - private var customFileFileDescriptor: ParcelFileDescriptor? = null - - abstract val concatenateFunction: KFunction3, String, String, CompletableDeferred> + abstract val concatenationFunction: KFunction3, String, String, CompletableDeferred> abstract val ffmpegParameters: Array fun initFolders() { @@ -34,10 +33,6 @@ abstract class BatchesFolder( } } - fun cleanup() { - customFileFileDescriptor?.close() - } - fun getInternalFolder(): File { return File(context.filesDir, subfolderName) } @@ -115,29 +110,33 @@ abstract class BatchesFolder( extension: String, ): String + abstract fun cleanup() + open suspend fun concatenate( recordingStart: LocalDateTime, extension: String, disableCache: Boolean = false, onNextParameterTry: (String) -> Unit = {}, ): String { - val outputFile = getOutputFileForFFmpeg( - date = recordingStart, - extension = extension, - ) - if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) { - return outputFile + return getOutputFileForFFmpeg( + date = recordingStart, + extension = extension, + ) } - val filePaths = getBatchesForFFmpeg() - for (parameter in ffmpegParameters) { Log.i("Concatenation", "Trying parameter $parameter") onNextParameterTry(parameter) try { - concatenateFunction( + val filePaths = getBatchesForFFmpeg() + val outputFile = getOutputFileForFFmpeg( + date = recordingStart, + extension = extension, + ) + + concatenationFunction( filePaths, outputFile, parameter, @@ -203,20 +202,8 @@ abstract class BatchesFolder( } } - fun asInternalGetOutputPath(counter: Long, fileExtension: String): String { - return getInternalFolder().absolutePath + "/$counter.$fileExtension" - } - - fun asCustomGetFileDescriptor( - counter: Long, - fileExtension: String, - ): FileDescriptor { - val file = - getCustomDefinedFolder().createFile("audio/$fileExtension", "$counter.$fileExtension")!! - - customFileFileDescriptor = context.contentResolver.openFileDescriptor(file.uri, "w")!! - - return customFileFileDescriptor!!.fileDescriptor + fun asInternalGetFile(counter: Long, fileExtension: String): File { + return File(getInternalFolder(), "$counter.$fileExtension") } enum class BatchType { diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index 990b25610..d60e47ba0 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -2,6 +2,7 @@ package app.myzel394.alibi.helpers import android.content.Context import android.net.Uri +import android.os.ParcelFileDescriptor import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles import com.arthenica.ffmpegkit.FFmpegKitConfig @@ -19,9 +20,11 @@ class VideoBatchesFolder( customFolder, subfolderName, ) { - override val concatenateFunction = ::concatenateVideoFiles + override val concatenationFunction = ::concatenateVideoFiles override val ffmpegParameters = FFMPEG_PARAMETERS + private var customParcelFileDescriptor: ParcelFileDescriptor? = null + override fun getOutputFileForFFmpeg(date: LocalDateTime, extension: String): String { return when (type) { BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath @@ -35,6 +38,34 @@ class VideoBatchesFolder( } } + override fun cleanup() { + runCatching { + customParcelFileDescriptor?.close() + } + } + + fun asCustomGetParcelFileDescriptor( + counter: Long, + fileExtension: String, + ): ParcelFileDescriptor { + runCatching { + customParcelFileDescriptor?.close() + } + + val file = + getCustomDefinedFolder().createFile( + "video/$fileExtension", + "$counter.$fileExtension" + )!! + val resolver = context.contentResolver.acquireContentProviderClient(file.uri)!! + + resolver.use { + customParcelFileDescriptor = it.openFile(file.uri, "w")!! + + return customParcelFileDescriptor!! + } + } + companion object { fun viaInternalFolder(context: Context) = VideoBatchesFolder(context, BatchType.INTERNAL) From 029d9fe302629190f56b5b00a6df8acba4cc1ffb Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:47:46 +0100 Subject: [PATCH 096/176] feat: clean up BatchesFolder on stop; --- .../app/myzel394/alibi/services/IntervalRecorderService.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index f5e600a80..745e5f362 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -6,7 +6,7 @@ import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit -abstract class IntervalRecorderService : +abstract class IntervalRecorderService : RecorderService() { protected var counter = 0L private set @@ -15,7 +15,7 @@ abstract class IntervalRecorderService : private lateinit var cycleTimer: ScheduledExecutorService - abstract var batchesFolder: BatchesFolder + abstract var batchesFolder: B var onCustomOutputFolderNotAccessible: () -> Unit = {} @@ -61,6 +61,7 @@ abstract class IntervalRecorderService : override suspend fun stop() { cycleTimer.shutdown() + batchesFolder.cleanup() super.stop() } From 06f2e1de5e56630eef6b73a518dbe581dc42d50c Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:48:04 +0100 Subject: [PATCH 097/176] feat: Add custom VideoBatchesFolder support to VideoRecorderModel --- .../alibi/ui/models/VideoRecorderModel.kt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt index 6332d36ff..4550674c9 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt @@ -3,20 +3,25 @@ package app.myzel394.alibi.ui.models import android.Manifest import android.content.Context import android.content.Intent +import android.net.Uri import androidx.camera.core.CameraSelector import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.documentfile.provider.DocumentFile +import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState +import app.myzel394.alibi.helpers.AudioBatchesFolder +import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder import app.myzel394.alibi.services.VideoRecorderService import app.myzel394.alibi.ui.utils.CameraInfo import app.myzel394.alibi.ui.utils.PermissionHelper class VideoRecorderModel : - BaseRecorderModel() { + BaseRecorderModel() { override var batchesFolder: VideoBatchesFolder? = null override val intentClass = VideoRecorderService::class.java @@ -37,6 +42,21 @@ class VideoRecorderModel : cameraID = CameraInfo.Lens.BACK.androidValue } + override fun startRecording(context: Context, settings: AppSettings) { + batchesFolder = if (settings.saveFolder == null) + VideoBatchesFolder.viaInternalFolder(context) + else + VideoBatchesFolder.viaCustomFolder( + context, + DocumentFile.fromTreeUri( + context, + Uri.parse(settings.saveFolder) + )!! + ) + + super.startRecording(context, settings) + } + override fun onServiceConnected(service: VideoRecorderService) { // `onServiceConnected` may be called when reconnecting to the service, // so we only want to actually start the recording if the service is idle and thus From ab108305ef9fda4b1e7238bf743aa1c96a053b89 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:48:19 +0100 Subject: [PATCH 098/176] refactor: Improve recorder models --- .../java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt | 2 +- .../java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index aedac2e28..f4fdf10bb 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -14,7 +14,7 @@ import app.myzel394.alibi.services.AudioRecorderService import app.myzel394.alibi.ui.utils.MicrophoneInfo class AudioRecorderModel : - BaseRecorderModel() { + BaseRecorderModel() { override var batchesFolder: AudioBatchesFolder? = null override val intentClass = AudioRecorderService::class.java diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 19c1c231d..3fa71e1ed 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -19,7 +19,7 @@ import app.myzel394.alibi.services.RecorderNotificationHelper import app.myzel394.alibi.services.RecorderService import kotlinx.serialization.json.Json -abstract class BaseRecorderModel, B : BatchesFolder?> : +abstract class BaseRecorderModel> : ViewModel() { protected abstract val intentClass: Class @@ -47,7 +47,7 @@ abstract class BaseRecorderModel, B : BatchesF // thus the service is not running and thus doesn't need to be stopped or destroyed var onRecordingSave: (isSavingAsOldRecording: Boolean) -> Unit = {} var onError: () -> Unit = {} - abstract var batchesFolder: B + abstract var batchesFolder: B? private var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null From 5f1b6dcb43abca59119f1a40d49c1c462d89b859 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:48:41 +0100 Subject: [PATCH 099/176] fix: Update RecorderEventsHandler to new base models --- .../RecorderScreen/organisms/RecorderEventsHandler.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt index 22e24a529..186ec30ac 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt @@ -34,8 +34,8 @@ import kotlinx.coroutines.launch typealias RecorderModel = BaseRecorderModel< RecordingInformation, - IntervalRecorderService, - BatchesFolder? + BatchesFolder, + IntervalRecorderService, > @Composable From 76b384ffb6aee4266c7f2287ba29381ad53adf94 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:49:12 +0100 Subject: [PATCH 100/176] refactor: Small improvement for taking persistable uri permission --- .../SettingsScreen/Tiles/SaveFolderTile.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index c62db5c1a..8ffa9d040 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings @@ -58,6 +59,13 @@ fun SaveFolderTile( } } + if (path != null) { + context.contentResolver.takePersistableUriPermission( + Uri.parse(path), + Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION + ) + } + scope.launch { dataStore.updateData { it.setSaveFolder(path) @@ -70,11 +78,6 @@ fun SaveFolderTile( return@rememberFolderSelectorDialog } - context.contentResolver.takePersistableUriPermission( - folder, - Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION - ) - updateValue(folder.toString()) } From 4126dded6e3dd043b16c6814472e3df9dcb7b376 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:49:28 +0100 Subject: [PATCH 101/176] fix: Update AudioRecorderService to new batches folder --- .../myzel394/alibi/services/AudioRecorderService.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index ebcd08fa6..50fec5b4f 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -1,6 +1,7 @@ package app.myzel394.alibi.services import android.content.Context +import android.content.Context.AUDIO_SERVICE import android.content.pm.ServiceInfo import android.media.AudioDeviceCallback import android.media.AudioDeviceInfo @@ -11,6 +12,7 @@ import android.os.Build import android.os.Handler import android.os.Looper import androidx.core.app.ServiceCompat +import androidx.core.content.ContextCompat.getSystemService import app.myzel394.alibi.NotificationHelper import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation @@ -21,8 +23,8 @@ import app.myzel394.alibi.ui.utils.MicrophoneInfo import java.lang.IllegalStateException class AudioRecorderService : - IntervalRecorderService() { - override var batchesFolder: BatchesFolder = AudioBatchesFolder.viaInternalFolder(this) + IntervalRecorderService() { + override var batchesFolder = AudioBatchesFolder.viaInternalFolder(this) private val handler = Handler(Looper.getMainLooper()) @@ -181,7 +183,10 @@ class AudioRecorderService : when (batchesFolder.type) { BatchesFolder.BatchType.INTERNAL -> { setOutputFile( - batchesFolder.asInternalGetOutputPath(counter, audioSettings.fileExtension) + batchesFolder.asInternalGetFile( + counter, + audioSettings.fileExtension + ).absolutePath ) } From 4af8cd73187460d09147e3294a6f7bee58b8123f Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:53:48 +0100 Subject: [PATCH 102/176] feat: Add support for custom folder --- .../alibi/helpers/AudioBatchesFolder.kt | 18 +++++++++++------- .../myzel394/alibi/helpers/MediaConverter.kt | 7 +++++-- .../alibi/helpers/VideoBatchesFolder.kt | 18 +++++++++++------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt index da87eb7ad..b814c0f81 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -32,13 +32,17 @@ class AudioBatchesFolder( ): String { return when (type) { BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath - BatchType.CUSTOM -> FFmpegKitConfig.getSafParameterForWrite( - context, - customFolder!!.createFile( - "audio/${extension}", - getName(date, extension), - )!!.uri - )!! + BatchType.CUSTOM -> { + val name = getName(date, extension) + + FFmpegKitConfig.getSafParameterForWrite( + context, + (customFolder!!.findFile(name) ?: customFolder.createFile( + "audio/${extension}", + getName(date, extension), + )!!).uri + )!! + } } } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index b438bc42e..1f851f3af 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -1,8 +1,11 @@ package app.myzel394.alibi.helpers import android.content.Context +import android.net.Uri import android.util.Log +import androidx.documentfile.provider.DocumentFile import com.arthenica.ffmpegkit.FFmpegKit +import com.arthenica.ffmpegkit.FFmpegKitConfig import com.arthenica.ffmpegkit.ReturnCode import kotlinx.coroutines.CompletableDeferred import java.io.File @@ -66,7 +69,8 @@ class MediaConverter { val listFile = createTempFile(inputFiles.joinToString("\n") { "file '$it'" }) val command = - " -f concat" + + "-protocol_whitelist saf,concat,content,file,subfile" + + " -f concat" + " -safe 0" + " -i ${listFile.absolutePath}" + extraCommand + @@ -78,7 +82,6 @@ class MediaConverter { command ) { session -> runCatching { - listFile.delete() } if (!ReturnCode.isSuccess(session!!.returnCode)) { diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index d60e47ba0..8c6936968 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -28,13 +28,17 @@ class VideoBatchesFolder( override fun getOutputFileForFFmpeg(date: LocalDateTime, extension: String): String { return when (type) { BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath - BatchType.CUSTOM -> FFmpegKitConfig.getSafParameterForWrite( - context, - customFolder!!.createFile( - "video/${extension}", - getName(date, extension), - )!!.uri - )!! + BatchType.CUSTOM -> { + val name = getName(date, extension) + + FFmpegKitConfig.getSafParameterForWrite( + context, + (customFolder!!.findFile(name) ?: customFolder.createFile( + "video/${extension}", + getName(date, extension), + )!!).uri + )!! + } } } From 1128a6771d674b8e199df0c1236ce222e3c0edb0 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:54:05 +0100 Subject: [PATCH 103/176] feat: Add support for custom folder in VideoRecorderService --- .../alibi/services/VideoRecorderService.kt | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 92f1d0e85..acb0367eb 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -4,12 +4,17 @@ import android.annotation.SuppressLint import android.content.Intent import android.content.pm.ServiceInfo import android.os.Build +import android.os.ParcelFileDescriptor import android.util.Range import androidx.camera.core.Camera import androidx.camera.core.CameraSelector import androidx.camera.core.TorchState +import androidx.camera.core.processing.SurfaceProcessorNode.Out import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.camera.video.FileDescriptorOutputOptions import androidx.camera.video.FileOutputOptions +import androidx.camera.video.MediaStoreOutputOptions +import androidx.camera.video.OutputOptions import androidx.camera.video.Quality import androidx.camera.video.QualitySelector import androidx.camera.video.Recorder @@ -30,11 +35,12 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull +import java.nio.file.Files.createFile import kotlin.properties.Delegates class VideoRecorderService : - IntervalRecorderService() { - override var batchesFolder: BatchesFolder = VideoBatchesFolder.viaInternalFolder(this) + IntervalRecorderService() { + override var batchesFolder = VideoBatchesFolder.viaInternalFolder(this) private val job = SupervisorJob() private val scope = CoroutineScope(Dispatchers.IO + job) @@ -224,7 +230,32 @@ class VideoRecorderService : @SuppressLint("MissingPermission") private fun prepareVideoRecording() = videoCapture!!.output - .prepareRecording(this, getOutputOptions()) + .let { + // TODO: Add hint + if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + it.prepareRecording( + this, + FileDescriptorOutputOptions.Builder( + batchesFolder.asCustomGetParcelFileDescriptor( + counter, + settings.videoRecorderSettings.fileExtension + ) + ).build() + ) + } else { + it.prepareRecording( + this, + FileOutputOptions.Builder( + batchesFolder.asInternalGetFile( + counter, + settings.videoRecorderSettings.fileExtension + ).apply { + createNewFile() + } + ).build() + ) + } + } .run { if (enableAudio) { return@run withAudioEnabled() @@ -242,15 +273,6 @@ class VideoRecorderService : type = RecordingInformation.Type.VIDEO, ) - fun getOutputOptions(): FileOutputOptions { - val fileName = "${counter}.${settings.videoRecorderSettings.fileExtension}" - val file = batchesFolder.getInternalFolder().resolve(fileName).apply { - createNewFile() - } - - return FileOutputOptions.Builder(file).build() - } - companion object { const val CAMERA_CLOSE_TIMEOUT = 20000L } From 4950dc3505c7095ac8d8429c455b0dc487ac20dd Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 21:59:49 +0100 Subject: [PATCH 104/176] refactor: Outsource check into Constants.kt --- .../java/app/myzel394/alibi/services/VideoRecorderService.kt | 5 +++-- app/src/main/java/app/myzel394/alibi/ui/Constants.kt | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index acb0367eb..d0712872b 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -28,6 +28,7 @@ import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder +import app.myzel394.alibi.ui.VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -227,12 +228,12 @@ class VideoRecorderService : } } - @SuppressLint("MissingPermission") + @SuppressLint("MissingPermission", "NewApi") private fun prepareVideoRecording() = videoCapture!!.output .let { // TODO: Add hint - if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER) { it.prepareRecording( this, FileDescriptorOutputOptions.Builder( diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index 28a440413..af5106903 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -8,6 +8,7 @@ val BIG_PRIMARY_BUTTON_SIZE = 64.dp val MAX_AMPLITUDE = 20000 val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q val RECORDER_SUBFOLDER_NAME = ".recordings" +val VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O // You are not allowed to change the constants below. // If you do so, you will be blocked on GitHub. From 8b4c46a931dbc4a378f2ee6e2d2f5fdf8be6dd37 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 22:37:46 +0100 Subject: [PATCH 105/176] feat: Add hint for old Android version for video recorder custom folder notice --- .../SettingsScreen/Tiles/SaveFolderTile.kt | 35 +++++++++++++++++++ app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 37 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 8ffa9d040..0401d6a53 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -4,8 +4,10 @@ import android.content.Intent import android.net.Uri import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer 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.material.icons.Icons @@ -28,6 +30,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -36,6 +39,7 @@ import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.ui.VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER import app.myzel394.alibi.ui.components.atoms.SettingsTile import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog import kotlinx.coroutines.launch @@ -214,6 +218,37 @@ fun SaveFolderTile( modifier = Modifier.fillMaxWidth(), ) } + if (!VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER) { + Row( + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp), + ) { + Icon( + Icons.Default.Warning, + contentDescription = null, + tint = Color.Yellow, + modifier = Modifier.size(ButtonDefaults.IconSize), + ) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + Text( + stringResource(R.string.ui_settings_option_saveFolder_videoUnsupported), + style = MaterialTheme.typography.bodySmall, + color = Color.Yellow, + ) + Text( + stringResource(R.string.ui_minApiRequired, 8, 26), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurface, + ) + } + } + } } } ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ced0bc158..a41f71281 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -167,4 +167,6 @@ Please enroll a password or a biometric unlock method first Verification required You need to verify your identity to continue + Custom folders for Video Recordings aren\'t supported by your Android version. Alibi will fallback to the internal folder instead for Video Recordings. + You will need an Android phone running at least Android %s (API-Level: %s) to use this feature \ No newline at end of file From cb9a86be675c7463887467cd723b2994ba9bbf88 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Dec 2023 23:00:37 +0100 Subject: [PATCH 106/176] fix: Unify save button with delete button in RecordingControl --- .../alibi/ui/components/RecorderScreen/atoms/SaveButton.kt | 7 ------- .../RecorderScreen/molecules/RecordingControl.kt | 7 ------- app/src/main/res/values/strings.xml | 2 +- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt index 7304bf998..b881635cd 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt @@ -33,13 +33,6 @@ fun SaveButton( onClick = onSave, colors = ButtonDefaults.textButtonColors(), ) { - Icon( - Icons.Default.Save, - contentDescription = null, - modifier = Modifier - .size(ButtonDefaults.IconSize) - ) - Spacer(Modifier.width(ButtonDefaults.IconSpacing)) Text( label, fontSize = MaterialTheme.typography.bodySmall.fontSize, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt index a13d3ff4c..ad9fa7b49 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/RecordingControl.kt @@ -1,20 +1,14 @@ package app.myzel394.alibi.ui.components.RecorderScreen.molecules -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.EaseOutElastic import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.spring import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -26,7 +20,6 @@ import app.myzel394.alibi.ui.components.RecorderScreen.atoms.SaveButton import app.myzel394.alibi.ui.utils.RandomStack import app.myzel394.alibi.ui.utils.rememberInitialRecordingAnimation import kotlinx.coroutines.delay -import kotlinx.coroutines.launch @Composable fun RecordingControl( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a41f71281..5e2bfbd4f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,7 +25,7 @@ Are you sure you want to delete this recording? Pause Resume - Save Recording + Save Alibi will continue recording in the background and store the last\u0020 %s minutes From b1167577ef01aae36ddbde27ca653ad73eeb7fa9 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 30 Dec 2023 19:10:46 +0100 Subject: [PATCH 107/176] fix: Delete cache file after using it --- app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index 1f851f3af..ce1f77e4b 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -82,6 +82,7 @@ class MediaConverter { command ) { session -> runCatching { + listFile.delete() } if (!ReturnCode.isSuccess(session!!.returnCode)) { From 99085b217675c504e21f66fa95c15686109b40fe Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 30 Dec 2023 20:36:50 +0100 Subject: [PATCH 108/176] feat: Add media option to SaveFolderTile --- app/src/main/java/app/myzel394/alibi/ui/Constants.kt | 2 ++ .../components/SettingsScreen/Tiles/SaveFolderTile.kt | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index af5106903..15ba10c60 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -9,6 +9,8 @@ val MAX_AMPLITUDE = 20000 val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q val RECORDER_SUBFOLDER_NAME = ".recordings" val VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O +val MEDIA_RECORDINGS_PREFIX = "alibi-recording-" +val RECORDER_MEDIA_SELECTED_VALUE = "_'media" // You are not allowed to change the constants below. // If you do so, you will be blocked on GitHub. diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 0401d6a53..2adef3d85 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -39,6 +39,7 @@ import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import app.myzel394.alibi.ui.VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER import app.myzel394.alibi.ui.components.atoms.SettingsTile import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog @@ -54,7 +55,7 @@ fun SaveFolderTile( val dataStore = context.dataStore fun updateValue(path: String?) { - if (settings.saveFolder != null) { + if (settings.saveFolder != null && settings.saveFolder != RECORDER_MEDIA_SELECTED_VALUE) { runCatching { context.contentResolver.releasePersistableUriPermission( Uri.parse(settings.saveFolder), @@ -63,7 +64,7 @@ fun SaveFolderTile( } } - if (path != null) { + if (path != null && path != RECORDER_MEDIA_SELECTED_VALUE) { context.contentResolver.takePersistableUriPermission( Uri.parse(path), Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION @@ -249,6 +250,12 @@ fun SaveFolderTile( } } } + + Button( + onClick = { updateValue(RECORDER_MEDIA_SELECTED_VALUE) } + ) { + Text("Use Media") + } } } ) From 7401454269198a02ba30bf6168141acce1f42e10 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 30 Dec 2023 20:39:33 +0100 Subject: [PATCH 109/176] feat: Add support for media folders to video recording --- .../alibi/helpers/AudioBatchesFolder.kt | 8 +- .../myzel394/alibi/helpers/BatchesFolder.kt | 144 ++++++++++++++++-- .../alibi/helpers/VideoBatchesFolder.kt | 78 +++++++++- .../alibi/services/AudioRecorderService.kt | 6 +- .../alibi/services/VideoRecorderService.kt | 41 ++++- .../organisms/RecorderEventsHandler.kt | 17 +++ .../alibi/ui/models/VideoRecorderModel.kt | 12 +- 7 files changed, 277 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt index b814c0f81..e29c05383 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -3,8 +3,8 @@ package app.myzel394.alibi.helpers import android.content.Context import android.net.Uri import android.os.ParcelFileDescriptor +import android.provider.MediaStore import androidx.documentfile.provider.DocumentFile -import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateAudioFiles import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles import com.arthenica.ffmpegkit.FFmpegKitConfig import java.io.FileDescriptor @@ -23,6 +23,7 @@ class AudioBatchesFolder( ) { override val concatenationFunction = ::concatenateVideoFiles override val ffmpegParameters = FFMPEG_PARAMETERS + override val mediaContentUri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI private var customFileFileDescriptor: ParcelFileDescriptor? = null @@ -32,6 +33,7 @@ class AudioBatchesFolder( ): String { return when (type) { BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath + BatchType.CUSTOM -> { val name = getName(date, extension) @@ -43,6 +45,10 @@ class AudioBatchesFolder( )!!).uri )!! } + + BatchType.MEDIA -> { + return "" + } } } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 892e40aec..f7257da3b 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -1,16 +1,18 @@ package app.myzel394.alibi.helpers +import app.myzel394.alibi.ui.MEDIA_RECORDINGS_PREFIX + import android.content.Context +import android.database.Cursor import android.net.Uri +import android.provider.MediaStore.Video.Media import androidx.documentfile.provider.DocumentFile import java.io.File import java.time.LocalDateTime import java.time.format.DateTimeFormatter import com.arthenica.ffmpegkit.FFmpegKitConfig -import android.os.ParcelFileDescriptor import android.util.Log import kotlinx.coroutines.CompletableDeferred -import java.io.FileDescriptor import kotlin.reflect.KFunction3 abstract class BatchesFolder( @@ -21,15 +23,24 @@ abstract class BatchesFolder( ) { abstract val concatenationFunction: KFunction3, String, String, CompletableDeferred> abstract val ffmpegParameters: Array + abstract val mediaContentUri: Uri + + val mediaPrefix + get() = MEDIA_RECORDINGS_PREFIX + subfolderName fun initFolders() { when (type) { BatchType.INTERNAL -> getInternalFolder().mkdirs() + BatchType.CUSTOM -> { if (customFolder!!.findFile(subfolderName) == null) { customFolder!!.createDirectory(subfolderName) } } + + BatchType.MEDIA -> { + // Add support for < Android 10 + } } } @@ -41,6 +52,52 @@ abstract class BatchesFolder( return customFolder!!.findFile(subfolderName)!! } + protected fun queryMediaContent( + callback: (rawName: String, counter: Int, uri: Uri, cursor: Cursor) -> Any?, + ) { + context.contentResolver.query( + mediaContentUri, + null, + null, + null, + null, + )!!.use { cursor -> + while (cursor.moveToNext()) { + val rawName = cursor.getColumnIndex(Media.DISPLAY_NAME).let { id -> + if (id == -1) "" else cursor.getString(id) + } + + if (rawName == "" || rawName == null) { + continue + } + + if (!rawName.startsWith(mediaPrefix)) { + continue + } + + val counter = + rawName.substringAfter(mediaPrefix).substringBeforeLast(".").toIntOrNull() + ?: continue + + val id = cursor.getColumnIndex(Media._ID).let { id -> + if (id == -1) "" else cursor.getString(id) + } + + if (id == "" || id == null) { + continue + } + + val uri = Uri.withAppendedPath(mediaContentUri, id) + + val result = callback(rawName, counter, uri, cursor) + + if (result != null) { + return + } + } + } + } + fun getBatchesForFFmpeg(): List { return when (type) { BatchType.INTERNAL -> @@ -64,6 +121,21 @@ abstract class BatchesFolder( it.uri, )!! } + + BatchType.MEDIA -> { + val filePaths = mutableListOf() + + queryMediaContent { _, _, uri, _ -> + filePaths.add( + FFmpegKitConfig.getSafParameterForRead( + context, + uri, + )!! + ) + } + + filePaths + } } } @@ -81,27 +153,36 @@ abstract class BatchesFolder( return File(getInternalFolder(), getName(date, extension)) } - fun asCustomGetOutputFile( - date: LocalDateTime, - extension: String, - ): DocumentFile { - return getCustomDefinedFolder().createFile("audio/$extension", getName(date, extension))!! - } - fun checkIfOutputAlreadyExists( date: LocalDateTime, extension: String ): Boolean { - val name = date + val stem = date .format(DateTimeFormatter.ISO_DATE_TIME) .toString() .replace(":", "-") .replace(".", "_") + val fileName = "$stem.$extension" return when (type) { - BatchType.INTERNAL -> File(getInternalFolder(), "$name.$extension").exists() + BatchType.INTERNAL -> File(getInternalFolder(), fileName).exists() + BatchType.CUSTOM -> - getCustomDefinedFolder().findFile("${name}.${extension}")?.exists() ?: false + getCustomDefinedFolder().findFile(fileName)?.exists() ?: false + + BatchType.MEDIA -> { + var exists = false + + queryMediaContent { rawName, _, _, _ -> + if (rawName == fileName) { + exists = true + return@queryMediaContent true + } else { + } + } + + exists + } } } @@ -154,24 +235,48 @@ abstract class BatchesFolder( return when (type) { BatchType.INTERNAL -> "_'internal" BatchType.CUSTOM -> customFolder!!.uri.toString() + BatchType.MEDIA -> "_'media" } } fun deleteRecordings() { when (type) { BatchType.INTERNAL -> getInternalFolder().deleteRecursively() + BatchType.CUSTOM -> customFolder?.findFile(subfolderName)?.delete() ?: customFolder?.findFile(subfolderName)?.listFiles()?.forEach { it.delete() } + + BatchType.MEDIA -> { + queryMediaContent { _, _, uri, _ -> + context.contentResolver.delete( + uri, + null, + null, + ) + } + } } } fun hasRecordingsAvailable(): Boolean { return when (type) { BatchType.INTERNAL -> getInternalFolder().listFiles()?.isNotEmpty() ?: false + BatchType.CUSTOM -> customFolder?.findFile(subfolderName)?.listFiles()?.isNotEmpty() ?: false + + BatchType.MEDIA -> { + var hasRecordings = false + + queryMediaContent { _, _, _, _ -> + hasRecordings = true + return@queryMediaContent true + } + + hasRecordings + } } } @@ -192,6 +297,18 @@ abstract class BatchesFolder( it.delete() } } + + BatchType.MEDIA -> { + queryMediaContent { _, counter, uri, _ -> + if (counter < earliestCounter) { + context.contentResolver.delete( + uri, + null, + null, + ) + } + } + } } } @@ -199,6 +316,8 @@ abstract class BatchesFolder( return when (type) { BatchType.INTERNAL -> true BatchType.CUSTOM -> getCustomDefinedFolder().canWrite() && getCustomDefinedFolder().canRead() + // Add support for < Android 10 + BatchType.MEDIA -> true } } @@ -209,6 +328,7 @@ abstract class BatchesFolder( enum class BatchType { INTERNAL, CUSTOM, + MEDIA, } } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index 8c6936968..5fed2cd18 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -1,17 +1,20 @@ package app.myzel394.alibi.helpers +import android.content.ContentUris import android.content.Context import android.net.Uri +import android.os.Environment import android.os.ParcelFileDescriptor +import android.provider.MediaStore import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles +import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import com.arthenica.ffmpegkit.FFmpegKitConfig -import com.arthenica.ffmpegkit.ReturnCode import java.time.LocalDateTime class VideoBatchesFolder( override val context: Context, - override val type: BatchesFolder.BatchType, + override val type: BatchType, override val customFolder: DocumentFile? = null, override val subfolderName: String = ".video_recordings", ) : BatchesFolder( @@ -22,12 +25,14 @@ class VideoBatchesFolder( ) { override val concatenationFunction = ::concatenateVideoFiles override val ffmpegParameters = FFMPEG_PARAMETERS + override val mediaContentUri: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI private var customParcelFileDescriptor: ParcelFileDescriptor? = null override fun getOutputFileForFFmpeg(date: LocalDateTime, extension: String): String { return when (type) { BatchType.INTERNAL -> asInternalGetOutputFile(date, extension).absolutePath + BatchType.CUSTOM -> { val name = getName(date, extension) @@ -39,6 +44,72 @@ class VideoBatchesFolder( )!!).uri )!! } + + BatchType.MEDIA -> { + val name = getName(date, extension) + + // Check if already exists + var uri: Uri? = null + context.contentResolver.query( + mediaContentUri, + null, + // TODO: Improve + null, + null, + null, + )!!.use { cursor -> + while (cursor.moveToNext()) { + val id = cursor.getColumnIndex(MediaStore.MediaColumns._ID) + + if (id == -1) { + continue + } + + val nameID = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME) + + if (nameID == -1) { + continue + } + + val cursorName = cursor.getString(nameID) + + if (cursorName != name) { + continue + } + + uri = ContentUris.withAppendedId( + mediaContentUri, + cursor.getLong(id) + ) + return@use + } + } + + if (uri == null) { + uri = context.contentResolver.insert( + mediaContentUri, + android.content.ContentValues().apply { + put( + MediaStore.MediaColumns.DISPLAY_NAME, + name + ) + put( + MediaStore.MediaColumns.MIME_TYPE, + "video/$extension" + ) + put( + MediaStore.Video.Media.RELATIVE_PATH, + Environment.DIRECTORY_DCIM + "/alibi/video_recordings" + ) + } + )!! + } + + FFmpegKitConfig.getSafParameterForWrite( + context, + uri + )!! + } } } @@ -76,8 +147,11 @@ class VideoBatchesFolder( fun viaCustomFolder(context: Context, folder: DocumentFile) = VideoBatchesFolder(context, BatchType.CUSTOM, folder) + fun viaMediaFolder(context: Context) = VideoBatchesFolder(context, BatchType.MEDIA) + fun importFromFolder(folder: String, context: Context) = when (folder) { "_'internal" -> viaInternalFolder(context) + RECORDER_MEDIA_SELECTED_VALUE -> viaMediaFolder(context) else -> viaCustomFolder( context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!! diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index 50fec5b4f..a203697c8 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -1,7 +1,6 @@ package app.myzel394.alibi.services import android.content.Context -import android.content.Context.AUDIO_SERVICE import android.content.pm.ServiceInfo import android.media.AudioDeviceCallback import android.media.AudioDeviceInfo @@ -12,9 +11,7 @@ import android.os.Build import android.os.Handler import android.os.Looper import androidx.core.app.ServiceCompat -import androidx.core.content.ContextCompat.getSystemService import app.myzel394.alibi.NotificationHelper -import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.AudioBatchesFolder @@ -198,6 +195,9 @@ class AudioRecorderService : ) ) } + + // TODO: Add media + else -> {} } setOutputFormat(audioSettings.getOutputFormat()) diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index d0712872b..ac2d4ff4c 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -1,20 +1,20 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint +import android.content.ContentValues import android.content.Intent import android.content.pm.ServiceInfo import android.os.Build -import android.os.ParcelFileDescriptor +import android.os.Environment +import android.provider.MediaStore import android.util.Range import androidx.camera.core.Camera import androidx.camera.core.CameraSelector import androidx.camera.core.TorchState -import androidx.camera.core.processing.SurfaceProcessorNode.Out import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.video.FileDescriptorOutputOptions import androidx.camera.video.FileOutputOptions import androidx.camera.video.MediaStoreOutputOptions -import androidx.camera.video.OutputOptions import androidx.camera.video.Quality import androidx.camera.video.QualitySelector import androidx.camera.video.Recorder @@ -36,7 +36,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull -import java.nio.file.Files.createFile import kotlin.properties.Delegates class VideoRecorderService : @@ -232,7 +231,6 @@ class VideoRecorderService : private fun prepareVideoRecording() = videoCapture!!.output .let { - // TODO: Add hint if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER) { it.prepareRecording( this, @@ -243,6 +241,39 @@ class VideoRecorderService : ) ).build() ) + } else if (batchesFolder.type == BatchesFolder.BatchType.MEDIA) { + it.prepareRecording( + this, + MediaStoreOutputOptions.Builder( + contentResolver, + batchesFolder.mediaContentUri, + ).setContentValues( + ContentValues().apply { + val name = + "${batchesFolder.mediaPrefix}$counter.${settings.videoRecorderSettings.fileExtension}" + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + put( + MediaStore.Video.Media.IS_PENDING, + 1 + ) + put( + MediaStore.Video.Media.RELATIVE_PATH, + Environment.DIRECTORY_DCIM + "/alibi/video_recordings" + ) + put( + MediaStore.Video.Media.DISPLAY_NAME, + name + ) + } else { + put( + MediaStore.Video.Media.DISPLAY_NAME, + name + ) + } + } + ).build() + ) } else { it.prepareRecording( this, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt index 186ec30ac..20dd06c92 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt @@ -104,6 +104,15 @@ fun RecorderEventsHandler( context.startActivity(intent) } + fun showSnackbar() { + scope.launch { + snackbarHostState.showSnackbar( + message = successMessage, + duration = SnackbarDuration.Short, + ) + } + } + fun showSnackbar(uri: Uri) { scope.launch { val result = snackbarHostState.showSnackbar( @@ -186,6 +195,14 @@ fun RecorderEventsHandler( batchesFolder.deleteRecordings() } } + + BatchesFolder.BatchType.MEDIA -> { + showSnackbar() + + if (settings.deleteRecordingsImmediately) { + batchesFolder.deleteRecordings() + } + } } } catch (error: Exception) { Log.getStackTraceString(error) diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt index 4550674c9..be787f8cd 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt @@ -13,10 +13,9 @@ import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState -import app.myzel394.alibi.helpers.AudioBatchesFolder -import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder import app.myzel394.alibi.services.VideoRecorderService +import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import app.myzel394.alibi.ui.utils.CameraInfo import app.myzel394.alibi.ui.utils.PermissionHelper @@ -43,16 +42,17 @@ class VideoRecorderModel : } override fun startRecording(context: Context, settings: AppSettings) { - batchesFolder = if (settings.saveFolder == null) - VideoBatchesFolder.viaInternalFolder(context) - else - VideoBatchesFolder.viaCustomFolder( + batchesFolder = when (settings.saveFolder) { + null -> VideoBatchesFolder.viaInternalFolder(context) + RECORDER_MEDIA_SELECTED_VALUE -> VideoBatchesFolder.viaMediaFolder(context) + else -> VideoBatchesFolder.viaCustomFolder( context, DocumentFile.fromTreeUri( context, Uri.parse(settings.saveFolder) )!! ) + } super.startRecording(context, settings) } From 35614a7b7af0ac07bb738ee01b4c0322a612c8bd Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 30 Dec 2023 20:59:20 +0100 Subject: [PATCH 110/176] fix: Make constant name easier to read for end users --- app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index f7257da3b..c9846351f 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -26,7 +26,7 @@ abstract class BatchesFolder( abstract val mediaContentUri: Uri val mediaPrefix - get() = MEDIA_RECORDINGS_PREFIX + subfolderName + get() = MEDIA_RECORDINGS_PREFIX + subfolderName + "-" fun initFolders() { when (type) { From c6dca0fc77dd95f4c42014b58f0bc2155677f849 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 30 Dec 2023 20:59:59 +0100 Subject: [PATCH 111/176] refactor: Small code improvements, make code more dry --- .../alibi/services/VideoRecorderService.kt | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index ac2d4ff4c..d342b8c6e 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -259,18 +259,14 @@ class VideoRecorderService : ) put( MediaStore.Video.Media.RELATIVE_PATH, - Environment.DIRECTORY_DCIM + "/alibi/video_recordings" - ) - put( - MediaStore.Video.Media.DISPLAY_NAME, - name - ) - } else { - put( - MediaStore.Video.Media.DISPLAY_NAME, - name + VideoBatchesFolder.MEDIA_RELATIVE_PATH, ) } + + put( + MediaStore.Video.Media.DISPLAY_NAME, + name + ) } ).build() ) From 329b41b4c81a8e41c81b53c82cebdc7d60cc6119 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 30 Dec 2023 21:00:13 +0100 Subject: [PATCH 112/176] feat: Improve check for existing file query --- .../alibi/helpers/VideoBatchesFolder.kt | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index 5fed2cd18..9cc7c59f0 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -3,6 +3,7 @@ package app.myzel394.alibi.helpers import android.content.ContentUris import android.content.Context import android.net.Uri +import android.os.Build import android.os.Environment import android.os.ParcelFileDescriptor import android.provider.MediaStore @@ -52,40 +53,29 @@ class VideoBatchesFolder( var uri: Uri? = null context.contentResolver.query( mediaContentUri, - null, + arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME), // TODO: Improve - null, + "${MediaStore.MediaColumns.DISPLAY_NAME} = '$name'", null, null, )!!.use { cursor -> - while (cursor.moveToNext()) { + if (cursor.moveToFirst()) { + // No need to check for the name since the query already did that val id = cursor.getColumnIndex(MediaStore.MediaColumns._ID) if (id == -1) { - continue - } - - val nameID = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME) - - if (nameID == -1) { - continue - } - - val cursorName = cursor.getString(nameID) - - if (cursorName != name) { - continue + return@use } uri = ContentUris.withAppendedId( mediaContentUri, cursor.getLong(id) ) - return@use } } if (uri == null) { + // Create empty output file to be able to write to it uri = context.contentResolver.insert( mediaContentUri, android.content.ContentValues().apply { @@ -97,10 +87,13 @@ class VideoBatchesFolder( MediaStore.MediaColumns.MIME_TYPE, "video/$extension" ) - put( - MediaStore.Video.Media.RELATIVE_PATH, - Environment.DIRECTORY_DCIM + "/alibi/video_recordings" - ) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + put( + MediaStore.Video.Media.RELATIVE_PATH, + MEDIA_RELATIVE_PATH, + ) + } } )!! } @@ -158,6 +151,8 @@ class VideoBatchesFolder( ) } + val MEDIA_RELATIVE_PATH = Environment.DIRECTORY_DCIM + "/alibi/video_recordings" + // Parameters to be passed in descending order // Those parameters first try to concatenate without re-encoding // if that fails, it'll try several fallback methods From 7c6e44dd6928d3d34d47f487a60968bee5b3d558 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 30 Dec 2023 21:04:24 +0100 Subject: [PATCH 113/176] refactor: Outsource into getOrCreateNewMediaFile method --- .../myzel394/alibi/helpers/BatchesFolder.kt | 60 +++++++++++++++++++ .../alibi/helpers/VideoBatchesFolder.kt | 59 ++---------------- 2 files changed, 66 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index c9846351f..58387f296 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -1,10 +1,13 @@ package app.myzel394.alibi.helpers +import android.content.ContentUris import app.myzel394.alibi.ui.MEDIA_RECORDINGS_PREFIX import android.content.Context import android.database.Cursor import android.net.Uri +import android.os.Build +import android.provider.MediaStore import android.provider.MediaStore.Video.Media import androidx.documentfile.provider.DocumentFile import java.io.File @@ -325,6 +328,63 @@ abstract class BatchesFolder( return File(getInternalFolder(), "$counter.$fileExtension") } + protected fun getOrCreateMediaFile( + name: String, + mimeType: String, + relativePath: String, + ): Uri { + // Check if already exists + var uri: Uri? = null + + context.contentResolver.query( + mediaContentUri, + arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME), + "${MediaStore.MediaColumns.DISPLAY_NAME} = '$name'", + null, + null, + )!!.use { cursor -> + if (cursor.moveToFirst()) { + // No need to check for the name since the query already did that + val id = cursor.getColumnIndex(MediaStore.MediaColumns._ID) + + if (id == -1) { + return@use + } + + uri = ContentUris.withAppendedId( + mediaContentUri, + cursor.getLong(id) + ) + } + } + + if (uri == null) { + // Create empty output file to be able to write to it + uri = context.contentResolver.insert( + mediaContentUri, + android.content.ContentValues().apply { + put( + MediaStore.MediaColumns.DISPLAY_NAME, + name + ) + put( + MediaStore.MediaColumns.MIME_TYPE, + mimeType + ) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + put( + Media.RELATIVE_PATH, + relativePath, + ) + } + } + )!! + } + + return uri!! + } + enum class BatchType { INTERNAL, CUSTOM, diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index 9cc7c59f0..ae91e6320 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -1,9 +1,7 @@ package app.myzel394.alibi.helpers -import android.content.ContentUris import android.content.Context import android.net.Uri -import android.os.Build import android.os.Environment import android.os.ParcelFileDescriptor import android.provider.MediaStore @@ -47,60 +45,15 @@ class VideoBatchesFolder( } BatchType.MEDIA -> { - val name = getName(date, extension) - - // Check if already exists - var uri: Uri? = null - context.contentResolver.query( - mediaContentUri, - arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME), - // TODO: Improve - "${MediaStore.MediaColumns.DISPLAY_NAME} = '$name'", - null, - null, - )!!.use { cursor -> - if (cursor.moveToFirst()) { - // No need to check for the name since the query already did that - val id = cursor.getColumnIndex(MediaStore.MediaColumns._ID) - - if (id == -1) { - return@use - } - - uri = ContentUris.withAppendedId( - mediaContentUri, - cursor.getLong(id) - ) - } - } - - if (uri == null) { - // Create empty output file to be able to write to it - uri = context.contentResolver.insert( - mediaContentUri, - android.content.ContentValues().apply { - put( - MediaStore.MediaColumns.DISPLAY_NAME, - name - ) - put( - MediaStore.MediaColumns.MIME_TYPE, - "video/$extension" - ) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - put( - MediaStore.Video.Media.RELATIVE_PATH, - MEDIA_RELATIVE_PATH, - ) - } - } - )!! - } + val mediaUri = getOrCreateMediaFile( + name = getName(date, extension), + mimeType = "video/$extension", + relativePath = MEDIA_RELATIVE_PATH, + ) FFmpegKitConfig.getSafParameterForWrite( context, - uri + mediaUri )!! } } From 9d4345c2d1b1d487465cb7f46ac5903423439739 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 30 Dec 2023 21:07:30 +0100 Subject: [PATCH 114/176] refactor: Use constants instead of hardcoded values --- .../main/java/app/myzel394/alibi/helpers/BatchesFolder.kt | 6 ++++-- .../java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt | 3 ++- app/src/main/java/app/myzel394/alibi/ui/Constants.kt | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 58387f296..e960ee52e 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -15,6 +15,8 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import com.arthenica.ffmpegkit.FFmpegKitConfig import android.util.Log +import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE +import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import kotlinx.coroutines.CompletableDeferred import kotlin.reflect.KFunction3 @@ -236,9 +238,9 @@ abstract class BatchesFolder( fun exportFolderForSettings(): String { return when (type) { - BatchType.INTERNAL -> "_'internal" + BatchType.INTERNAL -> RECORDER_INTERNAL_SELECTED_VALUE + BatchType.MEDIA -> RECORDER_MEDIA_SELECTED_VALUE BatchType.CUSTOM -> customFolder!!.uri.toString() - BatchType.MEDIA -> "_'media" } } diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index ae91e6320..10311d3fa 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -7,6 +7,7 @@ import android.os.ParcelFileDescriptor import android.provider.MediaStore import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles +import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import com.arthenica.ffmpegkit.FFmpegKitConfig import java.time.LocalDateTime @@ -96,7 +97,7 @@ class VideoBatchesFolder( fun viaMediaFolder(context: Context) = VideoBatchesFolder(context, BatchType.MEDIA) fun importFromFolder(folder: String, context: Context) = when (folder) { - "_'internal" -> viaInternalFolder(context) + RECORDER_INTERNAL_SELECTED_VALUE -> viaInternalFolder(context) RECORDER_MEDIA_SELECTED_VALUE -> viaMediaFolder(context) else -> viaCustomFolder( context, diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index 15ba10c60..9c62b5548 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -11,6 +11,7 @@ val RECORDER_SUBFOLDER_NAME = ".recordings" val VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O val MEDIA_RECORDINGS_PREFIX = "alibi-recording-" val RECORDER_MEDIA_SELECTED_VALUE = "_'media" +val RECORDER_INTERNAL_SELECTED_VALUE = "_'internal" // You are not allowed to change the constants below. // If you do so, you will be blocked on GitHub. From ef6487903e3d76fdf38af1a8248337a598da272e Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 00:16:37 +0100 Subject: [PATCH 115/176] fix: Improvements --- app/src/main/AndroidManifest.xml | 8 ++++++++ .../java/app/myzel394/alibi/db/AppSettings.kt | 4 ++-- .../myzel394/alibi/helpers/BatchesFolder.kt | 19 ++++++++----------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fdab32fc1..4e2c5ab3f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,6 +26,14 @@ + + + + = Build.VERSION_CODES.Q) "mp4" else "3gp" companion object { fun getDefaultInstance() = VideoRecorderSettings() diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index e960ee52e..016d9151f 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -1,6 +1,7 @@ package app.myzel394.alibi.helpers import android.content.ContentUris +import android.content.ContentValues import app.myzel394.alibi.ui.MEDIA_RECORDINGS_PREFIX import android.content.Context @@ -31,7 +32,7 @@ abstract class BatchesFolder( abstract val mediaContentUri: Uri val mediaPrefix - get() = MEDIA_RECORDINGS_PREFIX + subfolderName + "-" + get() = MEDIA_RECORDINGS_PREFIX + subfolderName.substring(1) + "-" fun initFolders() { when (type) { @@ -69,14 +70,10 @@ abstract class BatchesFolder( )!!.use { cursor -> while (cursor.moveToNext()) { val rawName = cursor.getColumnIndex(Media.DISPLAY_NAME).let { id -> - if (id == -1) "" else cursor.getString(id) + if (id == -1) null else cursor.getString(id) } - if (rawName == "" || rawName == null) { - continue - } - - if (!rawName.startsWith(mediaPrefix)) { + if (rawName.isNullOrBlank() || !rawName.startsWith(mediaPrefix)) { continue } @@ -85,10 +82,10 @@ abstract class BatchesFolder( ?: continue val id = cursor.getColumnIndex(Media._ID).let { id -> - if (id == -1) "" else cursor.getString(id) + if (id == -1) null else cursor.getString(id) } - if (id == "" || id == null) { + if (id.isNullOrBlank()) { continue } @@ -330,7 +327,7 @@ abstract class BatchesFolder( return File(getInternalFolder(), "$counter.$fileExtension") } - protected fun getOrCreateMediaFile( + fun getOrCreateMediaFile( name: String, mimeType: String, relativePath: String, @@ -364,7 +361,7 @@ abstract class BatchesFolder( // Create empty output file to be able to write to it uri = context.contentResolver.insert( mediaContentUri, - android.content.ContentValues().apply { + ContentValues().apply { put( MediaStore.MediaColumns.DISPLAY_NAME, name From 4681a1d924c359efbf50e46cc27e1459f2fdc377 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 15:49:28 +0100 Subject: [PATCH 116/176] fix: Fix legacy storage support --- .../alibi/helpers/AudioBatchesFolder.kt | 9 +- .../myzel394/alibi/helpers/BatchesFolder.kt | 119 ++++++++++++------ .../alibi/helpers/VideoBatchesFolder.kt | 40 ++++-- .../alibi/services/VideoRecorderService.kt | 73 ++++++----- .../java/app/myzel394/alibi/ui/Constants.kt | 5 +- .../SettingsScreen/Tiles/SaveFolderTile.kt | 5 +- 6 files changed, 166 insertions(+), 85 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt index e29c05383..14ef3eb1d 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -4,9 +4,12 @@ import android.content.Context import android.net.Uri import android.os.ParcelFileDescriptor import android.provider.MediaStore +import androidx.core.net.toFile import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles +import app.myzel394.alibi.helpers.VideoBatchesFolder.Companion.MEDIA_SUBFOLDER import com.arthenica.ffmpegkit.FFmpegKitConfig +import java.io.File import java.io.FileDescriptor import java.time.LocalDateTime @@ -23,7 +26,11 @@ class AudioBatchesFolder( ) { override val concatenationFunction = ::concatenateVideoFiles override val ffmpegParameters = FFMPEG_PARAMETERS - override val mediaContentUri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + override val scopedMediaContentUri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + override val legacyMediaFolder = File( + scopedMediaContentUri.toFile(), + MEDIA_SUBFOLDER + "/" + subfolderName + ) private var customFileFileDescriptor: ParcelFileDescriptor? = null diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 016d9151f..5e4bd1c54 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -16,8 +16,11 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import com.arthenica.ffmpegkit.FFmpegKitConfig import android.util.Log +import androidx.annotation.RequiresApi +import androidx.core.net.toFile import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE +import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import kotlinx.coroutines.CompletableDeferred import kotlin.reflect.KFunction3 @@ -29,7 +32,8 @@ abstract class BatchesFolder( ) { abstract val concatenationFunction: KFunction3, String, String, CompletableDeferred> abstract val ffmpegParameters: Array - abstract val mediaContentUri: Uri + abstract val scopedMediaContentUri: Uri + abstract val legacyMediaFolder: File val mediaPrefix get() = MEDIA_RECORDINGS_PREFIX + subfolderName.substring(1) + "-" @@ -45,7 +49,11 @@ abstract class BatchesFolder( } BatchType.MEDIA -> { - // Add support for < Android 10 + // Scoped storage works fine on new Android versions, + // we need to manually manage the folder on older versions + if (!SUPPORTS_SCOPED_STORAGE) { + legacyMediaFolder.mkdirs() + } } } } @@ -58,11 +66,12 @@ abstract class BatchesFolder( return customFolder!!.findFile(subfolderName)!! } + @RequiresApi(Build.VERSION_CODES.Q) protected fun queryMediaContent( callback: (rawName: String, counter: Int, uri: Uri, cursor: Cursor) -> Any?, ) { context.contentResolver.query( - mediaContentUri, + scopedMediaContentUri, null, null, null, @@ -89,7 +98,7 @@ abstract class BatchesFolder( continue } - val uri = Uri.withAppendedPath(mediaContentUri, id) + val uri = Uri.withAppendedPath(scopedMediaContentUri, id) val result = callback(rawName, counter, uri, cursor) @@ -127,13 +136,19 @@ abstract class BatchesFolder( BatchType.MEDIA -> { val filePaths = mutableListOf() - queryMediaContent { _, _, uri, _ -> - filePaths.add( - FFmpegKitConfig.getSafParameterForRead( - context, - uri, - )!! - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + queryMediaContent { _, _, uri, _ -> + filePaths.add( + FFmpegKitConfig.getSafParameterForRead( + context, + uri, + )!! + ) + } + } else { + legacyMediaFolder.listFiles()?.forEach { + filePaths.add(it.absolutePath) + } } filePaths @@ -175,15 +190,22 @@ abstract class BatchesFolder( BatchType.MEDIA -> { var exists = false - queryMediaContent { rawName, _, _, _ -> - if (rawName == fileName) { - exists = true - return@queryMediaContent true - } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + queryMediaContent { rawName, _, _, _ -> + if (rawName == fileName) { + exists = true + return@queryMediaContent true + } else { + } } - } - exists + return exists + } else { + return File( + legacyMediaFolder, + fileName, + ).exists() + } } } } @@ -251,12 +273,16 @@ abstract class BatchesFolder( } BatchType.MEDIA -> { - queryMediaContent { _, _, uri, _ -> - context.contentResolver.delete( - uri, - null, - null, - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + queryMediaContent { _, _, uri, _ -> + context.contentResolver.delete( + uri, + null, + null, + ) + } + } else { + legacyMediaFolder.deleteRecursively() } } } @@ -272,12 +298,16 @@ abstract class BatchesFolder( BatchType.MEDIA -> { var hasRecordings = false - queryMediaContent { _, _, _, _ -> - hasRecordings = true - return@queryMediaContent true - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + queryMediaContent { _, _, _, _ -> + hasRecordings = true + return@queryMediaContent true + } - hasRecordings + return hasRecordings + } else { + return legacyMediaFolder.listFiles()?.isNotEmpty() ?: false + } } } } @@ -301,13 +331,23 @@ abstract class BatchesFolder( } BatchType.MEDIA -> { - queryMediaContent { _, counter, uri, _ -> - if (counter < earliestCounter) { - context.contentResolver.delete( - uri, - null, - null, - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + queryMediaContent { _, counter, uri, _ -> + if (counter < earliestCounter) { + context.contentResolver.delete( + uri, + null, + null, + ) + } + } + } else { + legacyMediaFolder.listFiles()?.forEach { + val fileCounter = it.nameWithoutExtension.toIntOrNull() ?: return@forEach + + if (fileCounter < earliestCounter) { + it.delete() + } } } } @@ -327,6 +367,7 @@ abstract class BatchesFolder( return File(getInternalFolder(), "$counter.$fileExtension") } + @RequiresApi(Build.VERSION_CODES.Q) fun getOrCreateMediaFile( name: String, mimeType: String, @@ -336,7 +377,7 @@ abstract class BatchesFolder( var uri: Uri? = null context.contentResolver.query( - mediaContentUri, + scopedMediaContentUri, arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME), "${MediaStore.MediaColumns.DISPLAY_NAME} = '$name'", null, @@ -351,7 +392,7 @@ abstract class BatchesFolder( } uri = ContentUris.withAppendedId( - mediaContentUri, + scopedMediaContentUri, cursor.getLong(id) ) } @@ -360,7 +401,7 @@ abstract class BatchesFolder( if (uri == null) { // Create empty output file to be able to write to it uri = context.contentResolver.insert( - mediaContentUri, + scopedMediaContentUri, ContentValues().apply { put( MediaStore.MediaColumns.DISPLAY_NAME, diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index 10311d3fa..dd3359506 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -2,6 +2,7 @@ package app.myzel394.alibi.helpers import android.content.Context import android.net.Uri +import android.os.Build import android.os.Environment import android.os.ParcelFileDescriptor import android.provider.MediaStore @@ -10,6 +11,7 @@ import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import com.arthenica.ffmpegkit.FFmpegKitConfig +import java.io.File import java.time.LocalDateTime class VideoBatchesFolder( @@ -25,7 +27,11 @@ class VideoBatchesFolder( ) { override val concatenationFunction = ::concatenateVideoFiles override val ffmpegParameters = FFMPEG_PARAMETERS - override val mediaContentUri: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI + override val scopedMediaContentUri: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI + override val legacyMediaFolder = File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), + MEDIA_SUBFOLDER + ) private var customParcelFileDescriptor: ParcelFileDescriptor? = null @@ -46,16 +52,25 @@ class VideoBatchesFolder( } BatchType.MEDIA -> { - val mediaUri = getOrCreateMediaFile( - name = getName(date, extension), - mimeType = "video/$extension", - relativePath = MEDIA_RELATIVE_PATH, - ) - - FFmpegKitConfig.getSafParameterForWrite( - context, - mediaUri - )!! + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val mediaUri = getOrCreateMediaFile( + name = getName(date, extension), + mimeType = "video/$extension", + relativePath = SCOPED_STORAGE_RELATIVE_PATH, + ) + + return FFmpegKitConfig.getSafParameterForWrite( + context, + mediaUri + )!! + } else { + return File( + legacyMediaFolder.parentFile!!, + getName(date, extension) + ).apply { + createNewFile() + }.absolutePath + } } } } @@ -105,7 +120,8 @@ class VideoBatchesFolder( ) } - val MEDIA_RELATIVE_PATH = Environment.DIRECTORY_DCIM + "/alibi/video_recordings" + val MEDIA_SUBFOLDER = "/alibi/.video_recordings" + val SCOPED_STORAGE_RELATIVE_PATH = Environment.DIRECTORY_DCIM + MEDIA_SUBFOLDER // Parameters to be passed in descending order // Those parameters first try to concatenate without re-encoding diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index d342b8c6e..faaef9141 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -5,7 +5,6 @@ import android.content.ContentValues import android.content.Intent import android.content.pm.ServiceInfo import android.os.Build -import android.os.Environment import android.provider.MediaStore import android.util.Range import androidx.camera.core.Camera @@ -28,7 +27,7 @@ import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder -import app.myzel394.alibi.ui.VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER +import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -36,6 +35,7 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull +import java.io.File import kotlin.properties.Delegates class VideoRecorderService : @@ -227,11 +227,14 @@ class VideoRecorderService : } } + private fun getNameForMediaFile() = + "${batchesFolder.mediaPrefix}$counter.${settings.videoRecorderSettings.fileExtension}" + @SuppressLint("MissingPermission", "NewApi") private fun prepareVideoRecording() = videoCapture!!.output .let { - if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER) { + if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && SUPPORTS_SCOPED_STORAGE) { it.prepareRecording( this, FileDescriptorOutputOptions.Builder( @@ -242,34 +245,48 @@ class VideoRecorderService : ).build() ) } else if (batchesFolder.type == BatchesFolder.BatchType.MEDIA) { - it.prepareRecording( - this, - MediaStoreOutputOptions.Builder( - contentResolver, - batchesFolder.mediaContentUri, - ).setContentValues( - ContentValues().apply { - val name = - "${batchesFolder.mediaPrefix}$counter.${settings.videoRecorderSettings.fileExtension}" - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - put( - MediaStore.Video.Media.IS_PENDING, - 1 - ) + if (SUPPORTS_SCOPED_STORAGE) { + it.prepareRecording( + this, + MediaStoreOutputOptions.Builder( + contentResolver, + batchesFolder.scopedMediaContentUri, + ).setContentValues( + ContentValues().apply { + val name = getNameForMediaFile() + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + put( + MediaStore.Video.Media.IS_PENDING, + 1 + ) + put( + MediaStore.Video.Media.RELATIVE_PATH, + VideoBatchesFolder.SCOPED_STORAGE_RELATIVE_PATH, + ) + } + put( - MediaStore.Video.Media.RELATIVE_PATH, - VideoBatchesFolder.MEDIA_RELATIVE_PATH, + MediaStore.Video.Media.DISPLAY_NAME, + name ) } - - put( - MediaStore.Video.Media.DISPLAY_NAME, - name - ) - } - ).build() - ) + ).build() + ) + } else { + val name = getNameForMediaFile() + val file = File( + batchesFolder.legacyMediaFolder, + name + ).apply { + createNewFile() + } + + it.prepareRecording( + this, + FileOutputOptions.Builder(file).build() + ) + } } else { it.prepareRecording( this, diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index 9c62b5548..c294b56e4 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -2,13 +2,14 @@ package app.myzel394.alibi.ui import android.os.Build import androidx.compose.ui.unit.dp -import java.io.File val BIG_PRIMARY_BUTTON_SIZE = 64.dp val MAX_AMPLITUDE = 20000 val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q val RECORDER_SUBFOLDER_NAME = ".recordings" -val VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + +// TODO: Fix! +val SUPPORTS_SCOPED_STORAGE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O val MEDIA_RECORDINGS_PREFIX = "alibi-recording-" val RECORDER_MEDIA_SELECTED_VALUE = "_'media" val RECORDER_INTERNAL_SELECTED_VALUE = "_'internal" diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 2adef3d85..765bb50df 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -35,12 +35,11 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE -import app.myzel394.alibi.ui.VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER +import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import app.myzel394.alibi.ui.components.atoms.SettingsTile import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog import kotlinx.coroutines.launch @@ -219,7 +218,7 @@ fun SaveFolderTile( modifier = Modifier.fillMaxWidth(), ) } - if (!VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER) { + if (!SUPPORTS_SCOPED_STORAGE) { Row( horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, From 61a63eeabb68987582cb025694763814b935a400 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 16:04:02 +0100 Subject: [PATCH 117/176] fix: Improve queries --- .../myzel394/alibi/helpers/BatchesFolder.kt | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 5e4bd1c54..10f333767 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -274,13 +274,11 @@ abstract class BatchesFolder( BatchType.MEDIA -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - queryMediaContent { _, _, uri, _ -> - context.contentResolver.delete( - uri, - null, - null, - ) - } + context.contentResolver.delete( + scopedMediaContentUri, + "${MediaStore.MediaColumns.DISPLAY_NAME} LIKE '$mediaPrefix%'", + null, + ) } else { legacyMediaFolder.deleteRecursively() } @@ -299,9 +297,16 @@ abstract class BatchesFolder( var hasRecordings = false if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - queryMediaContent { _, _, _, _ -> - hasRecordings = true - return@queryMediaContent true + context.contentResolver.query( + scopedMediaContentUri, + arrayOf(MediaStore.MediaColumns.DISPLAY_NAME), + "${MediaStore.MediaColumns.DISPLAY_NAME} LIKE '$mediaPrefix%'", + null, + null, + )!!.use { cursor -> + if (cursor.moveToFirst()) { + hasRecordings = true + } } return hasRecordings @@ -358,7 +363,7 @@ abstract class BatchesFolder( return when (type) { BatchType.INTERNAL -> true BatchType.CUSTOM -> getCustomDefinedFolder().canWrite() && getCustomDefinedFolder().canRead() - // Add support for < Android 10 + // TODO: Add support for < Android 10 BatchType.MEDIA -> true } } From 7c646835e929fa0236d7331720b983517b1fcdf0 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 16:54:15 +0100 Subject: [PATCH 118/176] fix: Convert recordingTime to float before calculation to fix progress bar --- .../main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 3fa71e1ed..29c8da484 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -35,7 +35,7 @@ abstract class BaseRecorderModel(null) protected set From 69b76a7640c20f00f4b09da488589de950ee7879 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 17:56:20 +0100 Subject: [PATCH 119/176] fix: Delete old files for legacy media storage --- app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 10f333767..c32a294f7 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -348,7 +348,9 @@ abstract class BatchesFolder( } } else { legacyMediaFolder.listFiles()?.forEach { - val fileCounter = it.nameWithoutExtension.toIntOrNull() ?: return@forEach + val fileCounter = + it.nameWithoutExtension.substring(mediaPrefix.length).toIntOrNull() + ?: return@forEach if (fileCounter < earliestCounter) { it.delete() From b707681a6343a88d049127891c99c4d825365cab Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 17:56:41 +0100 Subject: [PATCH 120/176] fix: Avoid unnecessary call if earlierCounter is negative --- .../app/myzel394/alibi/services/IntervalRecorderService.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index 745e5f362..97a512f7d 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -73,9 +73,14 @@ abstract class IntervalRecorderService : val timeMultiplier = settings.maxDuration / settings.intervalDuration val earliestCounter = counter - timeMultiplier + if (earliestCounter <= 0) { + return + } + batchesFolder.deleteOldRecordings(earliestCounter) } + // TODO abstract class Settings( open val maxDuration: Long, open val intervalDuration: Long, From cb25c1bb90c7d772bcc56c46216869819e7f38e8 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 19:58:50 +0100 Subject: [PATCH 121/176] fix: Improve folders --- .../myzel394/alibi/helpers/BatchesFolder.kt | 21 +++++++++++----- .../alibi/helpers/VideoBatchesFolder.kt | 25 ++++++++++++------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index c32a294f7..e37d9c8c2 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -264,6 +264,9 @@ abstract class BatchesFolder( } fun deleteRecordings() { + // Currently deletes all recordings. + // This is fine, because we are saving the recordings + // in a dedicated subfolder when (type) { BatchType.INTERNAL -> getInternalFolder().deleteRecursively() @@ -274,11 +277,13 @@ abstract class BatchesFolder( BatchType.MEDIA -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + // TODO: Also delete pending recordings context.contentResolver.delete( scopedMediaContentUri, "${MediaStore.MediaColumns.DISPLAY_NAME} LIKE '$mediaPrefix%'", null, ) + } else { legacyMediaFolder.deleteRecursively() } @@ -337,15 +342,19 @@ abstract class BatchesFolder( BatchType.MEDIA -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - queryMediaContent { _, counter, uri, _ -> + val deletableNames = mutableListOf() + + queryMediaContent { rawName, counter, _, _ -> if (counter < earliestCounter) { - context.contentResolver.delete( - uri, - null, - null, - ) + deletableNames.add(rawName) } } + + context.contentResolver.delete( + scopedMediaContentUri, + "${MediaStore.MediaColumns.DISPLAY_NAME} IN (${deletableNames.joinToString(",")})", + null, + ) } else { legacyMediaFolder.listFiles()?.forEach { val fileCounter = diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index dd3359506..64c6f0b75 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -6,13 +6,16 @@ import android.os.Build import android.os.Environment import android.os.ParcelFileDescriptor import android.provider.MediaStore +import androidx.core.net.toFile import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import com.arthenica.ffmpegkit.FFmpegKitConfig import java.io.File +import java.nio.file.Paths import java.time.LocalDateTime +import kotlin.io.path.Path class VideoBatchesFolder( override val context: Context, @@ -30,7 +33,7 @@ class VideoBatchesFolder( override val scopedMediaContentUri: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI override val legacyMediaFolder = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), - MEDIA_SUBFOLDER + MEDIA_RECORDINGS_SUBFOLDER, ) private var customParcelFileDescriptor: ParcelFileDescriptor? = null @@ -56,7 +59,7 @@ class VideoBatchesFolder( val mediaUri = getOrCreateMediaFile( name = getName(date, extension), mimeType = "video/$extension", - relativePath = SCOPED_STORAGE_RELATIVE_PATH, + relativePath = Environment.DIRECTORY_DCIM + MEDIA_SUBFOLDER, ) return FFmpegKitConfig.getSafParameterForWrite( @@ -64,12 +67,15 @@ class VideoBatchesFolder( mediaUri )!! } else { - return File( - legacyMediaFolder.parentFile!!, + return Paths.get( + MediaStore.Video.Media.EXTERNAL_CONTENT_URI.path, + Environment.DIRECTORY_DCIM, + MEDIA_SUBFOLDER, getName(date, extension) - ).apply { - createNewFile() - }.absolutePath + ).toFile() + .apply { + createNewFile() + }.absolutePath } } } @@ -120,8 +126,9 @@ class VideoBatchesFolder( ) } - val MEDIA_SUBFOLDER = "/alibi/.video_recordings" - val SCOPED_STORAGE_RELATIVE_PATH = Environment.DIRECTORY_DCIM + MEDIA_SUBFOLDER + val MEDIA_SUBFOLDER = "/alibi" + val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER + "/video_recordings" + val SCOPED_STORAGE_RELATIVE_PATH = Environment.DIRECTORY_DCIM + MEDIA_RECORDINGS_SUBFOLDER // Parameters to be passed in descending order // Those parameters first try to concatenate without re-encoding From 1187d83e86b27f8e1966e1fd910a9268f917164b Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 20:12:12 +0100 Subject: [PATCH 122/176] feat: Fix icons, add more mipmaps --- app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 7293 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 4491 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 10387 bytes app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 17978 bytes app/src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 27781 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..6264caf34787334300186dfe153af83dc735482a GIT binary patch literal 7293 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 Xi1GthB*$mp00000NkvXXu0mjf`kknv literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..f2558bf5f3e4e06bbc464858f7958029c399a654 GIT binary patch literal 4491 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..94cd1f074a632ceb2dece47e4f3f838192936a92 GIT binary patch literal 10387 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..f527783f7ed7a35f66ecf9c5ae0d9f5a1d3cb529 GIT binary patch literal 17978 zcmXtAbySn@+kZxkh9TV}1O%i@xsgrRvmQv9sqE?ecsPKg%&GZRM+@OiO_x*q?)K4v{%S?VQ zj#Dt*3y+;y|Kl8@iq|@>!aSBC;$uHQBRd{GH~w^OYenl+yyWTJZpEvhw%*EvgA7Ph zZdFNnh4V_l$*a?#u4|lKL!H~DfYO*k+nB<2l0+%72mH2>Yx+}+yY9zFYudo`>usVR zQVv_g&Aer*{$o(zyQ|~c+uK`T6BCm{tuiJrqo#=$sq$RPU$x4fKZ_oon$ogzb*&K* z5YTb>Z5LcvdVnoOk)4Bb;VeLUEeC95r(4Ye*0#2`vE}6~Y%%Vi4_jFbQX7n>`2z*1 z(WJYo4qxc#sL5-_!7+)~O3nzhbuL5Tt|4$%_L$Fl#8!=loNIRPW?$CoAEj!QdA=3Q z>bPuNa&F@b1M21Vh{G55X$Lf}nE=9R)|ky<)bt_Claj-KvVm)ojLAcBFJjyai|tU$ z%h`ONiG-x^?9AyBcX#y&W)@Gn0KDx`>6&%F)t3=w_l;U*_oI!+Cd9D=S+d`3Fu7M4 z)AvvC;ntLJ1P0TGWw)Q=%?6dZ7Vl;)_|-&prulJDPyOKk=@g@PZAqB^i7}Va7pl!D zKV+lEFxApgI}57*Bm9ewSpK$P*KbXwWV&m)>*Gff$;|Zh31((yWLsNQ z919D}q2u)08bN+`n)27&KX;Eqt@st1HY9>pW)K>5jvU86DQnXM-Vt=^nrhvOpfR`X zmDt$Wu4HdVR^{~aivwjZs|&rHTb;HlFcu*yj;YAR9+LvjEAa< zx(?CO!YZ*N&Z&RiZcI&0*+4Ncx<0IT2V;BlOGr4Yz7Zt*cZ~1N-`OP!!CVYRB#&8R zaE$m%bC3okRUNWeXrQ?w#T^QYCi9LHusp&`f(2z|XY3oY0@u2D(?)`>inE+E$wW)@ z9NK7Tk53c*faApmm9>rlp^OMa&N(u)9-M!7cUn~&ZcSq&C=QU~+%>9scFv^J)u@GK=J zW>2FoOavB+?+Y(YYA+4{=A5ImYu6?RANe@6>4^2Ohl_Mo+V(^^I5_BtLw$=P3#e37 z?9obFn&acd#KgN)DOyI!W6d4Z0QL zHrCdEpz@lZZp7{3A_k$_WFtRme3qwmbRM)}XhSDzvpmAfRC7#%hgM6CCj8>!5AtsG zn-`2H2XuHqS)2DUug~eKlgncLexW|c6+IygP3! zx~GI%`b#WJ$ffi5_irj8aQNM$Br`ju`+t?JDhm)p-j9B?17mf?x zcbtmtI_3MlPokxJe%R_ac{E#N5N}TKt5s{WxnyNzv{=qn8O@%ov=u}rM7e zEG@UEe8uqp+)_OoIaGMV?$6*>X_u+VmJ$`6z!a5$hWJe*>YWZ2zB&vHjI~d1O4g3= z9@k*LB7$m>A7L~;UIb%}$3oO1o_igN*m6|(`5wPsv0Sa3erOUD6T1(QV}Q6+xN)-k z+f-V}olHtT_lO`?`l{vEb98i6(sC+v{!NHjYLl(zZP{;&$cTu}<2#AD1IIl#q3?YO zXF+u}-f3xRPan+8HZX{Yh-Uly!#XsNS8IZ^)6==Z;;~#%P>{TNcB8JNLqqtg^2u$c zjdo0lACGh@a4jz*Cnrd*Qn&4u;p5Wf2GzZ*ED#p9R@t;#zVhMjm}lgB)r{AJbgw7P zPM9rM+e!K=8N+Pru3wkC(e-3eQPEY)ZtkI)s_NfB=0OEONqjfkgJUmOElbddFm`~G zm{yPM^(3RHl2}pUjAoIehvoZ~zqXuHv<(KwG5h=bPmF(Is_)cS?s8G1oSZq+0(1ql z9@&GVLkHjRtM+Bx{KR;GC>*b zYhtNFUHSdfQ%TMg}#GTT=hAx zL-qUd^&}RKD`JkM?eR=T8GzV3!Z-As-KHb@?sX-7(!KR(?W`&o4Wu;R6TW)_wTMn=Zd#GhMF zfpYUKmW$-VW!lR7hlh+r&((yFp6iM%lA%Js)z{bm_eJ@6CYm2HA}o-i@Hbs{M#k;# z+1c66_BK;$>ASQS?}ZR@PE~ozU*AxWlQ$b{ez{5F81FU(Sob|CH**lv9d-wpR5VVb zh+qW+LqjHAk>0p|&4YvG<>ibW_gkN>t*y-VPANqeCP8B^#BZmi#(}HTH5ZrF_S@K0 ztK&tDt0aAJwSxG#xurx(gu36A*t_syJ8tGDl9G}tvCv4@s&Qyn>URCF=&0%^oSnG^ zh(mr{0#UJj%Z8jj*38MbZZI@M^2XeU#tvAw$b)nuV?|!F{9#BdZ22PDz z@)G>oahJpjK|E|#T`$;cywXS;*&!A?GkvH#@NweM=*#THvvcE*8Wv&A8&8%nPvl&r z6Ry(@paMJfLi1cOY{3*5grVbAwt!doG*%1jVk zpEN9gFE3Z}%k51Pj%?AnN+V(aVcb5Uh_cDl%V1Rx>!*7Xke2n$7E3QC3DOhWe=%nLw-cXoe&-`41~L<$0dk3ZcC5+Grv7ngG; zbnGe0wc2c6m1g86(Pgj1<3&)#qL^J4zFY0E^}Dy3>&!SSREio+RycIkJ=6>-(1GPS z$}k8~{*w$wlKU#*qD`yp@1CRJ`EHjt!^7!L z=4)k_np{j2--d{w!1^yz^o%JaeUWg(Yl4BTv$X5QzydzAsz&FP#RF_$#JzVbz)kk{Bl4E+b z)gM;1aT>D{{{xExvw+%C=cCAOAL`I=JY*QUhiLvFbNNC5?R3?3K zcG?=7e1UA7*CDIw&5ZBMVaWA^otI97{F=+1CnfFG4J53j>Mepz1aY$ZYd5S!=8fS80D*VR`qE0F;0iO>yDdJ?e z96R{VsW#XIGS$$p6t2DNb~Db-D$QyD;S?>Vu;BG*;aOsRN{M}Dry)nD3)m1}W(hj+ zmXwq%#5D+5X+q>`^CTb75~B?&t*v-7Tqq*WCX6ANIkBxY&4<2gxE%LJjIeMnB0m^q z$Iq({s3za1dXW;{xFL8F7fO3xgpF?HRZO&SsjqCAwulgE>;i#rH!7Q^g-jA8?D@N@ zwiA&$eU4QZkh}0=hQz_%YhF+C`g&Ifti;OIQL}DePmQVSwtP2(3z!o=#vNwwJOyAC za{E#?w9o_yn$xyOP_5P3Ks8s2^4XF!VQ2&4;X8^}ytR@xyc9n6_H0wQ39O;&6j!yi zwKuuq0U?i%j~Cw;j%)%*?xaf?cw!j1J1MYDYR}=wJB8l5jQnkOp+NKtsW8b?UCWW5 zyWSB?v|m@g#Zm?vN{|`a4w!3Zg2y`kc;5j6MF|&~RB?P_X!9p)T?* z)ypw`CZOl8_3Q`LTkxSTedz@5`k9XOD#k3%&P-&680NW7#A!2vrrOoP3W67JnvrTg z@Xeosy-t-}l{d=r?Id1QFG4Yks)4Eu%ULLoF=4DuMxzVQbx@U!k0lRPqR+$HI)Y(F ze8Pn~<*^B5jY2MKoko%mf2UPIFz+0|QlJEcJT8DI+yBFdRS*-IeCIfvocycqARS4h z_+N&R_ldS&Ca#2R2I_$|H!v7ma1NvJ`;8(kY6fU-{A>_lr5TVi>ne>^kP~$rK0qxh zjVZ2H-AW3#`z?(X8u^a+_{VyonlC!{gtJ&rBa;72>I(&huNZv-AvfA5S7%!x34a!vki*_Y16(^;WfUtcL4-b!Kl5Hf2;0Zj9B#lR} zW6tjcj(=bkg-*U6tO3Z-e&yTf`CC<^?(9DmugY_3x=^EM(fvWx2= zx9B!j%{1}Nr|>#l+IerojJ^AdaY+-u`z<k?7LA>e%Kj z%k;F4A*s=MGr+~x^>|s0r35fFr7R^_pxPb=B8PN9#P_Os%bYi(o~s?%Yu~o4{~nNj zx_`XxQh%eaeweT&X6NdPEWfNFctfoBqKeI$jut>bCv|H^D~vDMcCgfaWmf(oNocQ> zw-a#MZqFO%uV?g3Dg(ivq37JPLuzGd8K#O34I4vsz`6B_7ZZ@ca3&&0L;Y|Xu=d_> zfZe!2?AT7d2=X&jfM!)_%Ah^I{bf^V;>DQDhqt$TijvCTu%V%LeWzzw%tG%&|!x%)v?ZUAtQ?2|Re4S2(3V%Rr7vAr+5^ z5#&~)_-jfn_H;(~r0F;2VEnc^bvvZSSq%p@aS89!hiyhWO^N#rf@yRmos2=MOy&my zQhQ?Px}YLJvY&5W>+2?w_ar=j(odw$(NLl7hy`a-ukQS3zm*_*0{_*-arhl9%HJ=2 zP4~!gDanDH5d6nOlPBfYKeBZHU>PrGWe?j6buZufN_Ad$oK7MvkOd_-F{uls8Cm1& zQakYZb@5X_g4*XzqNApS(CQuLb9pqi$XHws?79TOkk>sju7diEUT&(HwVJHN5}D(> zTp(aQ05OK%a+3=kn*Fqu*&@nbc|{d30gcF)%OE=1`JExlXV*-4mCc0l!TMs1o47GQ zQ&5p&H$4Qp5?8f}Gt=aUdtr?a%&2zu_R4-6I3}gb?jx1MNF?lmG?dVqVIi(yOqd{j zadJ{~XYJ`>jbuBq`#-7`XCONKLdE#}*-qP>=Y_#Cpl{yZ>p~e#eVHdKHUyC%o(e|Wkfduzx8eNA*$HH>OJGtkvg)2 zZIkU5vBF0C`{OysA zYA%I%WC#~YMzk#`?()EN1 zJ$!k$s^?PxLm0t)+|aiL+SGdVmUEEWHGyi=?qO?Z_K+nuOI{%Fu;@+IoP+lasZ_xA zNqm~}bHLUW?2%1olee<$yW$?eyzrzi3!+2UR`;)^U#jF$t?fz&8)m-d>h(&@ErODdLoYT z|3K(uva~)|;e0lP9FwkFwjYk%e0y|2C+4f+w=25#y7tB6n15nN?iycIRoA2c&c6Cb z5kG6#^xk2qfs%*>29e9i?nOG2KMUY_fA&-F`e*?a5FnmXqlgl(5j}0^)2!63iz%cQ z;^wY-KV4s$W4*Bu*s}z=>)r|4qFQVAhqZPP&+(v~)BAD5%byOr9%Iv!LcIWqDo45A z>gLTD3zWZsi@4j-(O5g6e#3NyrsDpk6P2{^1R3xxQMNcE4miG4SO5KX3|A~nI*-_30oTPL4 z;>j4kK>D|mD~11qnCEXar#jt%l8Awd}m-1F@E9yv6Fo~`l6R=7pRh(lr^F5-b9KRTPv=A1%ld6oO_ z^==Q)>ha;j<2!D%(9lqoV!Kq3nfwvF=%+-%95}bh@k*5t5U|>O6Ea}VW3zOnyWIn# zg(@PTHX~#N>qqC9-^1zZcj3Id=4Z@0B97xz0g9tiJlmVsVozKT> zpJDK>|En@|!tXb$F}dF)o8~e7MfngAdF(PZSiE7$d4Q;A$8<60Oy?jM}QH>zYl zxWkl>mnKgQod;}(G=AO9b*76_8!jL3miC)-fYcG?9vC@hJ)J2^-h-a$d|F~ zScp@4wB2|Gmz@D@N9^i@hU%b~DWOP-|1vz((%yXZsqZrm?dFC#ZsAvmZ{Uqcq4hkA|)%WhKOD;i&@3D3NQ|us-WJu>84%cBzIpyNy?4(%LV@t^- z2+3bQuzvXx64rKXo0-ayHi8PSRPLJ+le{tOJc@?w(0K-AN;d)PzR%Qa7J=qMl;_+d1SxfA(8oIhCNdyOs;UCaxy!yGhxk)y>a~`mD=J+y&_Yklr zjd#X||AvhH`eo2s#S(@Enh?>uJMJy9f%W{oFIypS!)WqAUp3$Oa>g1O5qwPtwCj93 zUHql>THZRlc!};ATvC4!j4iCje)b+gJ^Ly7m?R;8Ltmayc&x4FcYJ7Eq59^{=LIL5 z)?S-1Z&|(^-_7VRX9RN}gxV0zE`$P#nV8>R>M1m~AYCtvU*?TJz5l9pt16aT2}p69 zfdKyMb~o=-kj=#j8!lInmYO=M{PnYjjsON8apF(9ypf$%ikbM`xHGUYm3Gt6;^kR> zVaIY70e&BcLU7q46M|nvqnNHjIS`84>lddmd&Gi#8D^e`&@SQ%Rcfz`75KB&hbFO2 zPf)<{fS-!SB=iB?<>sKq$)!4vf@-__E62FV){_y?q?w2pTD1kFO!M7amOsv6?*cc9TaUcCVvT}=O`CWPj;y^SZ-ctmVquCAJ0t&jW9jh=1yEC$uqXfV zhn1tB8BP9N^50}4!!`X+Fp0AVH(VoXonb?@nE^I5*ei z3|KiW(Rx#}h;nlyt9->INWFKcyhQk5{d}tWzi$W#(5&g5-zkB>ml~6U zf`Y1*N~U+^&-(RI3!s?5fp(~C%Zt+XEPHuel)r%LoQ|H6yg$=$Xam~8MT_gE0k$8Z_1ROhZG9#ct1&Z2xW6DDJEl#HRvLE5FoO3c~BQQ?^R zhx+^3nJ=};c^zG@cB|$4mC*7UhD%C)Q$urSE0Zd%&be`lmM?-1SsF{gD!X z3;0u5*nIs&zj)@)W|xErT^jEn=^E;%*91yDfFFFk!lDZ{>VRU@so|H$Bcvg$;NfX34j`+c0 zlAE`>@tZFyDZ6q1g3!>QW$oc)jt3KV-=FWP^?~81d46cC89c$n?2+v1Px(@n}n=~mJ0|8SK+Xy4NR~Wcd>)?idvdgS(Ls3}> z5Ul?HEWqof#yi6~8qog}N(qRxddJeBV(03N?bVjv*q4h{c}s0}=6FTR*}BJoZk2^O zd`1H-WiX_(|EH1raAfn=T!1ZfkmG!|sWSx%ROdwLOOWbEsZLC29D1wd^sEQF{t$a5 zR+#CMklJS^1$$N&hk5k#RgEMU2ZtfjC8GY?x=cwnE)Mn*>5rul1m zDTWOq!PP6Tu09>eQ2kS;i4v1<0bruP8?^V~gVpRr4Q`U?kz7rxrlZsgGn)&SJI&l2c>-_W?cJ{4J?I|dbWJb zvsSa1{G)zVC(3NM3{~G=3>wrz4@vo!1Pws8XmPbFd&bL6LXzq9ax|{(p)8q z#vC(XPC!RV*{biUFkP;2@b}bx zLu!sRy*A?|#E=%GMV%X3zc|y-j@b0uYa0Lfcrn~@(vULi7PIL5iY?AjjGR~`p#r_{ z7#fgo4=E-cow3_@)R&UQsGaV0TnW28V08OP?p1r%rKN}kxK8>*$wb0(7mmJ$EE`eKz!xpp` z=~ajJfSh55z7y((f5s{gQkb^NnCV%M?~#lmjpst3^i%uY$|*B61ds$ll8CvwHG?-( zHzuo1Pj{hYx{iKc_h>=>kvx|_A*7NhdF1$qgrLI^K?BUUG_rl__*LyFwsUz_Jb z#456w;x<8WFJevZLr?-SHP`i(l9xdcTjJekxiR^|*K)mR1^LKb!6b7ZeQq*feue>V zy*v$_hA&D?)**0xccUiDce)K0KtcB9lnR#)n3@pyEk}lpZkJ-+nqjh55(;gb#fxbM^c04H*5sy|0422Oe7}gm7A`#u-16#|{if$*?59 zF0p`uX%3C+la-2JzkX%9?trGP1qf>PTb&oZR-3S{a;kBCfq$+Wkb-CuBoL*jm?>yp6jw`>*R#qDleV#<^0I*jH4&%j*ajll?n^-+D=ax@a~(IU0QcL4;vKx7dbiy#z*vG2R3IS z%)Wr8bNtV(*s8-`q6#?niqO(-TtcFMbDsWhcYFa{csdW0j?|Dz=HtDSjGtuQG zNx({~$cRE*vRl{xw4mof10-?0L_((dDM%04I_Y}aZRbaJlu7UwQA9vaE3%cC*icbX z8{Aut5)!{J|YK3Blonr_n%EoZ0EfP*Hjky0=08Ff{nQifX}mPuZ4cbnYqb z<1Lw4Ykq^S?t2TuwS2sOiLYAw{{kgGeyutrQJc2*G#U?$2J$^cjE1c1@I#{XPpa6V ziyZ+?-or0S^uuK!K+Hm84h#sPpu!A+R~-I(AuB6eO+pgm@U0maZus(Eu7O&27jZaD zQ=62t;tf8AstX!O|qmCr~Nfv@<^p+I9|>r9wos@9+q3OpAd z4;7lFL+jyYroFnp?&3&N%3h5Mw-k+&ULWW&dxoSD0i?T1?EQ^bie95hU~d`6{&xox%yRY$GsTK@|jjJxg31KT#I zI2#EOC>C(3F(;P@o)2zA*3&T^aOKi?Trq!}aUi}_}*+t2s!zD`R0bkU9f_yC0CzxDnsS_^+PAJ`9V#Ldsd?By}tfU*o1^f z=~qQD%{@9_lLkk@*fy=!0rSb^!iligEiHI&DdvG(@;?z{S3ysx|~!L6n+8+QN8$bjHDqiOko>ry+onWek<&U&lv;=#fes&ac}g=(U=T)?k*%B zwOy3g-4z{+jnL)+C z-vgnlzeFAef})^(C5^ehzLpeC9?D)|>*(QpKFveGVZ5=)_+KKvjB4nM zbjHdYC{n*>MEwdlP zpYtLOzME;x->kKJYvo}EmmhcA-XpCgSO{UKvpNu5aC|m%$P_FoXX}3ZJK2BX@7Q;z zr5Vt;o&$woL(Dxn20~A4c(&v~2tKV?sY$?bL;6X%v{D_uJ2eZWZs$%Sy_A55e$ML- zG-&JnF8<7wmbbcdLF|&AX3Qx~&|+Bptf&ummq~dyke(W0%noFn>Yt9=9S;|l2XPVO z+>-?82TFn=8@{bL0WZh+1_l%ErGZT`8O~~tSV;%pv_q^?S&f4CE{NT5?@il~S zN!kde?!L|7M5aFnY{LivA1R;?+XaofxK?0dI zX5FF7d0!5}*JOiWR^5W83En`}^W@j4BA6sGuDd? zS{7Po`6SPtV>%S`J+W>9C&s%#j?k$gRCXOxd06Kf+J}+rtnL6>G>W+MZb4qFF{yg3 zTQb($dlWplyOymG8>(jE$DvPq#r|kPZ~CX?4(pEc#ndK*9?jwUQpO! zzVcV|R|!O@IJcdNHk2U-I3W^3nC_;i;4gEt|@`%SZPICGw z=JU;xz$17dccP4gf!@{(B}qdrm6lHgIqY$eL_RPx_D92-_O?6)BR_|9)5BD7(~#2m z_DVZy{Aj*5bTR(Q;xpWHZrEHuSgTC?g;exEMh?_aZwm#EQ3h2mEw=Vq!bKwJgMq^P zqD3sR6y=?+-9LY-5Ux<)uzeKYRHHfh1t_2m>J5UlsaCfWk{tO1#(G$LsX%WUm{0g8 zFlSfHPTA#SWaEa0gP1Apa5Cb1 zLwnsQh-2tyx}R6xWY2N;yC7ew_x14jwive{VlkvT?Wv4OxadRbp6ymWMI%*c7^il)~|u|u73 zhpJZp8=3}1gl6o3dBfJ{*LMl-3*wi*--^LRZh0C(&3C>zmbfFs!j{_10Yp(X9Y?uc&ZEzbtn(w}3Nk~Jw&Rc<$uI4ATjOdnN zDu^EE_FGRduYymcf!c3}(J@s4{SwU^&TJ{}mJgvE?Zk3%_;5!obw^O)V9To2Dhs=_ zBImX+WZAxx@UsjFM`CNPoC+tr;^+T+q|GKkDo^0H8*b94*K#jT;Mu%osS&oIAgFf; zA)pg=jPXNeXl|btHFg+fF*atY&Yhp1=YpDj!%L^-vDZ2?D9b+M7pzuhOw_J=)r2#p?)7cw)=2NQ>-VB(MwSZKt1Yi-R-VU*7EZ5^9A1Yf3@Rzdu$ z9c8q=yk@t;LQ+yv8f8Rq_^U(J-^$sID9;Ee4P82qqzeLdHxit#yKsw3zfIe=DR59) zCnE*TkdXrcMU71;Z%WnuT3SWHD5r*9l!y&r^IBlWYRtrpPHnn8BB_cz0ta}73tw(% zsjeQE1Y7oG!oON&C()1Oi7 zcaKCORruBVuD!y^~NVqLGIEZ zBC!Vo*Q!BF$9O~dms4w%9{Sy<6TtiSsTky0tKYxK^JUhRI#P=& zVT&9^3at>1X&!|)q#BkYPW^LIE^v=-alb$zXYHuYbvIHvBxO?D3B4j-s?Iu~Afl+a zdNKGN)RH`V>|e(_QO1y5`3oVCN_;Fl6ltXjH#ep6?=2!5r>rNK$JnHBqnqaM0OO_5 z^H&H~j>*h@F)*#5;Gw+rU-BERjrg9pg~rOhxR)FplCn}X@_`p?;HDQW7BmZ_I5xfo`Z zV4u&+8nUEa#foaBz=W#urP^aGCM5k{$MAGiUi_AX@8q)<6HWQ|r*f`C%gx2W{iYDV z8mccm)2uPoEx=z#=WvwK`u)5q9pWdynwa=>_SM$eu|!&p7UQYRL>TYnD^xd$xN&0Z zIImAmOO)-kvXS@{n4B`ZQOz(ZIjLvIcy3 z=s5IxE=-80pIVVav22XyqJv;T))xY=UPb>Mz>Z*UMTLD(;>01xtajI^)vegCI^4AT zvE_!^BdZ!Js74eg=g*3X($1T%1)F7k06+?K*^?a{ePHanG;;1-)01Q>IEnY$C9}8; z)Jpz+{rc5~&$p&3vFZ?w`kPx6bs8ER1CZ%dN#`W{gR}Wg&;&NVymAPIg@WB($)UNb zA|!l$Dj-+!1%A!MIj?n}f&NOa{$Qh%Ed!J=+Mz!t1TOWcI%ZvG1Db3(E7)@EdMr~| zYA)1)LI|DlEPae|kO3xQhxZZ)sD<9)d&sYXvj%|LfY%CJNEiTX=X_cAD#BsDwd!q4 zgc&(guG;k9q#((QQNGZ&lSS1NQGMieoo)H7{j^n9~F!MD9c-So(seO|R`^gHqh!kXJ>lQzVY zP5dUdmhXY+@A$3Lzp9MdxGhqg)#IKKc<)2t+^}>^=9ic#h!}_^mIvuuV)Wij)lBeo zCn+5b?zlg_m#by0lpYgb9w4tG6YRJR8i^2k_}q<3)BwtU;ABKA0Ws*8%{qFbc2 zIW+`MoKOiN7^SHrF!TX1SIIUd(|d^q0=>}S3voJZBqSjJ3soU_i3aPTvJgiD(50pR zaA2ZP$hk#Fo6ZhqXB4-5?dp1Zaprq}6OhSy0NFfXt}v)vZ3J_n0^dsMM@gO2p-CKd zZ*&@dXC8cZg(8riu3WyDHiAY`M74*`??^ef5k=#lZ+n+c)W~f~V_!-sxv64WOJPvNOQbP9SN~T7@cgla~=SMOWJ09a6gBcvZXa`489L3q#0j zzXwiJ(FUKD-ijI690%2a#K1L0+L7?to&fH|T|Z3h&qL4D&1t`_GN_UncNkPER3qEB zyXm_b74rgqNX-*w@wD%j^6O*5H@gdW{a|wnFf`ZXs=x0ZhU;j%aVT%KVM?qlD|ACen2N4_t8x6JcrQr8MnrfoS zrK8_9%Um}DwB5-Xm0}DqK5C6zV8Qk+w9OY-s-ci|R`uQ|%z;MDe+dq@WDcpRnz8Tx>^NoG zsKgi?g!YN`hIL*=<*U~9;o)HbF8}A!%HVDX{QoR4v1m5i@u_|7!Ndj}@wQ^-k?J`~ zujSCUw;15vh+;}^LHPvRlXlleMCxPs)S=-(#G>xHooue8S5@@WT#+@;@4n$k-S9Zj zH~&Qrq9G38A+#v2+-)R?(iwYbUDAKC04)kh_qHFcGY`4~13dc0IGE_U?E)8}54TTh zp96o5Qi&#ERdYFz#I>f`sz9T;z>xDva_w5BSwC!u;dJ;q$Z}GXT32>}BQzoywQffk z-`yaJyU&OPkiO>jh3CFH>07ixg%FoI3h3jwImbz(CE~)}@r5(_)}@q)qVgldN}&i` zG+GENg$R!BxU~N!IBzrmQ%4?xyvuhl-8I&B&)r zn{7p(E#n{XCx7(zcVLiIBo^bJ z;5&k2J^_JntM{djBoF>ffSf9qb2x&kCn^6=GfY29nrR>+J^HUchk`L>mLUDNV47zO zF(}OC!AX|fNzoS7c-5xD={$Wk|B{DphSHN=-p#Tq`k}M)Fbm3g`k0=R^P5tdQ`N7y zWut;JeAA5mCG%1vk0`O<%jP4?ax_=?C$kt#Va$67e~8}!5Hi#|=Dm3){H*NAtrDTw zd;}06Pyx5!erugUfJROb(*{SL@rYJPX%2fM&X?$DmBambFe&aY`RY{}I7E>%N}j<> zUkYL1nfHSAgn>~Cws;apxwx=#J*odR_}?Q2wLJ&4#mpI%6KyjUq+bJAmQ1|=umIK+ z<>R;k^J@XzA`=v%!{@Vsn&T~~ao-lTIfF|1@bss=RtCVBj7NjPio?uV6;=4V zB6=~X(sa|$LRfD|)*(@0`wZ)tlSeqS^b?p@1-=B`eMh0G-*?fQI`9w%AuQ!l#&tAl zt(!R8Ki89v7wRzJ3;?~%Sh3?TuS*Ud1!;m1;tYOkhMFWaCKN&-tb5x`mjOAki#+02 z-3ym|21})Ru!(6wI`&VYFg3@LG-%WQq8F>f4>eH+3%=J{{t- zoDR$J@1*ms5(O{W30(J7gGz6VDVXi>u&ORy=vYX4Hv{{L+KA>(N6Mzc|Uw zO0=7awx;?tTfZlr9+ly?;*ByL78_?Y!xKk4Rc*emNXJCAen(pSOj!=i)N`8uvKuq5 zo0&*K{Rrcv6CiE;dQ5ovLv1>}vj0l^Ibxh2RZw=)aE$qk?rsDj>YGtC05D{peXQ5k^U`~? zSr^6!vqvT$NrGh%k*cJ*rnIV)b{MqT%b>X1#~idpfvIQ;L@Ss+_xa)roA|_n6KfB= z7PRSIhK;O8BN*q)bSoyO`~jjc!7QOLC=#&k)&P>t$p5~FF!`r+%XOu6Stt1=idQoi$F>z3(M@>ga8G0&gY1-w8;2)KSOSfCQ^_kTIx0Sl zxlj)gWunQ6!ps}eU4yO>=Eum#xcZiX?049>YtI%*E_K3-zp(1;AAh2sN=_%qY>U1y zLz0m}?9F_y>_@wM*k68R0P6mTlPe=)nhGE`2ylW+I4Z^TdpGSUe@IZ2S=efiH*C}L zhyKOjx^&H_1&nZW{W42O$2i^-a;k)fus=F^M0$jyQ;uAHS9y#{$Cn?{8SDmRLoNd!)WEt<3`P^;PLqm8xJT9t2!=BppF1aE%kXmiXu{t zG&G_c!puvIp62=x@EVuT7Kjnq!fZ6Vk`BuvSE`Q|dFDnbVy8oZz!<;aR=QC#zDgy3 zZA)YQ-10XBpR^ITM#PmIF2iIIU``AnPJj9Dyi7iC~RuKOM-LJ)y$VoCWB>>4!4yLScfempu{=>5)B*mG!~eUl!$r zeflND&EN{!^IwMS@GBruMF8$5h(hY zwpGv4v3Q^NE1+CqxGa33FCThNm;I-k;M0LEZefZ!e;|vyjaTqX)YpOzpUHLU>%$M#y9ph` zRtN!_pVE^Op-w3!1DTOa14oQN&yv>9nDIRl`P9O-+myff5=6`~;2?_~E|YnNJ_qT# z&R!OGZ0%2ZYZV0SPxO{pzfAn-?C;;&2my+|^`4mfLF&JsgZwe`bu^rZ<&H8yTwkW| z__^+yUA0~d+Pj(A!(>+^LKU77DK2Eq_IyFSHlW` z2t2Y_K5Vj9Vm){?iZjhA=-(v;`O@{^(p$H!vhT}t1Q&V)JqobqD zJS46GAW5Updv^CFHmZA7A)T|z+rk2yU?z@tS(a7zvV_S4$N;ESX$xM+6PgO!2~dDp zsFPI{1BE~|J?h|*W^fW<02IMA^jfRc!mPxmi~+;1%s|XE0yuNQthOoGs>35(31lL( zj`s)@?%THy?4yR+2j2_kk=x7d51&W)^Opa2>xJ=78~%O*qIG3fBTYsOr#kRs7S>@l z3WJEw_%MjbmkWBv4E}v9zY7+7^W_y<=*0zRRJV`1OJOvU*)S+GG$m< z)n?OF_s~{ANA5YBs`~$_RLZYZD#SXhmjnY5^RSo2fI)ze-^=+eW*u%n>mK|s5~V!{ z&qgs&eZRcAgIod&Cc*~cln0+UYOC>{(+h0YSY(Uu>aI8gflZwRCT5;htRs$7ci(v# zU`tWdWgBTOVxu}hu-vXNiCB72x|Mm&*h$28^mM z2C6zh8b{VTb>JK@0%hA_v&v}-xxN|Oa;b)xqRi8jd%`>|8Kff)TiH0wMD_pKoPmWV zCnph3uYboN;$S1Y`3xYDS|Bim^^pW9GFy`OB>x`VxEe(evhJ$z!Q+*&AILta>bA``2vo13a z-piUNbOMclvTpG7l7YCrjhJZFXEK1GiBQxXW+T=_aP?aO2?q^}?J$TKJPatQW5Ymd z-XBFl0E u?~IKh!mkA5%D95Ru;!7@bHl)cWBh-J52cP4sqBjY00007oVlOLmKHuX%JXSK%_ysK{})x76cIy5GABRrMsm&q`SMjdtv$J{o~`D zWzX5OvpYNU+joaif=D({W26Jm6p;#n&mg z*5D=};#C>mI8EuAgyW0AXW|5hf$-~hG`)K@ZEn#RJ9Jc37dOXCZ;1hgva*TJQvIjA z24dhSIu7GLL&9IYx_)dD!~d4I(siT9szQ(YDlnLfGTM4)XQwJv*o8JNJ^hG|np#=6 zXiBj_+s(cvMT;kC$-c%QMvg&r<%V2hBsEHo!98O~rQ4$7%&lHr@a4-mcj}!#4z9f7 zKu@W0qrLz2A%ws?^jEiQGchpq3JYt40!vG+%7vPmT72N5$^W+H$*@4%KJ}o%<~2hc zKXos~rFa?s(2;M3R*6o!A``>NLwzZgxX%f^r3sy+NeHP`kBbb_sWEba2X6_MEzrJy z&y&;`WM;OSOU$ZDjW5_9xQ1=|Ov2|ht5qp6-#0d0USSavcK>nfc)o_yLd3sYj7jXp zi*gCd=w&EnvGv^2^2l(s5$$s>^k&gLTOWP;LcNp|~MFMbJ~cD+F(P^t@9Y0WX>IpIuD(gn}RJ+9yV;L(OcpAcquBEhaYF5*6HoGTm6cw@r_-i-1=GRfpcZGxJ-wv z-9Nu0VNckViNqDZea(D@O!kL&H3pB9;9nheFZ=j$`YZ3A>ywp{v#Yc_E#B;+0Q3V- zdfloyTS<#^8TU^S5hv>n$8_BmFKKUdl(N&7ys1Z;qx*}u150}9r({0oXubfK#1A8$ zn@I3a?Ny9>Rs9FYnNnkKk>0x)Bw+1Ec=5x>y5OgvvD;*2Ya3J3l_;Z`KsNzO8tDc2 zTaK0K%cAG;dHZS73yV3>`B%mq36g@s!Z+FAC(1exiVPiz9px3O7@OY20IL42e2UhY$V` zSfgcc%xnlUx)R1nBDY}%L8RO=c!2MxjZ8G{-ddU>bA|cP1Ot@pFBvg0N~OaL!*HfhN8nnbO-kl58B&fygg9oaBj?bFDX-@_we%s z9{fgT2nKY!8Nku<1Oeqf)$iJKj@RsukB=@F;wPmoCkI)!Z#o(hD<@XBch!g`JYD*C zF3QAR*Sl=OoR8XN1q>P}o-Y3iBqoe99sY5LV;PspQa;T`iG2EJQ z15ldYPkS!i42_JmTKoC=d4a9)tl9sD$m!Klf4SN#fMB`ldfvfO*Vyyj*&zSOqJD`E zbk%qHkxMHeZ~CNuk@uCknX&QAqXtis|5{g9R}kA6bCQv!=u$^c9x0H&GUj-ktCGqS zEyr;3MUDZvF7W8LHZOlwedMc3B|Ci)rp3OI_q z;nzoJvJrTsBO)7L*v7_{Kt6oB2zzIlJv50 z%3dp~yE|`KdGK(d#zEI@r6o|gP2W4wOah6X08wICckE_-Qx&seg}ZZNaI2QkmtJ^K zT639tfQi8n*EhJnzE0fR+uJ(ywXrh-zw8x3F(>fz%4;3GP@GnL$qLqd$s_kIP%{-x z(bpTkJMUlZLX!2sb-f54sKvs{+KPw4(|kET?$=LROa&y6m`t*qwoD&~% zPT;md>Dg0=XN&EV&TyLQ{nq^Y(u2DeXjOUnhqKGeOYbvG+S@kt#2IXiY4G0mN?upE zRyO|r{d?!;>gv(EL}y`^bG~Z7Ghhc*XFsIypy3vU6)U`BqmL3fls9niE%S`0TD@91 zEkF<^rl6p35~q~YvL`aU{VM#)E1UxoaspQXCqgGH3(K_1=gW8`VBQ^ZxASA|3J!-y zRLvdgN{lp)r03w)ajwnu!w*(Otj6nyYGDG&4L+NFaedjc z5f3VI;9vD8#bwvsdkbGJ`J3C8qUTP4$%0idOuLcX!Yg+Y4 z-zJR+AUJ+lMTh>Bx)pYuJ^f|HcUR_ZI~3d-rSndFB#MWJr^Uy@VtvKycrh7liBZ^j z41CqwfYdE->%;l8ZYN}9Wc(JvP2ZXmIHtpo$c$|GFmcHO?Kfs>$^k!pL}Idw8bT1t-?{{4$Z3f+M|-F4TTqfes%7*C|SM(yLwK`cK+t6E#1C`T@Yo@sf{Ytk8t z5ZWh_eeJeT6{k(Ip$+KqzPEi{tbGfS&C!fgs5eSPhkt?Kv}~y91~_ju9b7)ULeGt% zgCbTfDts6P{Yw0k*69?Gy(h5O{Dq%~ftqjM-jB=2_ZR?Ox}+`}6vg;8c!b zjp@f5LO)vbBii{lx$h~oxm_ImYlvVlg>3Z?U%Gz*vL|+)#j?}zT-vYKo5 zJeYo#J;qu37YSk7v( z3)TOd^l9LFgkc$b&Ape4(!oV3!Ucv$4!K>dtgHkQlpdioo^uj$XeIvpzh$Cx6;|Q* z_lcOzgupEOVx;L z+O40ePr>T%Bf@|T!jun8+uDlqtTUU-cEppmUS2vGNsw%nU(5d_s_&>gKfmSY=f}@X zO@+7DIxScfiAGx20QBL`Gp50T>$hhcTewt00X)gdl3vYXEgY*D0Ya&sng!bB=ANm@ ze@pRnO{Ju91U2c4E$x|fvW$3w_hu_B)t3LdS{{N2E9V#`G-K)@176vN=2ljlk>J42 zESRz{bkW&Jp~0r5{JuMMdRnedvsBQ$L0o#-sUzUb+3VrsE<})D@y5o+aZfZ=Yl7|NVaj;8dWJdg0|&)A%@&-Pp|wgcVAmtS|XBdGV~8?NOvg!8HyDP zmLI$nbEb zq_zc!VI*phB#;3To1NkGxK!r`cF~tFH5Mx04i*5Xvp7#0W}{Q8ykB?GUKAV zSRTWMCI!-#6+UMB8!;#9GM!Ziu)qV)cX#u>XBwAlMh7*} zR#^QfiyW%QTn6yN&k2^rjW_OSgqu3)kWi?t0zP`4vgQzgVLKp&dp$OI(PyubqLxH-w!$hhtfy5j{PLG5Q7y=u%D+HBT&d3khVA|gJWW4{#~ zKsst3*HjdTXkQD zYT;m1|8H3X zMamrqa}b&0JWEJ*;C{$se4*N~gVv+#5J7-2bgtjh4w3pL4Kg>@sHiA%kUfuIUV1Dm zr|k7(d0+!B;63Ic&8*NX76PfuXGbA8s?4uu>z1KDLgd%o+uK8_ucjX;;dmEI@|l@P zDzjB@r;(48jw0|Np&*9;ha<>cWa}k+`99VSi(wbMM`}MaG7vo=s9*f85Ua^JIprs_ z1hg$x*~I#9CFoYVTY}iQ;V%f&f@69&c#o`p`B%jl?fH`i2|Ot!8~f91_fg^DNlawz zZ<2_GIeAYgg)SfeBk*Hc1d#ldtYcA{=8kql4W9TUB@u<$+1ZPg5Xt3y;}YB_a1ZSvKCw75}9LV06#b zI3#&5dCoAi>lA8p#O@y)z`yRbsg5kz+o1HiHiMl{@mEns=fYmw3~^XOm}N7Nt?!>2 z=z=dL!O>>}gGCB~#qB^tdsQK$rKP3zCU3&DXv+S$eka3lw};|bUcDJ)I&$FczjtoA z+g=BkjiF}2WM=WZOND_%u+jXf?TvX?C)RiEIwX*Z^~r5M0R^EQlC9A=^(B14u#CGz zPdo;K!qlHnw_fwu*z>qD_`{*_>5#&*Y{U*Zl#eTXX5;1|??DPrfkJ`ZzOTq;3OA4wcTw3K z*YNc0M{fnu&*phj=tl^)WrZEalo10(_dkj|!}}U1v3pJ2Y>tXN{E==XhCsa1L>oAvF_EJ%zUz%Oo$>b7w7 z@DK>z9nG%PXeq)6xC4Ktr5&bbW|ldDvzjqd&O(K(@z+F06SR0u{0)tigVfW`j6 z_YMYz7|*_)BI#j;*8Gx<4qHI`iyQWWxd1)GZ@!iEMLtvtl75ezK6jNE#0>Q9Tx9Z+ zMU8+{Fb*8nAiyT{`s4D7buUx}LvdX@;ZN|Z$QA}@Szl7;_&1=6x z4|0xD@(>Q0>vtJ^Nr~6&8W2EDDUhJd62(IK2e6&Ps>TK|gEeoU5)$bdX=zM6aF{a7 zMR(17d%7|&XVts+#!ES^e#m8+>;(HXHmPoKBXfdTBxeW23&GVu3H9B2PzCVQQSNN@ zGIT;E$PK&}b-dRqldwOkF6$VXHxYme%ErIF{FTlP{Nj7{3hrNefQa4kt-_X`#E;~zG6k-5-}(? zv=yh1S*?C3^_^zM@0`~NU(6N1LSe=E(=gHA(!{#PFkE$A}G95Zu4|rPFh{f{TDX*?Hv!h*;)? z^AXjfgwvGQ0`V3plR0yij`YDt=lXEX$V7cbRzIn82-xoFxD%Ah&I6%*A|iiB-#Dqi z0x*;^^-t~1&9hr|in>8cmj|j*$T|+kyy-(y6`{yg4IVr$daLg`$5NZD`OxQ-&=Ly1 z-8Ah;q=OwvWGBr%#{GKECW^7dA<#aXJhbg3hFWx;THHsS7Q}tc?zgArghVLloO0a- zNOJ9F*b(EOOf(q`{~pyYl&!5)C;R6(dDjmZr@45Bg8D~g`B^+qjT z*4wkrBL|#06ML^6#sdZf@{NJ=0#8RbH=>=S^POlaA;;S<2MwvJU!J_vFB`chd(5&Q zooLGpVt1I;v%25%CEySBo`KSq8&A`z%+ABbND|SWDq{4{f50LQ6u3ooO#^E#CNdG) z)DmqN^xCsdz-*|wVbp0QV~N{V`RRhw&MTCRuUsi`lbeR5>f~BvpXOe#g&0|QCmmw+ z?mVgWFyO#&g(wHW&{OH$evFG=u!;9s>&Y)C-WWLwDek4_UHtE)V=uh|?eea+>~3EZEQnD=X{k7bo7W zaM9q~h~Qmu#91tPk@?dcGLe_Z!RzlHAFimW-H&Q!#4;Z?S<{~oCr@PMd-E~@5j{nD z6ix)Qc%Gac8fv#w^V!XI!j$(fV|~9~CKP8;j|)aHt(XU!5I_G!YdOr$BU3;?;1V(Q3u;7CB0oD3v!6e`!_S6I)9k*Z zx|72jSb))D&zAFc?PuVtk7n3ePUHAAVw(>Kt%#zFJ+)wi0Cd|#tU9FU#g9ife)vOD zW?Ix#r`nco>3I@IcEeF7mkimS_j#TgLbLOBRY)>4j!wdej^M|cV9}G{CwMmz5>kF5 zZctm$b-#OO!sfAQqnMN=aeaQ~$P6YNO2UyG`)Mn%Afbcwo8(6YsU!^dag z;o%|O-QDwjeSHJ>_xA{U!TsRf7idIBY6?Y`g)(jY&xP~*G7)>3Vk7>`7hyB!z1~Nd zfd@Tr!)2tee`G#%`EF3H>;_zq72t?lA1MFLWS7}3UDqycn9rx zr_Hn?t}Mx%q}mWl?lz+Xq;)<7976FdC?OS)B(MsFM}+)9|s+QYD~0r)`v5h>{CkMn`+*U{>zSH+L`Z+i+n&96lC;~s{lDx zxOA=rIKYG0l9f$n9k8<&>7x5PV~k~eM>%-?=LXVRZ=+f*C=fBTKzbHkJpqQiA6OwA0YmHhS4U1nW@1&mufT!JoA)8F#cZ!$f% z6>}@{0d78{S<$4Ex15F(i9SEB}wQz}M5wk8}@aJ1dBzm9CjlhY|~S z1F;(1pnz=M=KXgTkW=hi%R`Y*k{SckvcPA2biV3SgaCpdYPfgvf$_R&G4qf^&Vayx z@I_O$!GUf(xa~J)D*y$D|!ltQwO}fq)GB&tXUVMW-a;H5l-?Y?iQjh*h@jVV% zEuLp5K{0*Y*X<@EXr9m{ruLc7LTLWP3cn!YUi`z-c|I{Qp;o9<^h6aYhh-H83hhce z8UD^Sy1+Ng7fGf;qGwM&JW0P%7$7%vl$7@)Cc{V*`Dkx=|MD2b<1od2B5ztn%?m$W z2>n_s|Bbih)&kRboJX?iH*>AlpbH^UHS?OWk06C(4F@+y_m_xE?YDw|zlW{{p7>{8 z=muZux;?Q8uyG|yrLXyAj|p{VUmuo3^R+oZX&7G6ZSwQy&ryl)lwo|^ZX`t{9CRON zXC4za5)|OfXR|$d@RSwjYVXYo2UyV<14F_qTqLVYCqdrm6_3u{Iq(%u(KXyVBRa>rhgdZB*2M^{2NY|?iP16i@DCL2jhifZ0K%gM<5s=1D}&WL2{ z36H72*1)%J7k$JjsELh%0Qb*9Lvny5oeC3C?1**M$u0?Cz9gqGDr!aI8`LLD%K+?h z9D3W6tiG|#bNUOSls{hRL1l!`UkNB-o+(J46Yfak`>E?5nXnGw&7UsrNY1psWO?(6 zH;b9GmRY@Ig@u#|7#tdE{-skCICjybRHBngMNK{EviP@RTuJ^fJ1}@0GX@e>02aUF zn=F$}04i^>;5!2R^8nkh54XYf+Fpo_A5YJ>nws5!1LKj>dJUCE14>=#)862R-r)pY z6Lu+GPMDMw{%5zfli~%Tx=5~Q+sBh9MA?OZ>y1Ajiq)s22LPjqTrNCiBBOzv8~~NV z2Yt{s)D%kP-f^oJb%TJyRgTBsy-@ujAZsB*ezxKjWi$&&%PYfWo)g9P2D{kW?v^wI zuwS&&ZEpfPh%;83wzysNNu|pjeD*_RT2DOf!m$L29SP9B8&vBCLIgIza|;%^X+8<` zUv|zhr*tixIi4zHANSgr2-w%k#5$+&BH}L&Ncs5OHUu#9^Sd8;n=?-Sj(DV^)H-?KLyh=Y8in18{bZ1Dx!NvjZ^v4%U3o^JfOStGGNBoJN@Dh ztHhg7ssOSWw7JAOn&fqFP#)&xB*D_%K#BeObr7@zANu+f@7h9;nL2cE!i7+QJ!=Lj zDq-iZ4W+5WTW;!EJ{$@6OCX6{_HwS(P_x_Edq&-T({j^njGYc~lMZmx*|N5VDpk2ksU zSO8zLvhTrk$yDIVqORwnJob_>yJ;Jw<+$O%==#aP$mpv)L!35G(nlobS>P3D3Ad6n z6L@VrZJ8oov#A#iyYQ#ycc`g`Y~sAOkJn*)#3*DR>c?zQIM%mGXP#3A3gRN2ACaRy zvcl=$v%OeAJ$^?^=F`K@`ys|xl;iBHhwcG;Z%y4mmoHXSd3DXb)O-T}J|dDw96+XL z+Ed)Jt0PlCG41{SPe3m0Z^Ws#l0+>oy)A8)(94%+9ZETFPJGEs#7#KBkQ;3z%3+$7bXlnF43n?~5n(o-ZI({a6^Iay zz%T@zx3TaPE>L~3);?&Lb6P~6q7aVRdqDSeM)xs+z$3+~N_D#ZxmVy5d}YOVVPTmi zpx)4o12;5A{33ZIo~@5n@(h#R`#2G?+dP(37O=&cPWQO0(2nT4LTNRj%c3qvQon|v8KU6sM?i(p!<(B10FrX*wv2mPfbeJjKpPl{L zs;Q!`1=tdO<@>D1l?Y;Wim#w`KV|QE07fSw7~}eE>*DCcqNOb>(P<`~A{=ajcV4X! zO1D){jFn)8a6hxP6(FAk=sgX`i)6m!Zlyg0f~4VFvA6K5CTnJkWzVynuJ8LsU#wwu zt$ERD_?$?mbl2q(H6`&KliI!UzvOev=iT#3QH~4%EnG2CFi>LD>za?2-is7Ju+_1; zY8Gkra|>lA8l^-rL=V*hjDEdm2;fO##7W^G!9q?@3RfJR1^JD?zaSkfq+!3XC=CvF zawqRv37on8zN^UhLP1FfsF^yLqpN|2gBJ?2XIC|@+n+}x^Ak0NW=q6nyVe^+GUqm7og zfwZ-?NoA|E#m7>MY6{J*#;|=WM(!6`dr>y;ip7_tRbGlYU1*x_$c#1^JIJZAVF!1RF5N3WWlW985Wr z?%=IY@Y3RpT8cbM1JrO0?qz4z`4qzeezwj3=-&*f;#5PnVwS&l@BW|UEizdC`?r>5 zQI?M$0RayqV0bD(fHFM<=^aOc_VzkhE_Kv*=>y>aHoMV1OG)!1OtZWz>Gzb8}vvlSjh708kLFXGgz{Gy+Z z3kwCX`1tW-*|TTQW|xLK{&MJzhTsR^b{gAJkTUQU7LR7VW%r^#Vf_~_Us&tB#L*!& zUaDoT9v$7iK`kdQmLuhdALf!B5a#cRq{6@Eyyf2$AqC*Q#E$Vqo-Ai9{t=dFOG=eg z?WWNFvVp5VZ)bbkjA6@GK&*s}K3Q3(IN3ZM0vO8Ox?5Yb6as8yQIDHQ0V`^kH#bkx zpm#M<3lMyl7yC(JI+%Twh?W1MZORpf>Fc$AD@2KghGi1X*qf3%5LSyDzZZyDd3wzJ zU{6-FPo`{T5Lt1fdAo;RGb+*{(v^;s;Lh&zQ+$NlCg(eiZLlgUi7n{EErHI(6Gd$A z+X_8p07k4#RIFJLKQvfA47ADp1Qi+|fIf4iw|~oxfhdv<_zv2yYhc%OIb^{K^L;-} z;`+hII`1Oz9Smb7uI*Q8OY)2A=T)JYVIkv~7PpfZ9=~x#>Ed`1r~YD(RRb|k_hnB8 zzuq*XNlb@R<0h7+TBXMvtvoKRyrHvDxiWbLgiJzIl$9UAAi^LjTbISl02nIpVOrmY z5Heoho(_Sr5x9qF6_5;Mf5TNB1f4o2rJkEdgpYWOC1{nV*WJ64j8=21@oi%P)lgKe zB_zNB>1n1=78AdAUq>)brkGSFs^(W$ogu)cc9MH3eG>AAl9>rsAo}FetU#bV93jXI z08V?8279wDk4G-#O0O}|fL*!Up>JfM(qwAFkx&}dyAg#3M9GD?)ypP$`1#k&QA(=> z0%ZJf5XQ$=wPyf&FiB!GW+DMGF|qq&TY-iGEzY)8;>xh9)=g$!*Zzbr#8L7KI-kYZ z^nT<)ANQhZR%=HB7VzWotL8hSOdr-uA*0*A-%h&k1cPPR?!cS7 zk~Eg+HBts-ErvhfVAFYkKQuT;Q}n;N$2W4(12$G=uqYWRbT|^e+yZlswd16yn%HJq zS+)X7($sGm!8~W?uyvISn3LQQ#tIjj5X2yTYuCD`;__X@Ux5s!a)y!KP&zqf2~iYy zZK=_FvL=sf57A4A36QEIlbR79$7DFdiXSTa5B_77IS(OK2QgpyXX%6`{Nwd-fGpon z*C|!vVOb^pt`5?D`_QV*@%RDk7JM_F?w}jMK^8T{{NrD@hnVrY%ZCr;^Q6G_;T&Vm zC>BhPq5Y4~G%97WPEluq5_94|E0e{Qm8W=B*=!6_11bc5#1RHyEr^O%q{ktaBnj8t z>7jDLxwWUFXb8s-7&kkC@s5ynnN2yCHQw>jd!kv1N|mC!;d33P`rbxoET zh(6abJ=&WEbp=GpM>;b&;1n-~aSRr{9Bi39T7Do-Q4aF$;N*n&JH`Pepa*nhZ9@7` zdNpneHJ&wdYmn0=yEBkx7R0&HA$9(t@mmfEd zAMW~1Jgzq-p$4FyGUPBkRiHEFziM>Le68eu6k4!+wtuU|F+`b_UOQK-qvyv)r9qGC zkP3_@o4E{ZxiQ6NiEEA7dSq)BY_6aAXb_u&DIJ7TQCG<>a@OO@d19Rp_(EK&ON*!m z8;M0Rt`bweI$OE6Y%mvDV=p*lp`Pm)4rt#{hzByuF|>KbZrwBCX&)2!m}#U4KEHTr z3N(yi&&xzhB9~nN4TL6@tpJQPwV?VifmFRUf^A7F^L{deEgyRO5&8{R{t#Dr9)Q`8 zuY>VCQ*eZug4+L`eT~@6%!~rJIv$pQkdRIWm}8DXGKYZTOVfXmD+@^fR;AU)w>d=J zAGE{Q(Q%@`er;kWi%Q?BaxhL6#DT$-PhB3TUBK8t`gBT`xptIh((|MoQLsjBk_t+vk3$v)`R>BY&3MB? zq-8qYc&Me16IxZ<3y}u;?H<;&s}O?d|P(SHAHo&`z2|A6i|w{}KZJa^(rUVmtWKg^ z#;fy7YWaAn(HIp-Hrqg{63rOIegU-M`zV9HkDWWz?2el&JGej{-)YE0G?$^41LunJ zPY3Ls`LBN}!b?nBqeGS((XU_EKw}JfiOe`~)FELw>Ym7cRJWJUJ>Kh!RO_>qs!rJP z%QQGG242UjDvUMW#!xOp3i4f>R^G2={;7ry0Ai8|hbD2-MF>Cfl2?JZ1y z;CP*DFw*;#8|)uHevD>P>5=tq0&F3D3Jh@;3~_8P86B;x{>)dPma~t7q`=DD{JA`Y z8Ze9WIEUnNrIb9Ikd>7!1HFk?^BmDaDVWj|Z&(MTk8}tQKNIkmAlU@W{(GG@8p^bqdV zX&bpmkNeC@Z2p(G@?tc>Iedi{f7_p5U*R!Q2(G^V1f~}5!Sx|57E^`s6f4Ith-?DJ z)nZ>~y}-i;Lnfy0UAaofguvcj?l8ksr^nTkB~y$hy$hz*=%4`S3%#PLQ)CM;JCtNq zHJ4YjfzI}1Il4NFBZ>X*+Fqs>j~f2aQRUi{FFj+t^3KdLPc5A<47TyX2Xkz{*{dJk zIUQ;6j4TFKn~OXYI*Ki=wbjm_Ex+##pd3w)e=vm1!sHKRY38)4xRypg){9dfD})kN1)&l8X;t3er|ckgS~B699{0C*YK!zpbjOS}tiktDvOj z?_?qe<=b&IK$CpacR2>k+PB(y)_agLp~<2xd!}2~Ev|oKdDGf%jro@B4+CY}yI&@z>c&(S45Ir4vx zMmF~RnhiE_@crxjQ?-5xgR8GCg@>9u+tR26&8r+JaQ>b*xV~x*%fn+8B`Frn?A7IH z?%m5VgrNQecVf7AJZbAkwbI)#q_W{Gdg){)bipAP3uY+U@OvaDZ=%+vy)nJ)820LWr1HR3^rNmh1DqGsPs z>|HX01hwF3oLGc-I8o=1D~L> z^VM|O%*><(e`w4NBt=?aO@ou_Lj38@;o+f8megs$ z$_=Z;2=P5}R5wO25fL}&CcBB#+Rz=eVYVDr4t%Cf7ud?xc&45BI*+gzE888Z4|UT` zv{1%ZDg=PQQLVt87r_j1A~qlvyecUrwVI&!9A~q(l%Twx-miI)Pf(~H#{D%k3~p~5 zD2BoAW{i4KGh=elbP2+;dvvrg9d=`BM*f^QZgqdmfQ6e?1CD^&o*XD;;7q8s$r%BS ztW3+TxPhuuS?QRJIzt~KCN{RU!ia}X#sy561uD88m4*EH%0x-mpCQpl-p(K`q*4^0|)!+r@0 zs`icEC)d%$i)4V6Dez5}@9UeF^+v;%uElqEzG>d~0Nz=;4`|R{x?^)4rHUdoaYG@Du^jY!6{bv|x*s2RqMvn{*GU1>ip-iH_RC^X| zhC(^ne_c?EHi{>!cbg`PH0buLt!RR1Pm2fTZQ5HY<>xfd-zh=%NsIQx#FdGjUld09 zUVVuw^E74KWq)H%ihC`qK9GCKT8u(v{!ck5xydv9T)4jU)jy)$j2#VRrpt(3{9)er zgrjt*0GZUrlhY2w5jb3GoB=H-L#A|9g*Ts*1ArhY(6Gq)$wn93`wtT*Y2f`f#vzz( z^)ceN5y1h(6zw3YlrmBw)HSiP8Fel~z&rW|%5zJ;l2_{T7MbHx}_TvIdd z`C`lNKfzq0mYBPDT3B5NWbEHYGeM=g$lAsRN*XCwSbFf~8k`XqEO+nP?r5l}e&aM$ zf(uM3A4evAB{6;VJ;`SyV2glNH7$!Ho|K^vi*=4Q%b?_Z@dScnIGvwc0Arxgar3p$DT_P{|B)eXiW*{a(5 zK@$0{vJHN7Qa6KA*tn}S5nhoc1*-X;buKHzr7vusF#co0m>kmC();LL8?O1I1UhEm zva1JEiP9x{5K^q@FIjimQR<9ce3TzHCL*y4u33&%AE>xz`-!44(9M<3i9pHEefZT$ z(?mbf?z2Zw3#sqi^sa3hC&GVq{+lf84T@s*Q%6U~4Y)x8DwDuUyfTMgY)~@HN`(2{ z^bpUKi#Sm^?+n?B79gaLdYbWsZPDtyh^!R92jlAu2Oo*BKo2oYno(R$Sy?LNdyN4u z!7BQtZ$@uJwrtBuo}PrChsWhR0=kYs?`F$9(IRIY4(@MsYk_2 zyLxxMThBoQwvf?=r%Y89m1bU|$oM%toMW&Q$iH;2Gr%GAw|W4PB}k-3Gy89EeN-Uj z-ep!H;cuo6Vo13G=xAUl$nqtDciC29ze)Py2pF$NhR?b!;@2+4`7SU0 zx7p!~wGRCu&U~U&4a*1&=0~Lewo1oSylP;TGUnn-lIC+*t;F3wH7~?YFoKosO)arH z7SQRkmB#UGpPthx%Q57fAlFxhbvhMQJN&ink9?Ye?$rZZ8eNH_6{H*8kP=l;jd%+Z z?6xLT259NQug9Eo;(KIJ9>)Rl)sdo+1Hdp^YYeI_9bk?nAS_HyK~3F-PR|QFjUsI$ z?7>>aCcr{P(F?J!p`@Xp@HG)jZwY2Jl@bh*T=0b~P>wR4&yeMFS+BZwi1O9>7#R*)$q@@Ry37WF4jEC4CvW~EA z*FUS~WktVl`D=d#4#d2J(lHqM$NA4iZu86b}J0vk|9 zD*kf?H3xnYeXeC!mx_)@ijI!;bWF(3wD8Elc{j1}T)~@GmXQN`=wO?@zvJ zp4l`pV0cPtCxaT2$nGHb)rxYAiRi7Kbo%RDj{>S}=dHThRd?|TD*fT_G{Q%?0@|ZT zUtRPd#RQLW#V26%r~`BCWuVNd9WpEv`1P6444<(-COF#9^o zsju2C@W8af$c3{uYAeF`CypqllNClp$gNTMr(>qStrpDq!EpJiC4VRc-3^#xZH;-& zesm$97Ia%Y5bd#t+^Rx)ZI}j*eMkKJ$9cg7z81uGg|p0t5ieJWD1LR>azIH`c}5`iO-(z{|Y|c`tVTyE<~3y*R0y%d%l(h=J)?LP!g@oU@{dx zI-2XT8@z`@uI617I;)o!(OU?nVVK*>B}_9}!IUDa0YbM1PIXScM{nMU+!RCmkqOK~ zJmx5x6D1>l7|-*n0Q7O0pddUlJTem8X%)DJz}}AQy}J@utA<{(rVe#H4xB!LSxOGT zr>lrgBelh^!TL39K3B>U@@-_t_yGorFdAg-4}dK}5Nw757`uPVoJ;d*cZC`t(HJt>WG&&ACMbGy!6_sBE{>8u_dYdd1 zkc7h;n!6@BWpCc@k^o;2wx2*Yd^UwNM@e$u9jNKnh9gv2R*R%QU{?3m?hn>)?)WPy zwiyyLZD>;Thb??;3hes{?Cdp0kla|wg6e;_mbsz4p6Kvd==4ZI2gpx{cbJ<=y8CXmoH#Q-B~h@ zX&OaS+`bGZ#SpanCw{slB(;Ag4loeyMaqD!8J zOdXUq4)c6WJT9EUSGfRiUUYx>FX4Ct#xZ8Vv}ox)1e`EwurOIn-;@vew@4)Y6;7XH<{!%NXJCxrl{me5|SVsCe8A;^7Ws%`x&yTGz z0{(mq0bRk7q#Dd5$MitX)d(oP) z{Qj1h-_Iz*WdlJ|yG0aC3x|UmR(at@)ama#h*SmGS1x$xGf!F1Kqg?S%0wg+yngn# z?EUDHW)?ur73TvgHx0$4O1<`y02Bzq$K?xx=|L_2we@wTGZTJ80%lcSpIYk{N@VOgL;a@dxyow= zaSh)(eLUhi5Iv}24hZ;tJ#U6LA$foZ{rR-Hxd{h1oZ_le{gTSvCk8fu9$;Uc{jZ|) z@Q3=3LL3=cmyu+I4rgb}DkC$SY(?g&$R?YNWMt$DneqGn z{)0Ta@Avb0zuwQ+^Q)Zf>Ky_$RfN;ky02pYNs-9EHktt!EO%fSu)%!^UL+rg$IQyY zQ|qU7B}G%onLpD;6Pqx&0w_!I8&>#@)=%bBg2>$sFKk?0pV~CUL!-Sznwu zxk@z>o1*qkWQa5^t+$_@D}W*djju_B1%N!~(jTto!Li+}4sU+_SGJ9MZ&}9^7Fn%GuP&1Wu3yf?|lC^ zbUc-EeK9{loS|PdoWDL)k9VlE+kESeD3j(n38}=R(hNRJeZZKk$+3^>We2nl_#fN* z_CISNYH}Sw@{s`vQtZ8wNjk+hpR%lOHhag_L#rF&7*`I4zz{v2%`o3aS8&@Z%mv+$ zXQ4uh5W#gby$Y(eriFFf%3&&>ZeB2>E?AXau&6g#*c-j}@Tv}9AJxT}vQW?x>TaE3 zybyT|t{uOCjqT$pH&Kv(+8s~)DGIzzPBtG=4 zT+sdgil8~3z{osbQN89DcdBg32?q#S98}r_Fmhe}dwe{|m!(&t5iQRa|LJk0qX3^U zQi4hnFaCnN8gQl)eIp|)``3nPdBS~~{*4ZP{5jRsSASV&1o?xGkaD>b;p&z#S;ZTR zNiNVOC(%de^SiN3Ouru6mD|gjGWlDxkv_64o@soXxMfSE&q=rhYo63E^-Anmw|Psb zlGczXXUOdcLgSL!0+JLMyVME=Qx8gD1<(xhrK9kyw+K&pBpSd3_kinIjXPtgYlO(7$N^Ptv?l zQOV!{lCHh@WKc{af&%C#p`pdIekL=95B#l!LLDJ*T>5div2rzVi+yr=dAaOqA^WYI z`-s?|f7Q#c+hPJ2bg9EOMyyjcWwq}8 za0&iqtRF?zw|&3XZ_v}m`O$~B7|a@tDo6l!%G3vz z5Q{`&0CUyZsam4`01l2uqw>8CnKYy~tHU5fOel>vW(jPe+>+o=xZ3Gvpj7Gd+^B(mP1USWZeh=hqy0t~Un19mM zftap@q8`#%;1Z>DbadKzZza_|%sPRB1S%)Jvw;x+e9TfM1t!S|9uNq)8)hrI6#RM zqrEh(&?-H41aT9gL_(myFL$E|el-17@9C%C&QY$#|FXyv#?t;}ykQ2w;MP17<+{PV zPR-Y`yOj)4@6ECgBtbKxoYPrX zyBX?6%crQ-^6LMzY?z9gT1|vA+3W4C7(AQOVzdi-L^XuJ<}Hy(%j9HT?WEn{Xfp%v zz7CBw?MTM2>jt;jn*I3WA0Dfd6fn=JUMx9%$>#( z&qqf(P&#L84xlO`>j5_I2M%Jt$rHdnD355161YSk`hSU@b*Ii$x{Ovno76?ctuYl4 zyp7%(&10X?%dz%!RH0mo$T1rZ9RyF|>tMVQKX>yT^OCrCNK;6&nb^nFdb+0mB30)E zV*IGrZa+0(pZV(k1@ToM8<^U?947pj9`^b6ntK5bpofD0hYB&FF6voz<;QDWSC*BU zF)=Y6V5KwMU%G1ynYZ>^z7=xPRn6y6o+4UrfYgu_h{lw< za8FpkifYuGBX}t{;@;s~7usBMNaPwLhmB_9%-#Kyh?nCD%<-SB!I1t&t(mZgW1I{^ zx+`l`#F>)q7hk7cjYU1cbD|Xtz8nRgma`$B|Gf_V5ttWR`|MlQ^z!ol^5b4gDDZ1N zPULNQMYXJcI4Qx}!#;&(EkKFA(#k=Q#sXl|3MYrS2r*(IrFV+{Io4ZF{($o-S5m{n zz*7zZgpnXw*f}{n`8boMUX+$7ht+iCJnUgw=z2u$PI9tWbNbjiZO69w_ZPDh>i-#W zbwf3HtImN`>wRgpc&k`=xrH`(c)3Al=LIwLdXBWunaSX`jcf6Ionx2ATp!Nv?u71! zxiujlEij?tzi*EtH%Ps{wN0t`ZlwT#E6Xr70=t|HS`JVs`E&|Nz}OCT0bQg1_&Cn+ zx!DCY(}qurEG2-GgX0@$sQR@uL^r#E{?-&2FSIyW{;R_Pb z+=1{j?Mo)O>q_CpO*nyWpF(4MTd!8|4m?k>*TaMIse>x7oe(c_oe^El%wNT*9Z zMC7NF66WQZm}EcnRI@-m2cays{B-u)5%0@)Ig52A7sys9&O50YdFVbCwSA{XMJ0$A zFafK{+B~$F7Y1*Sm8og$N%_1t0y)1(TVy}80oTT-d?N;q3z_{E@UE$~d2p0{OduQ2 zT6u&wtBhzF40tug9&+2y6};E>Q`#(Jf_YRCa>x}QQB(!GhHgRJd@LjurXg;^ReV~g z*zTH2uFpw!JTrEErb(cSV1Wfh$QPABs5Qk_E7PC}(#_l=j=JsZ<`zdG$%HDW!QV+t z3GX&W3Y7>P7CV@V0vcWbK=U}|H?v@I$Si0(h>D)~b(^{qCiwR(^_xrw5x3VQ&`LWZ z2bL#}zya8cRia=u>es91)MjNgAjLiGH~9Id_WSWmjj#gc?USN^;M}fj z!Q3-LQ>J>YM%{IkBk)b7s($in*^GGa&{lQ;`jR2RpRhk0;1W&L~M^dL^$~6{p#LB6%BfO4yL#i1z z#xs{5F+qBF^94xpBJIO4rs@@|kgZhDg)jA;n}YR!ci zWve%M1(P!so~KoraN=J_?`wueo(V3?nQ(1iRDmI^Opz)v1<%FmN9HWM#D8~lS(5-6 zYl^`~p@R(l8utj-K8Qkr$3xMS*V-dUsN)zVxJ@8{Dqtj8Xp+Xg(P7NpU1)(bs0&Oi z(4UY z{l3d{kv*q98~fw`1sS?HeDv9Y@vyj2ttn@dn*N(|LJY5(&d5h7@`A5h^mI~JqKH1H zx%N7+!h_~^8%i^ag#tX97M2Y9$Pb2pJaQkPdUm`#4q;2TJ;QQh!e}(EbMkF zAzs_nGaUrf-*=LA1PaXdx+30JZ`tSC(w;o1FV9vhPz6gl;%T;mL2GZD|M-AfB~|M? zlBfJyp=i1gDmO-8Flp*J4d**gxct^`H`g=a`G@N;nx_Ta3I&5ipPCMiR`2c?p%Jn6Wdv5G@K)XG1>JsG>Q`g- z*b4@*yp~W~sM^qSo!%A^I`2%a(>l;_z4G5R#ML&m`onzN-cA*dbyUxFjmYv-_|7sLs)o$1INH4Ul8SuY3 z;FtvH|J<9c*Q9gQiVTqOUP8{~fR!AI`V;7`tY+ye6_PrrN4q*n&RDrtd;;%~N26f+ zZ4PH5=o2za7zC$~xjtWb|J8sGsO0FSi~X1t&E%JhB99ChcB%u%mvfgAtmW%P(~83# z@Mr7A7_YstxBUQ}`b9=MW}SS!c@BR&K$S871W=~`2@ZZ{!rZ%HHfnv;p8E035QfOs ze&495tSlOyoZ*lm|7@7+m!@WZ^m*FF1kBT#d(0p5c;a`ELy5YBh<%Ks^S=SDhDJ-=aXtZ1%HV??}s+mst)-sq1E z)Ec;#eDIIwL}!77s#e#Z`yy;3=FE6MnLq>;duIGA*?Fb(UV&PbMwkT*+!I#g!f}|f zqZvDKi|#ye`z@OJ`|Jf){$*(9kO)QRHMZ!gFUh@5^d-zM$yISMX(Z4i5IAA8qbqg^R~a%;wrN zMIV8uq!n6rPrds$^~$NvLDd*JIB>G<%Td*0f)g*1Cu3ZRXUGfmUmB*%AfT?&+PX;X zzBN#+{s|UUgWG@;;01gj`0L+KQ>9kGSWJw7f=NC1c{1xNGr&T|pCn1kvqq!^+MD3D z-bo!taF>tiQAIs_}UjZjjgGz3?uOxL0{*r=0C*69{33Q*NPIbXmeI|W5x?u3Sh4pRkI z5=4$nfP{pEx}P#7ay9Z@V(wSA0J5K|G3X=6d3l@KU?HiF@*gdM8*ei0ot(59J{jan zn%!>ds$e%4LGFu*Ajf%%G(HRINENO-^ONDr+KJhL9(gx101N9Rf?=^mmRg#Dhf zJO33On}0R+^wpiLV5`!-hpzRO?Ly$9h%bNiYGC3-q7OGy1((2!hkLPG1E$`UMxPSn zHkbz-BkjtKT5YE1gO@~xxwm;y#(Q2xDg&qWfJX4ICoez9%Xh?3acC%*V1)#3nw{Uh zvrwA_9~&?Z6c8BXQULNiMY_*HCrw)mV_oA%CjL~dtZ*LiikXE@bp}WrZgtmSA^2e@ zY_3ya4N4+hJgE^9ZOmV!O+XXDEPUGa!57?9Fg_xwbh3hf;JqU^Shu?bk*6#-t0F(e zUcQXdSOo)xHdbNBGXox}=mqj-T8Q1m8PAD8tNFTB{G{1dV(&F$&dys0IcJQtVSU z8-V$F)2RCKGu2y`v>|C27@+aYfJgtdQ!8joQ}dbGLe8dBVjMzbQ7C&EzzHTBcuQY! zZOS2hwnOatzh3@3cTTWpEi_-oC*2*yFO$M6&##G4@LPa0EcZAO3DpS>$5p9@%7PK$ z2t?tRmm#AJLqZ%A-xVO?_0SU{TTv$ZWuOKwVbB^GdFROIz{;L-=;;Fm4QbtO2hIlR z;;hG-eEwiTc7&_{1EzKdt}-@eoBB#s1vbP<#qV!K zj(j?u%I3o788&pvBBnWk%70%dc7!L6ssW`&P;mH)N5RY2chZ?ka@C|6suxl1s`17c znMe^U-hFIIL{9Oi^*70n*^=A2eB&^L{I_k?H8;MH$Z!oY_zS@`*cnEqV)Xf^Xq%aD zxq2ldpuf#kt9aZ5U+r($A*0sgkH;5a82MJ!DQ;Cs-*P5bCKt*Td^y+$2D*mpPfq2) z-U?GsDOW;d9kkKynRFXMF`L8CW1NF__xYNczW#4)p$LFGl7DHV=P90k zNQ}01U={8^}@b=yeN&(QaIY(A=lyAKbLVn#FeC-Kf7VXsWAD@1> zBYL!)`FCFk*q$Q!naI6DLdD+Ag}C|A<#BlI;YLTMQB}8Q8lQFb7by%wdOxC0dB+_d z9>|3Q!sNhbi&iv#g;uk==>&{vSrKYtvkS6>-#F#|!T{n;;=85gA3r z7h2LXhj>gNjCGYXk|V6fqN->rk&%YmqN)JT;ggGY78>An|HD#MAl)zbDNS(3<=)+{ed~Xkyp0clUu(LZQ zTQ+6xks}b3lBFPt1wIf!f!P>$@Mkp3QZ3pp)8mJwNP=)53+lFEDrx?<*Ce{!Q4A?< z0n@jyCJw)M7xj3*C~7kGvC)iQ2`L5w#ER8J5mls~#G{qJ>0`AL z%abSg2ge!1 z_1s?m+KWQa6e8&@fJk(!p^5MUKvQEs-E%_O*N3qTVmEHY-OV)+;I{*$B$Db+VEuPR znGW*+E~ATazJY!*wd_pTrYf)!vtmeVzDBwEj>DEVfYAlEJO+WG8_LgKm+V1e zuy4_}U2AgDGYh`g#|YeGFm#8R@TtHD{fh93*T_yUh29BUvBzIA)W;9|&Sid7G!&?~o?U3$C0ES}b%Hi!sdj+IN2`V$+>TtiKhd7){Nj3sGKaTJZ?OYa>M8sWDXM0A9d zao-J;RgJWi7n_m|f2+ZXPy}xJ>f~jN@ZLtG*E9W@N3;<32))(#ga@}4ipPU_4|xcm z$C-!k{P=_kKD8NSJxMG%U1$3|m(daI?VIR4e$q(^qYat5I11?M7>F><|95_@|6RXW zO)oTv_WT{Z&6=TXXo{!`# z6qZ_qVb><1e(rLRcX3PN0vDn0A8^}qySxm-P)Mr_WumEnu;qype+_z$V%xMp^=gU{ zEwhHrEF<_OO*rad(Ff#FriF5hMc>s})Thg7=A42#u&!48v4^vGFVS>ku1B^}TqXUS zBeMJVw!elGYPI6G>tC)k+5+~h@b5zoBHgb3v5_2%-<8;{HG{P*)5)Sl!#v@9VJ z{Q|t$bhrE$p565)RwxHSx=bJ*nQfz97=k0~gj3K4@$M=Qn%#+Y@!0x+XhPjd(ZbfkxRkDb?#+p~o(anp#wWXsZ|8 zW8ZaF)>-I@Gj@88=wlL=Q9nF2F{ERk##1w;rUDlBoVR>j{w9-$-kmBd;k+kJIc={C2g&2++BP`cht zqFXa9h{~US%XVwGo)dXCDYDs7Sy#@MbFa8@n9Z8^4mR==c;1jPL@u@8dn*q06r_^G zHc2w(YCZj76V$Hya&R3GoNk?Jo$9|sP?nZ90|k?x;^2iwVvnUf8zMtv5FKP{lQAB( z{3kd2-px$j8eJv_q+wwKpAR390ZTi4K^XFV&oWD?PdX`|cqk|-6~L`Uwm9M(2Dvg4 zNE__DD^i$LwMu}}9Nh0-PsFW}m)EZ75F|6Jac891>#H`TjQN!SKymH=Zv;4!rk_cy zumOnoSEk0!50}E~*(Nxch81)b=BB&ZUES3JJu!(aM((WLC_>dIH?dSpVM&Q34^*sK z9VOz2973V~cp=r+d)e<$UgsL#g0q8BLL>;BeDaMZ;0VB>W{v8emVu#aK- zDyT1aj$w9BUbf$>u+3DBRC+cAbQkR8uGTTGsI)?gkJdSHy*03_Wd}J&!yTR-&bIKKRTh7xOfd{g>xM z?;~*EmG1;eNV|5z$;9{vR8my6+9bi4W5sfI^wo28uwY?vn@cUk24+itU-+WsFZJ*t z9Z;>a^)<6^3Wh2;#ULC+U-yK9io!URvL63b12Kqi6qWR(*7qOV%$Cmd8oFjHdx9py z84s>Mqdf%2r@?nQ7wRi=miDyja+&3C$ExTP$Ha$cUP@H9w*c2Odwp*#&nlMEfm0L(}BHnkMis6S8BCoL;M>eZX>agn2}Y>M&)cHH!b$FC?b_J%{ZQ z=VH}#TNJ1IM$(0p-p_yhJE#dXo&Btw0y-B-H}9>M?U(;);;;jcE7|*g#%0RcA?TK{ z!183HhN7B~F%bZUx81~Fw}LKkZv-5X_A|tYg_Xeh4Ju9etI`$6F8uqYohY^Hep4Gj zhg+CZbH8rA`03FMZP7MS1H@|g7{l)Ay#XbFGazg+1E{k^=IMo=r0w&gG&?yv<5rk z;bXG1R<@oJB5D%9KAy)?HEfg_c{@Fsa&y15mq2Ulk@v|!)3A?<+l+d0^$`?_bzl)^ zs)$ERmcz7c77=Y2qeZ`P3gzuDI{i)l=M;eotuj=aqTHZ6U~DyIxzyhgOg z-uSZrn9qEzYUP@kmo;RMi`JtVnQ1;gO4K8Z`g$xpd0|l70_RWETjGG~Gb zpN*y3myH#r4Y9Sh4=a_hdX2`a<&VyQONSp;$&#CC{QWzhPH00)RNSVd2jjyR>K$9x ztdq~JJVkFCd?W`630#M%G-S*NrX0=d6)L+W7 z81ODWsdlqC=*7{^q4Q@1f{K^piEov!dAmdq8#NeTC!0bzuoc>s{u8SzoO8C`3XYrG z*xc0KDoHa)SmxVipeZ_)8T#T)77n`HPX!e(rMG+C?=OGd-Qi!hu2PnxTlz`@RH{#r z5p4DO0uo7oup)QC80qvR)p6Iu=e4IkTVtNgSjJ}4%Sn?KY=m=2I0q;TPWyoj-~Y{= zFy@xWO%d(PcK`H4Jrp`*nec?Gp6tn6MU6)y99XE!b*Qj)ceBF4x%N(uk2sM=s|^(< z1BROy_a@! z`I7|job{WjPa2#!)Vb$$*ZyIP4CBYSw5%XpOEjQL@;i32U1$W zNvcwA^G#ZPx=GWPmK#NT8xp(p?kEso5=;&6ZD*pfw67<*3%_57`F{8ZozncHefw$A zKZR)0jl~>OwkGA;EQCrI-(ixjZ3RyMDJ$CXF48Z>(^c-hEIQQ5=_3fz)vKk=v~1kk zk@9y~bq!~bak$8eyl2ShNZzhyOdOudlh(VY^m zxFge^=}rj^ih#(>k99QqP_Y8D0X!+i`yUE#AVeva<7}S7crL7$u+-79fdQmHtfcO= zlY?~PfQ^z1$6I|WGtd}#;DXNyTk%m(`=(7;4>8D9qX}Zf=Jonr9tB+K;>9|qvEvIC z>h)}ZF^zN^Ni5m#n0K`#4}D#iFWNu!R!i4dppqT9ZVr_9A|=MT$0u6MjpOi*Mjy=b zsI*Oc-Zgca7C1L6Papd)Knozmj82*zbu>^qBC_>0|Ipsv^$uG9hozss?Hz;FVe^&S zRs6;z(}yPa6`eX8n>#FFBRx-kfUS$^GdIc~jRpcl+KgQqj^_psCM3#|D3|NK;h?}Z z%`C0fd`HuJ{F3d@T;dv5 zqBj}se~SM=Ea2;hnCmKDI4eLMENEuYd26#SzTV#6tbj-2=zbF}akLkG1sdx|KrJyx zB-i>!yADzD`~~-O!cyL56;)kxtG1==vl|x^U9xOJ7j5IMFN5FCsouBLpJAAIMoNSV zLU_H8fAW=N|ZxtXmuSS3{QDm%MDy&%2(m(->d6Fx~1Zia}-lBisAB@&_SuOIN? z=zEl9UiTSAyh!keCt(xsKubtXxTh|mbV?N&Mr9^^vd7f6*1os8KlA1cD}pVvo9=_e z58mh9>f-HjcK)n=ZryA9;c7ACwsM#%q?aN;`BKz3-*B8uK7i*|tiV4Xh zYsjE)8{goeuN!HA*R9w9uD914$ah$rGTs0G^>eJD-ivL&F%zt#VTvM4qHZ3)RPH~F zZnMuq#e{IlzqI%x(w!ApZ*aczQs9x7d+>s{h4JkXR9HE;VKb5pI-2u`!_~}a*p+W? z>#kDgD3oO2NMWQ@MJMb8J>v!ALc$i8mu7h;-v<2&nzsUee$eS}^nY!m6N5h>VK;^8 zs}r4mHFax-AykQaez0{?$LI~ep<_J62~<3$l`R?Yee`IA=}(%i$h&v=@~|gEYqxh0 zY_1FKl<(|d%a+u1M(MP*PTMcFW5UUom`J}DOAM33zBRy}d5Yx3^?kgg@5)wA?Kj&XKm$3c-9{wGqiv7ZqM9>dT_&CeC}VBoV^5wCWo;5y1lh=f9*_AO5u0^an-;Y+Lq@^=yE4Y zHUIK^TmkX8x;YabRpa<>cPtDj2v@v$lZW1qMhhGH^I9o~+J|&=fisA9L)5$xcCOI& zAN%81m6I#76=qSPJAq~^9!M5D-jl>9{&|$MPzXZd!=IdWdrJa`E0U(6u6yS+rF5}ffzMn{ehSgIsYeT0)V{KnQ19JzRVm9j_2gU8E4JCxj5e8DHkzgkj*Hz_gtR8a{ z{NT!9pyBf=D@)6<1#+d&e>W4cMRnT<;`Z8FYMR?E@AC+E<2-(8hkzEP05A7Y1-YcX z#YLNy$@%pGY?t?%j_ZR5Jn!GSJjwVqE8XY+xx(?p-*? zGz*RCYB)r?SUs%Tqu!!!ibTsGxTS1&vWwgBk*qiz-NOy}MatDhbzX_tl`)^$$$s2J zE0qaPX;S0oe<{1aE-zEdhll7CSP@N~(HHLQ?DPh~UQRM!*#4x*2fyBgh@n=T)_N3h zbwzTm2f`snBy##<;i#Wq6(dh?ag8UxkJOzkY)F%HUdw&6SQHaMX^J~UJ`GaG&N?w? l=CJn!s|6*W6@VXnS%?})1{M)6;CICUU2P++w;J~0{{gi9X=eZc literal 0 HcmV?d00001 From d6ab56f02779bf997513aab2df3e99d3090391bc Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 21:07:34 +0100 Subject: [PATCH 123/176] feat: Add selection sheet for SaveFolderTile --- .../java/app/myzel394/alibi/ui/Constants.kt | 3 + .../SettingsScreen/Tiles/SaveFolderTile.kt | 181 +++++++++++++----- app/src/main/res/values/strings.xml | 2 + 3 files changed, 138 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index c294b56e4..b8c981632 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -4,6 +4,9 @@ import android.os.Build import androidx.compose.ui.unit.dp val BIG_PRIMARY_BUTTON_SIZE = 64.dp + +// TODO: Add everywhere +val SHEET_BOTTOM_OFFSET = 56.dp val MAX_AMPLITUDE = 20000 val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q val RECORDER_SUBFOLDER_NAME = ".recordings" diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 765bb50df..2436856b3 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -2,26 +2,34 @@ package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import android.content.Intent import android.net.Uri +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.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.AudioFile import androidx.compose.material.icons.filled.Cancel import androidx.compose.material.icons.filled.Folder +import androidx.compose.material.icons.filled.InsertDriveFile import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.PermMedia import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -30,21 +38,30 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE +import app.myzel394.alibi.ui.SHEET_BOTTOM_OFFSET import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE +import app.myzel394.alibi.ui.components.atoms.MessageBox +import app.myzel394.alibi.ui.components.atoms.MessageType import app.myzel394.alibi.ui.components.atoms.SettingsTile import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog +import com.maxkeppeker.sheets.core.models.base.SelectionButton import kotlinx.coroutines.launch import java.net.URLDecoder +@OptIn(ExperimentalMaterial3Api::class) @Composable fun SaveFolderTile( settings: AppSettings, @@ -138,33 +155,107 @@ fun SaveFolderTile( ) } + var selectionVisible by remember { mutableStateOf(false) } + val selectionSheetState = rememberModalBottomSheetState(true) + + fun hideSheet() { + scope.launch { + selectionSheetState.hide() + selectionVisible = false + } + } + + if (selectionVisible) { + ModalBottomSheet( + sheetState = selectionSheetState, + onDismissRequest = ::hideSheet, + ) { + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(bottom = SHEET_BOTTOM_OFFSET), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(24.dp), + ) { + Text( + stringResource(R.string.ui_settings_option_saveFolder_title), + style = MaterialTheme.typography.headlineMedium, + textAlign = TextAlign.Center, + ) + + SelectionButton( + label = stringResource(R.string.ui_settings_option_saveFolder_action_default_label), + icon = Icons.Default.Lock, + onClick = { + hideSheet() + updateValue(null) + }, + ) + + Divider() + + Column { + SelectionButton( + label = stringResource(R.string.ui_settings_option_saveFolder_action_custom_label), + icon = Icons.Default.Folder, + onClick = { + hideSheet() + showWarning = true + }, + ) + if (!SUPPORTS_SCOPED_STORAGE) { + Column( + modifier = Modifier.padding(horizontal = 32.dp, vertical = 12.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + MessageBox( + type = MessageType.INFO, + message = stringResource(R.string.ui_settings_option_saveFolder_videoUnsupported), + ) + Text( + stringResource(R.string.ui_minApiRequired, 8, 26), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurface, + ) + } + } + } + + Divider() + + SelectionButton( + label = stringResource(R.string.ui_settings_option_saveFolder_action_dcim_label), + icon = Icons.Default.PermMedia, + onClick = { + hideSheet() + updateValue(RECORDER_MEDIA_SELECTED_VALUE) + }, + ) + } + } + } SettingsTile( title = stringResource(R.string.ui_settings_option_saveFolder_title), description = stringResource(R.string.ui_settings_option_saveFolder_explanation), leading = { Icon( - Icons.Default.AudioFile, + Icons.Default.InsertDriveFile, contentDescription = null, ) }, trailing = { Button( onClick = { - showWarning = true + scope.launch { + selectionVisible = true + } }, colors = ButtonDefaults.filledTonalButtonColors( containerColor = MaterialTheme.colorScheme.surfaceVariant, ), shape = MaterialTheme.shapes.medium, ) { - Icon( - Icons.Default.Folder, - contentDescription = null, - modifier = Modifier.size(ButtonDefaults.IconSize), - ) - Spacer( - modifier = Modifier.size(ButtonDefaults.IconSpacing) - ) Text( text = stringResource(R.string.ui_settings_option_saveFolder_action_select_label), ) @@ -218,48 +309,42 @@ fun SaveFolderTile( modifier = Modifier.fillMaxWidth(), ) } - if (!SUPPORTS_SCOPED_STORAGE) { - Row( - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp), - ) { - Icon( - Icons.Default.Warning, - contentDescription = null, - tint = Color.Yellow, - modifier = Modifier.size(ButtonDefaults.IconSize), - ) - Column( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(12.dp), - ) { - Text( - stringResource(R.string.ui_settings_option_saveFolder_videoUnsupported), - style = MaterialTheme.typography.bodySmall, - color = Color.Yellow, - ) - Text( - stringResource(R.string.ui_minApiRequired, 8, 26), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurface, - ) - } - } - } - - Button( - onClick = { updateValue(RECORDER_MEDIA_SELECTED_VALUE) } - ) { - Text("Use Media") - } } } ) } +@Composable +fun SelectionButton( + label: String, + icon: ImageVector, + onClick: () -> Unit, +) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .height(48.dp) + .clip(MaterialTheme.shapes.medium) + .semantics { + contentDescription = label + } + .clickable { + onClick() + } + .padding(horizontal = 16.dp) + ) { + Icon( + icon, + contentDescription = null, + modifier = Modifier.size(ButtonDefaults.IconSize), + ) + Text(label) + Box {} + } +} + fun splitPath(path: String): List { return try { URLDecoder diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5e2bfbd4f..859b47999 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -169,4 +169,6 @@ You need to verify your identity to continue Custom folders for Video Recordings aren\'t supported by your Android version. Alibi will fallback to the internal folder instead for Video Recordings. You will need an Android phone running at least Android %s (API-Level: %s) to use this feature + Select a custom location + Use the DCIM folder \ No newline at end of file From de72f88953d19cf18df413d8d228f395000c7baa Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 21:11:31 +0100 Subject: [PATCH 124/176] feat: Improve SaveFolderTile selected value and order of selectionsheet --- .../SettingsScreen/Tiles/SaveFolderTile.kt | 76 ++++++------------- app/src/main/res/values/strings.xml | 1 + 2 files changed, 25 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 2436856b3..e33a67200 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -194,6 +194,17 @@ fun SaveFolderTile( Divider() + SelectionButton( + label = stringResource(R.string.ui_settings_option_saveFolder_action_dcim_label), + icon = Icons.Default.PermMedia, + onClick = { + hideSheet() + updateValue(RECORDER_MEDIA_SELECTED_VALUE) + }, + ) + + Divider() + Column { SelectionButton( label = stringResource(R.string.ui_settings_option_saveFolder_action_custom_label), @@ -221,17 +232,6 @@ fun SaveFolderTile( } } } - - Divider() - - SelectionButton( - label = stringResource(R.string.ui_settings_option_saveFolder_action_dcim_label), - icon = Icons.Default.PermMedia, - onClick = { - hideSheet() - updateValue(RECORDER_MEDIA_SELECTED_VALUE) - }, - ) } } } @@ -267,48 +267,20 @@ fun SaveFolderTile( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp), ) { - if (settings.saveFolder != null) { - Text( - text = stringResource( - R.string.form_value_selected, - splitPath(settings.saveFolder).joinToString(" > ") - ), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth(), - ) - Button( - colors = ButtonDefaults.filledTonalButtonColors(), - contentPadding = ButtonDefaults.ButtonWithIconContentPadding, - onClick = { - updateValue(null) + Text( + text = stringResource( + R.string.form_value_selected, + when (settings.saveFolder) { + RECORDER_MEDIA_SELECTED_VALUE -> stringResource(R.string.ui_settings_option_saveFolder_dcimValue) + null -> stringResource(R.string.ui_settings_option_saveFolder_defaultValue) + else -> splitPath(settings.saveFolder).joinToString(" > ") } - ) { - Icon( - Icons.Default.Lock, - contentDescription = null, - modifier = Modifier.size(ButtonDefaults.IconSize), - ) - Spacer( - modifier = Modifier.size(ButtonDefaults.IconSpacing) - ) - Text( - text = stringResource(R.string.ui_settings_option_saveFolder_action_default_label), - ) - } - } else { - Text( - text = stringResource( - R.string.form_value_selected, - stringResource(R.string.ui_settings_option_saveFolder_defaultValue) - ), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth(), - ) - } + ), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth(), + ) } } ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 859b47999..e6dd15063 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -171,4 +171,5 @@ You will need an Android phone running at least Android %s (API-Level: %s) to use this feature Select a custom location Use the DCIM folder + DCIM Folder \ No newline at end of file From fa72ee096e5c4c12ca3443de1e3c8377242e984b Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 21:15:42 +0100 Subject: [PATCH 125/176] feat: Show snackbar on save folder change --- .../ui/components/SettingsScreen/Tiles/SaveFolderTile.kt | 8 ++++++++ .../java/app/myzel394/alibi/ui/screens/SettingsScreen.kt | 5 ++++- app/src/main/res/values/strings.xml | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index e33a67200..f8d82981a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -28,6 +28,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable @@ -65,11 +67,13 @@ import java.net.URLDecoder @Composable fun SaveFolderTile( settings: AppSettings, + snackbarHostState: SnackbarHostState, ) { val scope = rememberCoroutineScope() val context = LocalContext.current val dataStore = context.dataStore + val successMessage = stringResource(R.string.ui_settings_option_saveFolder_success) fun updateValue(path: String?) { if (settings.saveFolder != null && settings.saveFolder != RECORDER_MEDIA_SELECTED_VALUE) { runCatching { @@ -91,6 +95,10 @@ fun SaveFolderTile( dataStore.updateData { it.setSaveFolder(path) } + snackbarHostState.showSnackbar( + message = successMessage, + duration = SnackbarDuration.Short, + ) } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index 217641de6..9729051b1 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -157,7 +157,10 @@ fun SettingsScreen( verticalArrangement = Arrangement.spacedBy(32.dp), ) { Column { - SaveFolderTile(settings = settings) + SaveFolderTile( + settings = settings, + snackbarHostState = snackbarHostState, + ) DividerTitle( title = stringResource(R.string.ui_settings_sections_audio_title), diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e6dd15063..1ab0dd2ff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -172,4 +172,5 @@ Select a custom location Use the DCIM folder DCIM Folder + Batches Folder has been changed successfully \ No newline at end of file From f4334bf26b9d9696b442c9f745efa718fd3c8ab5 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 22:10:14 +0100 Subject: [PATCH 126/176] fix: Fix mainly Audio recording and some bugfixes for video recording --- .../alibi/helpers/AudioBatchesFolder.kt | 73 +++++++++++++++++-- .../myzel394/alibi/helpers/BatchesFolder.kt | 8 ++ .../alibi/helpers/VideoBatchesFolder.kt | 42 ++++++++--- .../alibi/services/AudioRecorderService.kt | 22 +++++- .../alibi/services/VideoRecorderService.kt | 46 ++++-------- .../java/app/myzel394/alibi/ui/Constants.kt | 3 +- .../alibi/ui/models/AudioRecorderModel.kt | 11 ++- 7 files changed, 146 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt index 14ef3eb1d..b709f4f0f 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -2,12 +2,16 @@ package app.myzel394.alibi.helpers import android.content.Context import android.net.Uri +import android.os.Build +import android.os.Environment import android.os.ParcelFileDescriptor import android.provider.MediaStore -import androidx.core.net.toFile +import androidx.annotation.RequiresApi import androidx.documentfile.provider.DocumentFile -import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles -import app.myzel394.alibi.helpers.VideoBatchesFolder.Companion.MEDIA_SUBFOLDER +import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateAudioFiles +import app.myzel394.alibi.ui.MEDIA_SUBFOLDER_NAME +import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE +import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import com.arthenica.ffmpegkit.FFmpegKitConfig import java.io.File import java.io.FileDescriptor @@ -24,15 +28,17 @@ class AudioBatchesFolder( customFolder, subfolderName, ) { - override val concatenationFunction = ::concatenateVideoFiles + override val concatenationFunction = ::concatenateAudioFiles override val ffmpegParameters = FFMPEG_PARAMETERS override val scopedMediaContentUri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI override val legacyMediaFolder = File( - scopedMediaContentUri.toFile(), - MEDIA_SUBFOLDER + "/" + subfolderName + // TODO: Add support for `DIRECTORY_RECORDINGS` + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), + MEDIA_RECORDINGS_SUBFOLDER, ) private var customFileFileDescriptor: ParcelFileDescriptor? = null + private var mediaFileFileDescriptor: ParcelFileDescriptor? = null override fun getOutputFileForFFmpeg( date: LocalDateTime, @@ -54,7 +60,28 @@ class AudioBatchesFolder( } BatchType.MEDIA -> { - return "" + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val mediaUri = getOrCreateMediaFile( + name = getName(date, extension), + mimeType = "audio/$extension", + relativePath = Environment.DIRECTORY_DCIM + "/" + MEDIA_SUBFOLDER_NAME, + ) + + return FFmpegKitConfig.getSafParameterForWrite( + context, + mediaUri + )!! + } else { + val path = arrayOf( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), + MEDIA_SUBFOLDER_NAME, + getName(date, extension) + ).joinToString("/") + return File(path) + .apply { + createNewFile() + }.absolutePath + } } } } @@ -63,6 +90,9 @@ class AudioBatchesFolder( runCatching { customFileFileDescriptor?.close() } + runCatching { + mediaFileFileDescriptor?.close() + } } fun asCustomGetFileDescriptor( @@ -81,17 +111,44 @@ class AudioBatchesFolder( return customFileFileDescriptor!!.fileDescriptor } + @RequiresApi(Build.VERSION_CODES.Q) + fun asMediaGetScopedStorageFileDescriptor( + name: String, + mimeType: String + ): FileDescriptor { + runCatching { + mediaFileFileDescriptor?.close() + } + + val mediaUri = getOrCreateMediaFile( + name = name, + mimeType = mimeType, + relativePath = SCOPED_STORAGE_RELATIVE_PATH, + ) + + mediaFileFileDescriptor = context.contentResolver.openFileDescriptor(mediaUri, "w")!! + + return mediaFileFileDescriptor!!.fileDescriptor + } + companion object { fun viaInternalFolder(context: Context) = AudioBatchesFolder(context, BatchType.INTERNAL) fun viaCustomFolder(context: Context, folder: DocumentFile) = AudioBatchesFolder(context, BatchType.CUSTOM, folder) + fun viaMediaFolder(context: Context) = AudioBatchesFolder(context, BatchType.MEDIA) + fun importFromFolder(folder: String, context: Context) = when (folder) { - "_'internal" -> viaInternalFolder(context) + RECORDER_INTERNAL_SELECTED_VALUE -> viaInternalFolder(context) + RECORDER_MEDIA_SELECTED_VALUE -> viaMediaFolder(context) else -> viaCustomFolder(context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!!) } + val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER_NAME + "/audio_recordings" + val SCOPED_STORAGE_RELATIVE_PATH = + Environment.DIRECTORY_DCIM + "/" + MEDIA_RECORDINGS_SUBFOLDER + // Parameters to be passed in descending order // Those parameters first try to concatenate without re-encoding // if that fails, it'll try several fallback methods diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index e37d9c8c2..77930458b 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -170,6 +170,14 @@ abstract class BatchesFolder( return File(getInternalFolder(), getName(date, extension)) } + fun asMediaGetLegacyFile(name: String): File = File( + legacyMediaFolder, + name + ).apply { + createNewFile() + } + + fun checkIfOutputAlreadyExists( date: LocalDateTime, extension: String diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index 64c6f0b75..2ed06d0a5 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -1,21 +1,20 @@ package app.myzel394.alibi.helpers +import android.content.ContentValues import android.content.Context import android.net.Uri import android.os.Build import android.os.Environment import android.os.ParcelFileDescriptor import android.provider.MediaStore -import androidx.core.net.toFile import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles +import app.myzel394.alibi.ui.MEDIA_SUBFOLDER_NAME import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import com.arthenica.ffmpegkit.FFmpegKitConfig import java.io.File -import java.nio.file.Paths import java.time.LocalDateTime -import kotlin.io.path.Path class VideoBatchesFolder( override val context: Context, @@ -28,6 +27,7 @@ class VideoBatchesFolder( customFolder, subfolderName, ) { + // TODO: Sort batches! override val concatenationFunction = ::concatenateVideoFiles override val ffmpegParameters = FFMPEG_PARAMETERS override val scopedMediaContentUri: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI @@ -59,7 +59,7 @@ class VideoBatchesFolder( val mediaUri = getOrCreateMediaFile( name = getName(date, extension), mimeType = "video/$extension", - relativePath = Environment.DIRECTORY_DCIM + MEDIA_SUBFOLDER, + relativePath = Environment.DIRECTORY_DCIM + "/" + MEDIA_SUBFOLDER_NAME, ) return FFmpegKitConfig.getSafParameterForWrite( @@ -67,12 +67,12 @@ class VideoBatchesFolder( mediaUri )!! } else { - return Paths.get( - MediaStore.Video.Media.EXTERNAL_CONTENT_URI.path, - Environment.DIRECTORY_DCIM, - MEDIA_SUBFOLDER, + val path = arrayOf( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), + MEDIA_SUBFOLDER_NAME, getName(date, extension) - ).toFile() + ).joinToString("/") + return File(path) .apply { createNewFile() }.absolutePath @@ -109,6 +109,24 @@ class VideoBatchesFolder( } } + fun asMediaGetScopedStorageContentValues(name: String) = ContentValues().apply { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + put( + MediaStore.Video.Media.IS_PENDING, + 1 + ) + put( + MediaStore.Video.Media.RELATIVE_PATH, + SCOPED_STORAGE_RELATIVE_PATH, + ) + } + + put( + MediaStore.Video.Media.DISPLAY_NAME, + name + ) + } + companion object { fun viaInternalFolder(context: Context) = VideoBatchesFolder(context, BatchType.INTERNAL) @@ -126,9 +144,9 @@ class VideoBatchesFolder( ) } - val MEDIA_SUBFOLDER = "/alibi" - val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER + "/video_recordings" - val SCOPED_STORAGE_RELATIVE_PATH = Environment.DIRECTORY_DCIM + MEDIA_RECORDINGS_SUBFOLDER + val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER_NAME + "/video_recordings" + val SCOPED_STORAGE_RELATIVE_PATH = + Environment.DIRECTORY_DCIM + "/" + MEDIA_RECORDINGS_SUBFOLDER // Parameters to be passed in descending order // Those parameters first try to concatenate without re-encoding diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index a203697c8..48b16c2f5 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -16,6 +16,7 @@ import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.BatchesFolder +import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import app.myzel394.alibi.ui.utils.MicrophoneInfo import java.lang.IllegalStateException @@ -160,6 +161,9 @@ class AudioRecorderService : } } + private fun getNameForMediaFile() = + "${batchesFolder.mediaPrefix}$counter.${settings.videoRecorderSettings.fileExtension}" + // ==== Actual recording related ==== private fun createRecorder(): MediaRecorder { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { @@ -196,8 +200,22 @@ class AudioRecorderService : ) } - // TODO: Add media - else -> {} + BatchesFolder.BatchType.MEDIA -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + setOutputFile( + batchesFolder.asMediaGetScopedStorageFileDescriptor( + getNameForMediaFile(), + "audio/${audioSettings.fileExtension}" + ) + ) + } else { + val name = getNameForMediaFile() + val file = batchesFolder.asMediaGetLegacyFile(name) + + // TODO: Ask permission on settings screen + setOutputFile(file.absolutePath) + } + } } setOutputFormat(audioSettings.getOutputFormat()) diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index faaef9141..0ca1659c6 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -1,11 +1,9 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint -import android.content.ContentValues import android.content.Intent import android.content.pm.ServiceInfo import android.os.Build -import android.provider.MediaStore import android.util.Range import androidx.camera.core.Camera import androidx.camera.core.CameraSelector @@ -35,7 +33,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull -import java.io.File import kotlin.properties.Delegates class VideoRecorderService : @@ -246,45 +243,30 @@ class VideoRecorderService : ) } else if (batchesFolder.type == BatchesFolder.BatchType.MEDIA) { if (SUPPORTS_SCOPED_STORAGE) { + val name = getNameForMediaFile() + it.prepareRecording( this, - MediaStoreOutputOptions.Builder( - contentResolver, - batchesFolder.scopedMediaContentUri, - ).setContentValues( - ContentValues().apply { - val name = getNameForMediaFile() - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - put( - MediaStore.Video.Media.IS_PENDING, - 1 - ) - put( - MediaStore.Video.Media.RELATIVE_PATH, - VideoBatchesFolder.SCOPED_STORAGE_RELATIVE_PATH, - ) - } - - put( - MediaStore.Video.Media.DISPLAY_NAME, + MediaStoreOutputOptions + .Builder( + contentResolver, + batchesFolder.scopedMediaContentUri, + ) + .setContentValues( + batchesFolder.asMediaGetScopedStorageContentValues( name ) - } - ).build() + ) + .build() ) } else { val name = getNameForMediaFile() - val file = File( - batchesFolder.legacyMediaFolder, - name - ).apply { - createNewFile() - } it.prepareRecording( this, - FileOutputOptions.Builder(file).build() + FileOutputOptions + .Builder(batchesFolder.asMediaGetLegacyFile(name)) + .build() ) } } else { diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index b8c981632..462cba72e 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -9,7 +9,8 @@ val BIG_PRIMARY_BUTTON_SIZE = 64.dp val SHEET_BOTTOM_OFFSET = 56.dp val MAX_AMPLITUDE = 20000 val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -val RECORDER_SUBFOLDER_NAME = ".recordings" + +val MEDIA_SUBFOLDER_NAME = "alibi" // TODO: Fix! val SUPPORTS_SCOPED_STORAGE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index f4fdf10bb..582dc77c8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -10,7 +10,9 @@ import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.AudioBatchesFolder +import app.myzel394.alibi.helpers.VideoBatchesFolder import app.myzel394.alibi.services.AudioRecorderService +import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import app.myzel394.alibi.ui.utils.MicrophoneInfo class AudioRecorderModel : @@ -63,16 +65,17 @@ class AudioRecorderModel : } override fun startRecording(context: Context, settings: AppSettings) { - batchesFolder = if (settings.saveFolder == null) - AudioBatchesFolder.viaInternalFolder(context) - else - AudioBatchesFolder.viaCustomFolder( + batchesFolder = when (settings.saveFolder) { + null -> AudioBatchesFolder.viaInternalFolder(context) + RECORDER_MEDIA_SELECTED_VALUE -> AudioBatchesFolder.viaMediaFolder(context) + else -> AudioBatchesFolder.viaCustomFolder( context, DocumentFile.fromTreeUri( context, Uri.parse(settings.saveFolder) )!! ) + } super.startRecording(context, settings) } From 5e3e2a2e22f222e071ff82cad3355f67350ded99 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 22:40:20 +0100 Subject: [PATCH 127/176] feat: Add permission required dialog to SaveFolder; some refactoring --- .../SettingsScreen/Tiles/SaveFolderTile.kt | 366 +++++++++++------- app/src/main/res/values/strings.xml | 3 + 2 files changed, 239 insertions(+), 130 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index f8d82981a..9fe052b66 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -1,5 +1,6 @@ package app.myzel394.alibi.ui.components.SettingsScreen.Tiles +import android.Manifest import android.content.Intent import android.net.Uri import androidx.compose.foundation.clickable @@ -28,6 +29,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.SheetState import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text @@ -41,7 +43,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -57,9 +58,10 @@ import app.myzel394.alibi.ui.SHEET_BOTTOM_OFFSET import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import app.myzel394.alibi.ui.components.atoms.MessageBox import app.myzel394.alibi.ui.components.atoms.MessageType +import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.components.atoms.SettingsTile +import app.myzel394.alibi.ui.utils.PermissionHelper import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog -import com.maxkeppeker.sheets.core.models.base.SelectionButton import kotlinx.coroutines.launch import java.net.URLDecoder @@ -102,67 +104,6 @@ fun SaveFolderTile( } } - val selectFolder = rememberFolderSelectorDialog { folder -> - if (folder == null) { - return@rememberFolderSelectorDialog - } - - updateValue(folder.toString()) - } - - var showWarning by remember { mutableStateOf(false) } - - if (showWarning) { - val title = stringResource(R.string.ui_settings_option_saveFolder_warning_title) - val text = stringResource(R.string.ui_settings_option_saveFolder_warning_text) - - AlertDialog( - icon = { - Icon( - Icons.Default.Warning, - contentDescription = null, - ) - }, - onDismissRequest = { - showWarning = false - }, - title = { - Text(text = title) - }, - text = { - Text(text = text) - }, - confirmButton = { - Button( - onClick = { - showWarning = false - selectFolder() - }, - ) { - Text( - text = stringResource(R.string.ui_settings_option_saveFolder_warning_action_confirm), - ) - } - }, - dismissButton = { - Button( - onClick = { - showWarning = false - }, - colors = ButtonDefaults.textButtonColors(), - ) { - Icon( - Icons.Default.Cancel, - contentDescription = null, - modifier = Modifier.size(ButtonDefaults.IconSize), - ) - Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) - Text(stringResource(R.string.dialog_close_cancel_label)) - } - } - ) - } - var selectionVisible by remember { mutableStateOf(false) } val selectionSheetState = rememberModalBottomSheetState(true) @@ -174,75 +115,16 @@ fun SaveFolderTile( } if (selectionVisible) { - ModalBottomSheet( + SelectionSheet( sheetState = selectionSheetState, - onDismissRequest = ::hideSheet, - ) { - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .padding(bottom = SHEET_BOTTOM_OFFSET), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(24.dp), - ) { - Text( - stringResource(R.string.ui_settings_option_saveFolder_title), - style = MaterialTheme.typography.headlineMedium, - textAlign = TextAlign.Center, - ) - - SelectionButton( - label = stringResource(R.string.ui_settings_option_saveFolder_action_default_label), - icon = Icons.Default.Lock, - onClick = { - hideSheet() - updateValue(null) - }, - ) - - Divider() - - SelectionButton( - label = stringResource(R.string.ui_settings_option_saveFolder_action_dcim_label), - icon = Icons.Default.PermMedia, - onClick = { - hideSheet() - updateValue(RECORDER_MEDIA_SELECTED_VALUE) - }, - ) - - Divider() - - Column { - SelectionButton( - label = stringResource(R.string.ui_settings_option_saveFolder_action_custom_label), - icon = Icons.Default.Folder, - onClick = { - hideSheet() - showWarning = true - }, - ) - if (!SUPPORTS_SCOPED_STORAGE) { - Column( - modifier = Modifier.padding(horizontal = 32.dp, vertical = 12.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - MessageBox( - type = MessageType.INFO, - message = stringResource(R.string.ui_settings_option_saveFolder_videoUnsupported), - ) - Text( - stringResource(R.string.ui_minApiRequired, 8, 26), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurface, - ) - } - } - } - } - } + updateValue = { path -> + updateValue(path) + hideSheet() + }, + onDismiss = ::hideSheet, + ) } + SettingsTile( title = stringResource(R.string.ui_settings_option_saveFolder_title), description = stringResource(R.string.ui_settings_option_saveFolder_explanation), @@ -294,6 +176,126 @@ fun SaveFolderTile( ) } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SelectionSheet( + sheetState: SheetState, + updateValue: (String?) -> Unit, + onDismiss: () -> Unit, +) { + val context = LocalContext.current + + var showCustomFolderWarning by remember { mutableStateOf(false) } + + if (showCustomFolderWarning) { + val selectFolder = rememberFolderSelectorDialog { folder -> + if (folder == null) { + return@rememberFolderSelectorDialog + } + + updateValue(folder.toString()) + } + + CustomFolderWarningDialog( + onDismiss = { + showCustomFolderWarning = false + }, + onConfirm = { + showCustomFolderWarning = false + selectFolder() + }, + ) + } + + var showExternalPermissionRequired by remember { mutableStateOf(false) } + + if (showExternalPermissionRequired) { + ExternalPermissionRequiredDialog( + onDismiss = { + showExternalPermissionRequired = false + }, + onGranted = { + showExternalPermissionRequired = false + updateValue(RECORDER_MEDIA_SELECTED_VALUE) + }, + ) + } + + ModalBottomSheet( + sheetState = sheetState, + onDismissRequest = onDismiss, + ) { + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(bottom = SHEET_BOTTOM_OFFSET), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(24.dp), + ) { + Text( + stringResource(R.string.ui_settings_option_saveFolder_title), + style = MaterialTheme.typography.headlineMedium, + textAlign = TextAlign.Center, + ) + + SelectionButton( + label = stringResource(R.string.ui_settings_option_saveFolder_action_default_label), + icon = Icons.Default.Lock, + onClick = { + updateValue(null) + }, + ) + + Divider() + + SelectionButton( + label = stringResource(R.string.ui_settings_option_saveFolder_action_dcim_label), + icon = Icons.Default.PermMedia, + onClick = { + if (PermissionHelper.hasGranted( + context, + Manifest.permission.READ_EXTERNAL_STORAGE + ) + ) { + updateValue(RECORDER_MEDIA_SELECTED_VALUE) + } else { + showExternalPermissionRequired = true + } + }, + ) + + Divider() + + Column { + SelectionButton( + label = stringResource(R.string.ui_settings_option_saveFolder_action_custom_label), + icon = Icons.Default.Folder, + onClick = { + showCustomFolderWarning = true + }, + ) + if (!SUPPORTS_SCOPED_STORAGE) { + Column( + modifier = Modifier.padding(horizontal = 32.dp, vertical = 12.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + MessageBox( + type = MessageType.INFO, + message = stringResource(R.string.ui_settings_option_saveFolder_videoUnsupported), + ) + Text( + stringResource(R.string.ui_minApiRequired, 8, 26), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurface, + ) + } + } + } + } + } +} + @Composable fun SelectionButton( label: String, @@ -325,6 +327,110 @@ fun SelectionButton( } } +@Composable +fun CustomFolderWarningDialog( + onDismiss: () -> Unit, + onConfirm: () -> Unit, +) { + val title = stringResource(R.string.ui_settings_option_saveFolder_warning_title) + val text = stringResource(R.string.ui_settings_option_saveFolder_warning_text) + + AlertDialog( + icon = { + Icon( + Icons.Default.Warning, + contentDescription = null, + ) + }, + onDismissRequest = onDismiss, + title = { + Text(text = title) + }, + text = { + Text(text = text) + }, + confirmButton = { + Button( + onClick = onConfirm, + ) { + Text( + text = stringResource(R.string.ui_settings_option_saveFolder_warning_action_confirm), + ) + } + }, + dismissButton = { + Button( + onClick = onDismiss, + colors = ButtonDefaults.textButtonColors(), + ) { + Icon( + Icons.Default.Cancel, + contentDescription = null, + modifier = Modifier.size(ButtonDefaults.IconSize), + ) + Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) + Text(stringResource(R.string.dialog_close_cancel_label)) + } + } + ) +} + +@Composable +fun ExternalPermissionRequiredDialog( + onDismiss: () -> Unit, + onGranted: () -> Unit, +) { + PermissionRequester( + icon = Icons.Default.PermMedia, + permission = Manifest.permission.READ_EXTERNAL_STORAGE, + onPermissionAvailable = onGranted, + ) { trigger -> + AlertDialog( + icon = { + Icon( + Icons.Default.Warning, + contentDescription = null, + ) + }, + onDismissRequest = onDismiss, + title = { + Text( + stringResource(R.string.ui_settings_option_saveFolder_externalPermissionRequired_title), + ) + }, + text = { + Text( + stringResource(R.string.ui_settings_option_saveFolder_externalPermissionRequired_text), + ) + }, + confirmButton = { + Button( + onClick = trigger, + ) { + Text( + stringResource(R.string.ui_settings_option_saveFolder_externalPermissionRequired_action_confirm), + ) + } + }, + dismissButton = { + Button( + onClick = onDismiss, + colors = ButtonDefaults.textButtonColors(), + ) { + Icon( + Icons.Default.Cancel, + contentDescription = null, + modifier = Modifier.size(ButtonDefaults.IconSize), + ) + Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) + Text(stringResource(R.string.dialog_close_cancel_label)) + } + } + ) + } +} + + fun splitPath(path: String): List { return try { URLDecoder diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ab0dd2ff..0e58126e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -173,4 +173,7 @@ Use the DCIM folder DCIM Folder Batches Folder has been changed successfully + Permission required + To access the DCIM folder, you need to grant Alibi the permission to access external storage. Alibi will only use this permission to write your recordings to the DCIM folder. + Grant permission \ No newline at end of file From e8337f2fc2cd180df5ce8ec98a573c382a08dcd5 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 22:45:27 +0100 Subject: [PATCH 128/176] fix: Fix message info --- .../SettingsScreen/Tiles/EnableAppLockTile.kt | 20 ++++++--- .../alibi/ui/components/atoms/MessageBox.kt | 43 +++++++++++++------ 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/EnableAppLockTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/EnableAppLockTile.kt index aeddb5e1f..babbd08e5 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/EnableAppLockTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/EnableAppLockTile.kt @@ -1,5 +1,7 @@ package app.myzel394.alibi.ui.components.SettingsScreen.Tiles +import android.os.Message +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding import androidx.compose.foundation.magnifier import androidx.compose.material.icons.Icons @@ -20,7 +22,10 @@ import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppLockSettings import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.helpers.AppLockHelper +import app.myzel394.alibi.ui.components.atoms.MessageBox +import app.myzel394.alibi.ui.components.atoms.MessageType import app.myzel394.alibi.ui.components.atoms.SettingsTile +import app.myzel394.alibi.ui.components.atoms.VisualDensity import kotlinx.coroutines.launch @Composable @@ -43,12 +48,15 @@ fun EnableAppLockTile( description = stringResource(R.string.ui_settings_option_enableAppLock_description), tertiaryLine = { if (appLockSupport === AppLockHelper.SupportType.NONE_ENROLLED) { - Text( - stringResource(R.string.ui_settings_option_enableAppLock_enrollmentRequired), - color = Color.Yellow, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(top = 4.dp) - ) + Box( + modifier = Modifier.padding(top = 8.dp) + ) { + MessageBox( + type = MessageType.WARNING, + message = stringResource(R.string.ui_settings_option_enableAppLock_enrollmentRequired), + density = VisualDensity.COMPACT, + ) + } } }, leading = { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt index 1c594ff4b..609d85525 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt @@ -3,6 +3,7 @@ package app.myzel394.alibi.ui.components.atoms import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check @@ -27,6 +28,7 @@ fun MessageBox( type: MessageType, message: String, title: String? = null, + density: VisualDensity = VisualDensity.COMFORTABLE, ) { val isDark = rememberIsInDarkMode() val containerColor = when (type) { @@ -51,33 +53,43 @@ fun MessageBox( modifier = Modifier .clip(MaterialTheme.shapes.medium) .background(backgroundColor) - .padding(horizontal = 8.dp, vertical = 16.dp) + .let { + if (density == VisualDensity.COMFORTABLE) { + it.padding(horizontal = 8.dp, vertical = 16.dp) + } else { + it.padding(8.dp) + } + } .then(modifier) ) { - Icon( - imageVector = when (type) { - MessageType.ERROR -> Icons.Default.Error - MessageType.INFO -> Icons.Default.Info - MessageType.SURFACE -> Icons.Default.Info - MessageType.SUCCESS -> Icons.Default.Check - MessageType.WARNING -> Icons.Default.Warning - }, - contentDescription = null, - tint = textColor, - modifier = Modifier.padding(16.dp) - ) + if (density == VisualDensity.COMFORTABLE) { + Icon( + imageVector = when (type) { + MessageType.ERROR -> Icons.Default.Error + MessageType.INFO -> Icons.Default.Info + MessageType.SURFACE -> Icons.Default.Info + MessageType.SUCCESS -> Icons.Default.Check + MessageType.WARNING -> Icons.Default.Warning + }, + contentDescription = null, + tint = textColor, + modifier = Modifier.padding(16.dp) + ) + } Column { if (title != null) { Text( text = title, style = MaterialTheme.typography.bodyLarge, color = textColor, + modifier = Modifier.fillMaxWidth() ) } Text( text = message, style = MaterialTheme.typography.bodyMedium, color = textColor, + modifier = Modifier.fillMaxWidth() ) } } @@ -90,4 +102,9 @@ enum class MessageType { SURFACE, SUCCESS, WARNING, +} + +enum class VisualDensity { + COMPACT, + COMFORTABLE, } \ No newline at end of file From b581505c979b92ae5eb38fcd2b4452d366113644 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 22:49:16 +0100 Subject: [PATCH 129/176] fix: Improve MessageBox colors --- .../alibi/ui/components/atoms/MessageBox.kt | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt index 609d85525..d67ad647a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt @@ -31,22 +31,36 @@ fun MessageBox( density: VisualDensity = VisualDensity.COMFORTABLE, ) { val isDark = rememberIsInDarkMode() - val containerColor = when (type) { + val containerColorDarkMode = when (type) { MessageType.ERROR -> MaterialTheme.colorScheme.errorContainer MessageType.INFO -> MaterialTheme.colorScheme.tertiaryContainer MessageType.SUCCESS -> Color.Green.copy(alpha = 0.3f) MessageType.WARNING -> Color.Yellow.copy(alpha = 0.3f) MessageType.SURFACE -> MaterialTheme.colorScheme.surfaceVariant } - val onContainerColor = when (type) { - MessageType.ERROR -> MaterialTheme.colorScheme.onError + val onContainerColorDarkMode = when (type) { + MessageType.ERROR -> MaterialTheme.colorScheme.onErrorContainer MessageType.INFO -> MaterialTheme.colorScheme.onTertiaryContainer MessageType.SUCCESS -> Color.Green MessageType.WARNING -> Color.Yellow MessageType.SURFACE -> MaterialTheme.colorScheme.onSurfaceVariant } - val textColor = if (isDark) onContainerColor else MaterialTheme.colorScheme.onSurface - val backgroundColor = if (isDark) containerColor else onContainerColor + val containerColorLightMode = when (type) { + MessageType.ERROR -> MaterialTheme.colorScheme.errorContainer + MessageType.INFO -> MaterialTheme.colorScheme.tertiaryContainer + MessageType.SUCCESS -> Color.Green + MessageType.WARNING -> Color.Yellow + MessageType.SURFACE -> MaterialTheme.colorScheme.onSurfaceVariant + } + val onContainerColorLightMode = when (type) { + MessageType.ERROR -> MaterialTheme.colorScheme.onErrorContainer + MessageType.INFO -> MaterialTheme.colorScheme.onTertiaryContainer + MessageType.SUCCESS -> MaterialTheme.colorScheme.onSurface + MessageType.WARNING -> MaterialTheme.colorScheme.onSurface + MessageType.SURFACE -> MaterialTheme.colorScheme.surfaceVariant + } + val textColor = if (isDark) onContainerColorDarkMode else onContainerColorLightMode + val backgroundColor = if (isDark) containerColorDarkMode else containerColorLightMode Row( verticalAlignment = Alignment.CenterVertically, From 666f0bee705f93b0f3a9a65d4bde3c2f14ea0763 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 22:54:47 +0100 Subject: [PATCH 130/176] feat: Check if external storage permission is granted on old Android version if media is selected --- .../myzel394/alibi/helpers/BatchesFolder.kt | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 77930458b..8e10bb2e6 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -1,5 +1,6 @@ package app.myzel394.alibi.helpers +import android.Manifest import android.content.ContentUris import android.content.ContentValues import app.myzel394.alibi.ui.MEDIA_RECORDINGS_PREFIX @@ -21,6 +22,7 @@ import androidx.core.net.toFile import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE +import app.myzel394.alibi.ui.utils.PermissionHelper import kotlinx.coroutines.CompletableDeferred import kotlin.reflect.KFunction3 @@ -286,6 +288,7 @@ abstract class BatchesFolder( BatchType.MEDIA -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // TODO: Also delete pending recordings + // --> Doesn't seem to be possible :/ context.contentResolver.delete( scopedMediaContentUri, "${MediaStore.MediaColumns.DISPLAY_NAME} LIKE '$mediaPrefix%'", @@ -382,8 +385,20 @@ abstract class BatchesFolder( return when (type) { BatchType.INTERNAL -> true BatchType.CUSTOM -> getCustomDefinedFolder().canWrite() && getCustomDefinedFolder().canRead() - // TODO: Add support for < Android 10 - BatchType.MEDIA -> true + BatchType.MEDIA -> { + if (SUPPORTS_SCOPED_STORAGE) { + return true + } + + return PermissionHelper.hasGranted( + context, + Manifest.permission.READ_EXTERNAL_STORAGE + ) && + PermissionHelper.hasGranted( + context, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + } } } From 2c0924569789acd25c26997ce5f937b201759e1a Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 22:55:43 +0100 Subject: [PATCH 131/176] refactor: Rename onCustomOutputFolderNotAccessible -> onBatchesFolderNotAccessible --- .../app/myzel394/alibi/services/AudioRecorderService.kt | 1 - .../app/myzel394/alibi/services/IntervalRecorderService.kt | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index 48b16c2f5..b82b4bdf4 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -212,7 +212,6 @@ class AudioRecorderService : val name = getNameForMediaFile() val file = batchesFolder.asMediaGetLegacyFile(name) - // TODO: Ask permission on settings screen setOutputFile(file.absolutePath) } } diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index 97a512f7d..e023d7418 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -17,7 +17,7 @@ abstract class IntervalRecorderService : abstract var batchesFolder: B - var onCustomOutputFolderNotAccessible: () -> Unit = {} + var onBatchesFolderNotAccessible: () -> Unit = {} abstract fun getRecordingInformation(): I @@ -42,8 +42,8 @@ abstract class IntervalRecorderService : super.start() batchesFolder.initFolders() if (!batchesFolder.checkIfFolderIsAccessible()) { - onCustomOutputFolderNotAccessible() - return + // TODO: Add handler + onBatchesFolderNotAccessible() } createTimer() From da34df6b87554f3439fc6d8ae813f1fd7f6145c4 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 22:58:37 +0100 Subject: [PATCH 132/176] refactor: Use constant SHEET_BOTTOM_OFFSET everywhere --- app/src/main/java/app/myzel394/alibi/ui/Constants.kt | 1 - .../RecorderScreen/molecules/MicrophoneSelection.kt | 3 ++- .../RecorderScreen/molecules/QuickMaxDurationSelector.kt | 4 +++- .../RecorderScreen/molecules/VideoRecorderPreparationSheet.kt | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index 462cba72e..5c4615eb3 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -5,7 +5,6 @@ import androidx.compose.ui.unit.dp val BIG_PRIMARY_BUTTON_SIZE = 64.dp -// TODO: Add everywhere val SHEET_BOTTOM_OFFSET = 56.dp val MAX_AMPLITUDE = 20000 val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt index 2c45de87f..1ec491520 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.ui.SHEET_BOTTOM_OFFSET import app.myzel394.alibi.ui.components.RecorderScreen.atoms.MicrophoneSelectionButton import app.myzel394.alibi.ui.components.RecorderScreen.atoms.MicrophoneTypeInfo import app.myzel394.alibi.ui.components.atoms.MessageBox @@ -91,7 +92,7 @@ fun MicrophoneSelection( Column( modifier = Modifier .padding(horizontal = 16.dp) - .padding(bottom = 24.dp), + .padding(bottom = SHEET_BOTTOM_OFFSET), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(48.dp), ) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt index 4d7695e56..bc3ddc229 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt @@ -27,6 +27,7 @@ import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AudioRecorderSettings.Companion.EXAMPLE_MAX_DURATIONS +import app.myzel394.alibi.ui.SHEET_BOTTOM_OFFSET import app.myzel394.alibi.ui.utils.formatDuration import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -52,7 +53,8 @@ fun QuickMaxDurationSelector( Box( modifier = Modifier .widthIn(max = 400.dp) - .padding(16.dp), + .padding(horizontal = 16.dp) + .padding(bottom = SHEET_BOTTOM_OFFSET), ) { Text( stringResource(R.string.ui_recorder_action_changeMaxDuration_title), diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index 062a8abd1..5fb96e8ac 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -51,6 +51,7 @@ import androidx.compose.ui.window.Popup import androidx.lifecycle.viewmodel.compose.viewModel import app.myzel394.alibi.R import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE +import app.myzel394.alibi.ui.SHEET_BOTTOM_OFFSET import app.myzel394.alibi.ui.components.RecorderScreen.atoms.CameraPreview import app.myzel394.alibi.ui.components.atoms.GlobalSwitch import app.myzel394.alibi.ui.components.atoms.PermissionRequester @@ -113,7 +114,8 @@ fun VideoRecorderPreparationSheet( } else Column( modifier = Modifier - .padding(horizontal = 16.dp, vertical = 24.dp), + .padding(horizontal = 16.dp) + .padding(bottom = SHEET_BOTTOM_OFFSET, top = 24.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(30.dp), ) { From 3a542f3a4d8c86fda51336127c0f656acebd5a26 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 23:37:32 +0100 Subject: [PATCH 133/176] fix: Sort batches --- .../myzel394/alibi/helpers/BatchesFolder.kt | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 8e10bb2e6..6cd59b49c 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -18,7 +18,7 @@ import java.time.format.DateTimeFormatter import com.arthenica.ffmpegkit.FFmpegKitConfig import android.util.Log import androidx.annotation.RequiresApi -import androidx.core.net.toFile +import androidx.core.net.toUri import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE @@ -112,15 +112,19 @@ abstract class BatchesFolder( } fun getBatchesForFFmpeg(): List { + // TODO: There is probably a better way to do this iteratively, look at it if you have time return when (type) { BatchType.INTERNAL -> - (getInternalFolder() + ((getInternalFolder() .listFiles() ?.filter { it.nameWithoutExtension.toIntOrNull() != null } ?.toList() - ?: emptyList()) + ?: emptyList()) as List) + .sortedBy { + it.nameWithoutExtension.toInt() + } .map { it.absolutePath } BatchType.CUSTOM -> getCustomDefinedFolder() @@ -128,6 +132,9 @@ abstract class BatchesFolder( .filter { it.name?.substringBeforeLast(".")?.toIntOrNull() != null } + .sortedBy { + it.name!!.substringBeforeLast(".").toInt() + } .map { FFmpegKitConfig.getSafParameterForRead( context, @@ -136,24 +143,32 @@ abstract class BatchesFolder( } BatchType.MEDIA -> { - val filePaths = mutableListOf() + val fileUris = mutableListOf() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { queryMediaContent { _, _, uri, _ -> - filePaths.add( - FFmpegKitConfig.getSafParameterForRead( - context, - uri, - )!! - ) + fileUris.add(uri) } } else { legacyMediaFolder.listFiles()?.forEach { - filePaths.add(it.absolutePath) + fileUris.add(it.toUri()) } } - filePaths + fileUris + .sortedBy { + return@sortedBy it + .lastPathSegment!! + .substring(mediaPrefix.length) + .substringBeforeLast(".") + .toInt() + } + .map { uri -> + FFmpegKitConfig.getSafParameterForRead( + context, + uri, + )!! + } } } } @@ -367,6 +382,7 @@ abstract class BatchesFolder( null, ) } else { + // TODO: Fix "would you like to try saving" -> Save button legacyMediaFolder.listFiles()?.forEach { val fileCounter = it.nameWithoutExtension.substring(mediaPrefix.length).toIntOrNull() From 32d35c5cd75c366829126204d81c4d0aae46495c Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 31 Dec 2023 23:40:54 +0100 Subject: [PATCH 134/176] chore: cleanup --- .../java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt | 1 - .../app/myzel394/alibi/services/IntervalRecorderService.kt | 6 ------ 2 files changed, 7 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index 2ed06d0a5..7517c4536 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -27,7 +27,6 @@ class VideoBatchesFolder( customFolder, subfolderName, ) { - // TODO: Sort batches! override val concatenationFunction = ::concatenateVideoFiles override val ffmpegParameters = FFMPEG_PARAMETERS override val scopedMediaContentUri: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index e023d7418..a1daed699 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -79,10 +79,4 @@ abstract class IntervalRecorderService : batchesFolder.deleteOldRecordings(earliestCounter) } - - // TODO - abstract class Settings( - open val maxDuration: Long, - open val intervalDuration: Long, - ) } \ No newline at end of file From 47fc65aaf2dd6d452a1cd80baca71bc5a99e014d Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 1 Jan 2024 00:06:00 +0100 Subject: [PATCH 135/176] fix: typo; happy new years eve --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0e58126e5..080f76f46 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -67,7 +67,7 @@ Auto Recording paused Audio Recording has been paused - An error occured + An error occurred Alibi encountered an error during recording. Would you like to try saving the recording? Language Change From cf6c653ad3d6360ed68034d22060bbc6ae0b4545 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 1 Jan 2024 00:23:51 +0100 Subject: [PATCH 136/176] feat: Show error dialog when batches folder is inaccessible --- .../java/app/myzel394/alibi/ui/Constants.kt | 1 - .../atoms/BatchesInaccessibleDialog.kt | 42 +++++++++++++++++++ .../organisms/RecorderEventsHandler.kt | 33 +++++++++++++++ .../alibi/ui/models/BaseRecorderModel.kt | 8 +++- app/src/main/res/values/strings.xml | 1 + 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index 5c4615eb3..9b2aac856 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -11,7 +11,6 @@ val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q val MEDIA_SUBFOLDER_NAME = "alibi" -// TODO: Fix! val SUPPORTS_SCOPED_STORAGE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O val MEDIA_RECORDINGS_PREFIX = "alibi-recording-" val RECORDER_MEDIA_SELECTED_VALUE = "_'media" diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt new file mode 100644 index 000000000..e9cda6661 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt @@ -0,0 +1,42 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.atoms + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Error +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import app.myzel394.alibi.R + +@Composable +fun BatchesInaccessibleDialog( + onClose: () -> Unit, +) { + AlertDialog( + onDismissRequest = onClose, + icon = { + Icon( + Icons.Default.Error, + contentDescription = null, + ) + }, + title = { + Text(stringResource(R.string.ui_audioRecorder_error_recording_title)) + }, + text = { + Text(stringResource(R.string.ui_audioRecorder_error_batchesInaccessible_description)) + }, + confirmButton = { + Button( + onClick = onClose, + colors = ButtonDefaults.textButtonColors(), + ) { + Text(stringResource(R.string.dialog_close_neutral_label)) + } + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt index 20dd06c92..49d21b97c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt @@ -23,6 +23,7 @@ import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder import app.myzel394.alibi.services.IntervalRecorderService +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.BatchesInaccessibleDialog import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecorderErrorDialog import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecorderProcessingDialog import app.myzel394.alibi.ui.models.AudioRecorderModel @@ -51,6 +52,7 @@ fun RecorderEventsHandler( var isProcessing by remember { mutableStateOf(false) } var showRecorderError by remember { mutableStateOf(false) } + var showBatchesInaccessibleError by remember { mutableStateOf(false) } val saveAudioFile = rememberFileSaverDialog(settings.audioRecorderSettings.getMimeType()) { if (settings.deleteRecordingsImmediately) { @@ -235,6 +237,18 @@ fun RecorderEventsHandler( showRecorderError = true } } + audioRecorder.onBatchesFolderNotAccessible = { + scope.launch { + showBatchesInaccessibleError = true + + runCatching { + audioRecorder.stopRecording(context) + } + runCatching { + audioRecorder.destroyService(context) + } + } + } onDispose { audioRecorder.onRecordingSave = {} @@ -266,6 +280,18 @@ fun RecorderEventsHandler( showRecorderError = true } } + videoRecorder.onBatchesFolderNotAccessible = { + scope.launch { + showBatchesInaccessibleError = true + + runCatching { + videoRecorder.stopRecording(context) + } + runCatching { + videoRecorder.destroyService(context) + } + } + } onDispose { videoRecorder.onRecordingSave = {} @@ -284,4 +310,11 @@ fun RecorderEventsHandler( onSave = { }, ) + + if (showBatchesInaccessibleError) + BatchesInaccessibleDialog( + onClose = { + showBatchesInaccessibleError = false + }, + ) } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index 29c8da484..a7f93ba9b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -47,6 +47,7 @@ abstract class BaseRecorderModel Unit = {} var onError: () -> Unit = {} + var onBatchesFolderNotAccessible: () -> Unit = {} abstract var batchesFolder: B? private var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null @@ -70,11 +71,14 @@ abstract class BaseRecorderModelPermission required To access the DCIM folder, you need to grant Alibi the permission to access external storage. Alibi will only use this permission to write your recordings to the DCIM folder. Grant permission + Alibi couldn\'t access or write to the batches folder. Try to choose a different folder or use the internal storage instead. The recording has been aborted. \ No newline at end of file From d24dd4cf4b8cb7d865e06f89b4fc66a98a46d3c4 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 1 Jan 2024 00:31:55 +0100 Subject: [PATCH 137/176] fix: Fix function not being referenced anymore --- .../SettingsScreen/Tiles/SaveFolderTile.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 9fe052b66..7b8976500 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -187,15 +187,15 @@ fun SelectionSheet( var showCustomFolderWarning by remember { mutableStateOf(false) } - if (showCustomFolderWarning) { - val selectFolder = rememberFolderSelectorDialog { folder -> - if (folder == null) { - return@rememberFolderSelectorDialog - } - - updateValue(folder.toString()) + val selectFolder = rememberFolderSelectorDialog { folder -> + if (folder == null) { + return@rememberFolderSelectorDialog } + updateValue(folder.toString()) + } + + if (showCustomFolderWarning) { CustomFolderWarningDialog( onDismiss = { showCustomFolderWarning = false @@ -252,7 +252,9 @@ fun SelectionSheet( label = stringResource(R.string.ui_settings_option_saveFolder_action_dcim_label), icon = Icons.Default.PermMedia, onClick = { - if (PermissionHelper.hasGranted( + if ( + SUPPORTS_SCOPED_STORAGE || + PermissionHelper.hasGranted( context, Manifest.permission.READ_EXTERNAL_STORAGE ) From 29d4e5a86a543810fd78ca7a3b137ad12bfcd72c Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 1 Jan 2024 14:53:42 +0100 Subject: [PATCH 138/176] feat: Add doctor; Add check for file saver dialog --- .../java/app/myzel394/alibi/helpers/Doctor.kt | 14 +++++ .../alibi/services/AudioRecorderService.kt | 7 +-- .../alibi/services/IntervalRecorderService.kt | 3 +- .../alibi/services/VideoRecorderService.kt | 2 +- .../myzel394/alibi/ui/LockedAppHandlers.kt | 52 +++++++++++++++++++ .../Tiles/VideoRecorderQualityTile.kt | 1 + .../alibi/ui/models/VideoRecorderModel.kt | 1 + .../alibi/ui/screens/RecorderScreen.kt | 1 - app/src/main/res/values/strings.xml | 2 + 9 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt diff --git a/app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt b/app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt new file mode 100644 index 000000000..e61920aec --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt @@ -0,0 +1,14 @@ +package app.myzel394.alibi.helpers + +import android.content.Context +import android.content.Intent + +data class Doctor( + val context: Context +) { + fun checkIfFileSaverDialogIsAvailable(): Boolean { + val fileSaver = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + + return fileSaver.resolveActivity(context.packageManager) != null + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index b82b4bdf4..604ec9811 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -231,9 +231,10 @@ class AudioRecorderService : // ==== Microphone related ==== private fun resetRecorder() { runCatching { - recorder?.let { - it.stop() - it.release() + recorder?.apply { + stop() + reset() + release() } clearAudioDevice() batchesFolder.cleanup() diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index a1daed699..ed8f9c733 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -40,9 +40,10 @@ abstract class IntervalRecorderService : override fun start() { super.start() + batchesFolder.initFolders() + if (!batchesFolder.checkIfFolderIsAccessible()) { - // TODO: Add handler onBatchesFolderNotAccessible() } diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 0ca1659c6..d9f50a262 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -305,7 +305,7 @@ class VideoRecorderService : } class CameraControl( - private val camera: Camera, + val camera: Camera ) { fun enableTorch() { camera.cameraControl.enableTorch(true) diff --git a/app/src/main/java/app/myzel394/alibi/ui/LockedAppHandlers.kt b/app/src/main/java/app/myzel394/alibi/ui/LockedAppHandlers.kt index 5db781c79..663c92d52 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/LockedAppHandlers.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/LockedAppHandlers.kt @@ -1,12 +1,25 @@ package app.myzel394.alibi.ui import androidx.appcompat.app.AppCompatDelegate +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Error +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.helpers.Doctor // Handlers that can safely be run when the app is locked (biometric authentication required) @Composable @@ -29,4 +42,43 @@ fun LockedAppHandlers() { } } } + + var showFileSaverUnavailableDialog by remember { mutableStateOf(false) } + + LaunchedEffect(Unit) { + val doctor = Doctor(context) + + if (!doctor.checkIfFileSaverDialogIsAvailable()) { + showFileSaverUnavailableDialog = true + } + } + + if (showFileSaverUnavailableDialog) { + AlertDialog( + icon = { + Icon( + Icons.Default.Error, + contentDescription = null + ) + }, + onDismissRequest = { + showFileSaverUnavailableDialog = false + }, + title = { + Text(stringResource(R.string.ui_severeError_fileSaverUnavailable_title)) + }, + text = { + Text(stringResource(R.string.ui_severeError_fileSaverUnavailable_text)) + }, + confirmButton = { + TextButton( + onClick = { + showFileSaverUnavailableDialog = false + } + ) { + Text(text = stringResource(R.string.dialog_close_neutral_label)) + } + } + ) + } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderQualityTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderQualityTile.kt index f872d05de..baa8c8605 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderQualityTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/VideoRecorderQualityTile.kt @@ -2,6 +2,7 @@ package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import android.media.MediaRecorder import androidx.camera.video.Quality +import androidx.camera.video.Recorder import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.HighQuality import androidx.compose.material3.Button diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt index be787f8cd..55ffbe24a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt @@ -13,6 +13,7 @@ import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState +import app.myzel394.alibi.helpers.Doctor import app.myzel394.alibi.helpers.VideoBatchesFolder import app.myzel394.alibi.services.VideoRecorderService import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt index cb5d969a2..977b04d3d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/RecorderScreen.kt @@ -30,7 +30,6 @@ import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.ui.components.RecorderScreen.organisms.RecorderEventsHandler import app.myzel394.alibi.ui.components.RecorderScreen.organisms.VideoRecordingStatus -import app.myzel394.alibi.ui.effects.rememberSettings import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.models.VideoRecorderModel diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1372b6317..fce7f8ccf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -177,4 +177,6 @@ To access the DCIM folder, you need to grant Alibi the permission to access external storage. Alibi will only use this permission to write your recordings to the DCIM folder. Grant permission Alibi couldn\'t access or write to the batches folder. Try to choose a different folder or use the internal storage instead. The recording has been aborted. + File Manager app not found + Alibi couldn\'t find a file manager app on your phone. Please install a file manager app and try again. If this message still appears, you can try using a custom batches folder in the advanced settings section. Alibi may not fully work on your device. \ No newline at end of file From df820e59fd3e73404c3e518377cf1751ef9d0c91 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 1 Jan 2024 14:55:30 +0100 Subject: [PATCH 139/176] fix: Show content faster when no app lock is enabled --- app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt b/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt index 634f7a33a..feb013e70 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt @@ -56,12 +56,10 @@ fun AsLockedApp( .value ?: return // -1 = Unlocked, any other value = locked - var tries by remember { mutableIntStateOf(0) } - - LaunchedEffect(settings.isAppLockEnabled()) { - if (!settings.isAppLockEnabled()) { - tries = -1 - } + var tries by remember { + mutableIntStateOf( + if (settings.isAppLockEnabled()) 0 else -1 + ) } if (tries == -1) { From 7b9457fc58031193c792b263298a0c872ebb5795 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 1 Jan 2024 15:10:44 +0100 Subject: [PATCH 140/176] feat: Add onRecordingStart listener --- .../main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt | 1 + .../main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt | 1 + .../main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt | 1 + 3 files changed, 3 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index 582dc77c8..6037a1f07 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -56,6 +56,7 @@ class AudioRecorderModel : if (service.state == RecorderState.IDLE) { service.clearAllRecordings() service.startRecording() + onRecordingStart() } recorderState = service.state diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt index a7f93ba9b..452419b13 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/BaseRecorderModel.kt @@ -46,6 +46,7 @@ abstract class BaseRecorderModel Unit = {} + var onRecordingStart: () -> Unit = {} var onError: () -> Unit = {} var onBatchesFolderNotAccessible: () -> Unit = {} abstract var batchesFolder: B? diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt index 55ffbe24a..6af720691 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/VideoRecorderModel.kt @@ -67,6 +67,7 @@ class VideoRecorderModel : service.clearAllRecordings() service.startRecording() + onRecordingStart() } else { isStartingRecording = false } From cba150b72eb6b4964c5bebb5e029cfd7c4ac0f25 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 1 Jan 2024 15:10:57 +0100 Subject: [PATCH 141/176] feat: Hide current snackbar on recording start --- .../RecorderScreen/organisms/RecorderEventsHandler.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt index 49d21b97c..83dbe1dd8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt @@ -230,6 +230,9 @@ fun RecorderEventsHandler( } } } + audioRecorder.onRecordingStart = { + snackbarHostState.currentSnackbarData?.dismiss() + } audioRecorder.onError = { scope.launch { saveAsLastRecording(audioRecorder as RecorderModel) @@ -273,6 +276,9 @@ fun RecorderEventsHandler( } } } + videoRecorder.onRecordingStart = { + snackbarHostState.currentSnackbarData?.dismiss() + } videoRecorder.onError = { scope.launch { saveAsLastRecording(videoRecorder as RecorderModel) From 9bc6908bb9390a551e05d6bb6582e1b1e183e9f1 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 1 Jan 2024 15:23:49 +0100 Subject: [PATCH 142/176] fix: Improve Doctor's checkIfFileSaverDialogIsAvailable --- .../java/app/myzel394/alibi/helpers/Doctor.kt | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt b/app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt index e61920aec..e55f31313 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt @@ -2,13 +2,33 @@ package app.myzel394.alibi.helpers import android.content.Context import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build data class Doctor( val context: Context ) { fun checkIfFileSaverDialogIsAvailable(): Boolean { - val fileSaver = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + // Since API 30, we can't query other packages so easily anymore + // (see https://developer.android.com/training/package-visibility). + // For now, we assume the user has a file saver app installed. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + return true + } - return fileSaver.resolveActivity(context.packageManager) != null + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + if (intent.resolveActivity(context.packageManager) != null) { + return true + + } + + val results = + context.packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + + if (results.isNotEmpty()) { + return true; + } + + return false } } \ No newline at end of file From eeaeef07a8522d9b0e1e762c0a574d135166b412 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 1 Jan 2024 17:18:34 +0100 Subject: [PATCH 143/176] fix: Fix modal bottom sheet --- app/build.gradle | 2 +- .../main/java/app/myzel394/alibi/ui/Constants.kt | 2 +- .../main/java/app/myzel394/alibi/ui/Navigation.kt | 1 + .../molecules/QuickMaxDurationSelector.kt | 9 ++++----- .../molecules/VideoRecorderPreparationSheet.kt | 3 ++- .../myzel394/alibi/ui/screens/SettingsScreen.kt | 14 ++++++++------ 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 20252d103..489198d80 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -101,7 +101,7 @@ dependencies { implementation 'androidx.compose.ui:ui' implementation 'androidx.compose.ui:ui-graphics' implementation 'androidx.compose.ui:ui-tooling-preview' - implementation 'androidx.compose.material3:material3' + implementation 'androidx.compose.material3:material3:1.1.2' implementation "androidx.compose.material:material-icons-extended:1.5.4" implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.documentfile:documentfile:1.0.1' diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index 9b2aac856..32e871b96 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -5,7 +5,7 @@ import androidx.compose.ui.unit.dp val BIG_PRIMARY_BUTTON_SIZE = 64.dp -val SHEET_BOTTOM_OFFSET = 56.dp +val SHEET_BOTTOM_OFFSET = 24.dp val MAX_AMPLITUDE = 20000 val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index 78a30e0cf..7b68c8e27 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -112,6 +112,7 @@ fun Navigation( SettingsScreen( navController = navController, audioRecorder = audioRecorder, + videoRecorder = videoRecorder, ) } composable( diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt index bc3ddc229..d494a1e48 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt @@ -48,13 +48,14 @@ fun QuickMaxDurationSelector( ) { Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .padding(bottom = SHEET_BOTTOM_OFFSET) ) { Box( modifier = Modifier .widthIn(max = 400.dp) - .padding(horizontal = 16.dp) - .padding(bottom = SHEET_BOTTOM_OFFSET), + .padding(horizontal = 16.dp, vertical = 24.dp), ) { Text( stringResource(R.string.ui_recorder_action_changeMaxDuration_title), @@ -88,8 +89,6 @@ fun QuickMaxDurationSelector( } } } - - Spacer(modifier = Modifier.height(32.dp)) } } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index 5fb96e8ac..e8061eef3 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -111,7 +111,7 @@ fun VideoRecorderPreparationSheet( .fillMaxSize(), cameraSelector = videoSettings.cameraSelector, ) - } else + } else { Column( modifier = Modifier .padding(horizontal = 16.dp) @@ -218,6 +218,7 @@ fun VideoRecorderPreparationSheet( ) } } + } } } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index 9729051b1..bc32bda19 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -60,6 +60,7 @@ import app.myzel394.alibi.ui.components.atoms.MessageBox import app.myzel394.alibi.ui.components.atoms.MessageType import app.myzel394.alibi.ui.effects.rememberSettings import app.myzel394.alibi.ui.models.AudioRecorderModel +import app.myzel394.alibi.ui.models.VideoRecorderModel import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @@ -67,6 +68,7 @@ import kotlinx.coroutines.launch fun SettingsScreen( navController: NavController, audioRecorder: AudioRecorderModel, + videoRecorder: VideoRecorderModel, ) { val snackbarHostState = remember { SnackbarHostState() } val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior( @@ -120,7 +122,7 @@ fun SettingsScreen( val settings = rememberSettings() // Show alert - if (audioRecorder.isInRecording) + if (audioRecorder.isInRecording || videoRecorder.isInRecording) { Box( modifier = Modifier .padding(16.dp) @@ -131,6 +133,7 @@ fun SettingsScreen( message = stringResource(R.string.ui_settings_hint_recordingActive_message), ) } + } if (!SUPPORTS_DARK_MODE_NATIVELY) { ThemeSelector() } @@ -140,6 +143,10 @@ fun SettingsScreen( DeleteRecordingsImmediatelyTile(settings = settings) CustomNotificationTile(navController = navController, settings = settings) EnableAppLockTile(settings = settings) + SaveFolderTile( + settings = settings, + snackbarHostState = snackbarHostState, + ) GlobalSwitch( label = stringResource(R.string.ui_settings_advancedSettings_label), checked = settings.showAdvancedSettings, @@ -157,11 +164,6 @@ fun SettingsScreen( verticalArrangement = Arrangement.spacedBy(32.dp), ) { Column { - SaveFolderTile( - settings = settings, - snackbarHostState = snackbarHostState, - ) - DividerTitle( title = stringResource(R.string.ui_settings_sections_audio_title), description = stringResource(R.string.ui_settings_sections_audio_description), From b8787586db718f2de90206e9e1a29da1e750f00f Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 1 Jan 2024 17:22:34 +0100 Subject: [PATCH 144/176] chore: remove empty line --- app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt b/app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt index e55f31313..d39550378 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/Doctor.kt @@ -19,7 +19,6 @@ data class Doctor( val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) if (intent.resolveActivity(context.packageManager) != null) { return true - } val results = From f35a54cfa65bee7e11f5e86aa82c920aa330cadd Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Tue, 2 Jan 2024 17:24:04 +0100 Subject: [PATCH 145/176] fix: Fix batches collector --- .../myzel394/alibi/helpers/BatchesFolder.kt | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 6cd59b49c..c250ad900 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -75,7 +75,7 @@ abstract class BatchesFolder( context.contentResolver.query( scopedMediaContentUri, null, - null, + "${MediaStore.MediaColumns.DISPLAY_NAME} LIKE '$mediaPrefix%'", null, null, )!!.use { cursor -> @@ -104,7 +104,7 @@ abstract class BatchesFolder( val result = callback(rawName, counter, uri, cursor) - if (result != null) { + if (result == false) { return } } @@ -143,27 +143,30 @@ abstract class BatchesFolder( } BatchType.MEDIA -> { - val fileUris = mutableListOf() + val fileUris = mutableListOf>() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - queryMediaContent { _, _, uri, _ -> - fileUris.add(uri) + queryMediaContent { rawName, _, uri, _ -> + fileUris.add(Pair(rawName, uri)) } } else { legacyMediaFolder.listFiles()?.forEach { - fileUris.add(it.toUri()) + fileUris.add(Pair(it.name, it.toUri())) } } fileUris .sortedBy { - return@sortedBy it - .lastPathSegment!! + val name = it.first + + return@sortedBy name .substring(mediaPrefix.length) .substringBeforeLast(".") .toInt() } - .map { uri -> + .map { pair -> + val uri = pair.second + FFmpegKitConfig.getSafParameterForRead( context, uri, From 3f1e00ac8245c3a6a66e2cf4bddb79dd338816d8 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Tue, 2 Jan 2024 22:11:37 +0100 Subject: [PATCH 146/176] refactor: Rename translation strings --- .../java/app/myzel394/alibi/db/AppSettings.kt | 23 +++---- .../services/RecorderNotificationHelper.kt | 40 +++++++---- .../alibi/services/RecorderService.kt | 5 +- .../molecules/EditNotificationInput.kt | 17 +---- .../organisms/NotificationEditor.kt | 2 +- .../atoms/BatchesInaccessibleDialog.kt | 5 +- .../atoms/ConfirmDeletionDialog.kt | 6 +- .../RecorderScreen/atoms/DeleteButton.kt | 2 +- .../RecorderScreen/atoms/PauseResumeButton.kt | 4 +- .../atoms/RecorderErrorDialog.kt | 6 +- .../atoms/RecorderProcessingDialog.kt | 4 +- .../RecorderScreen/atoms/SaveButton.kt | 8 +-- .../organisms/RecorderEventsHandler.kt | 4 +- .../organisms/StartRecording.kt | 10 ++- app/src/main/res/values-af/strings.xml | 28 ++++---- app/src/main/res/values-ar/strings.xml | 28 ++++---- app/src/main/res/values-ca/strings.xml | 28 ++++---- app/src/main/res/values-cs/strings.xml | 28 ++++---- app/src/main/res/values-da/strings.xml | 28 ++++---- app/src/main/res/values-de/strings.xml | 28 ++++---- app/src/main/res/values-el/strings.xml | 28 ++++---- app/src/main/res/values-es/strings.xml | 28 ++++---- app/src/main/res/values-fi/strings.xml | 28 ++++---- app/src/main/res/values-fr/strings.xml | 28 ++++---- app/src/main/res/values-he/strings.xml | 28 ++++---- app/src/main/res/values-hu/strings.xml | 28 ++++---- app/src/main/res/values-it/strings.xml | 28 ++++---- app/src/main/res/values-ja/strings.xml | 28 ++++---- app/src/main/res/values-ko/strings.xml | 28 ++++---- app/src/main/res/values-nl/strings.xml | 28 ++++---- app/src/main/res/values-no/strings.xml | 28 ++++---- app/src/main/res/values-pl/strings.xml | 28 ++++---- app/src/main/res/values-pt/strings.xml | 28 ++++---- app/src/main/res/values-ro/strings.xml | 28 ++++---- app/src/main/res/values-ru/strings.xml | 28 ++++---- app/src/main/res/values-sr/strings.xml | 28 ++++---- app/src/main/res/values-sv/strings.xml | 28 ++++---- app/src/main/res/values-tr/strings.xml | 28 ++++---- app/src/main/res/values-uk/strings.xml | 28 ++++---- app/src/main/res/values-vi/strings.xml | 28 ++++---- app/src/main/res/values-zh/strings.xml | 28 ++++---- app/src/main/res/values/strings.xml | 69 ++++++++++--------- 42 files changed, 474 insertions(+), 487 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 334899560..25783be35 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -1,18 +1,13 @@ package app.myzel394.alibi.db import android.content.Context -import android.content.Intent import android.media.MediaRecorder import android.os.Build -import androidx.biometric.BiometricManager -import androidx.biometric.BiometricManager.Authenticators import androidx.camera.video.Quality import androidx.camera.video.QualitySelector -import androidx.core.app.ActivityCompat.startActivityForResult import app.myzel394.alibi.R import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder -import app.myzel394.alibi.services.VideoRecorderService import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import java.time.LocalDateTime @@ -502,39 +497,39 @@ data class NotificationSettings( @Serializable data object Default : Preset( R.string.ui_audioRecorder_state_recording_title, - R.string.ui_audioRecorder_state_recording_description, + R.string.ui_recorder_state_recording_description, true, R.drawable.launcher_monochrome_noopacity, ) @Serializable data object Weather : Preset( - R.string.ui_audioRecorder_state_recording_fake_weather_title, - R.string.ui_audioRecorder_state_recording_fake_weather_description, + R.string.ui_recorder_state_recording_fake_weather_title, + R.string.ui_recorder_state_recording_fake_weather_description, false, R.drawable.ic_cloud ) @Serializable data object Player : Preset( - R.string.ui_audioRecorder_state_recording_fake_player_title, - R.string.ui_audioRecorder_state_recording_fake_player_description, + R.string.ui_recorder_state_recording_fake_player_title, + R.string.ui_recorder_state_recording_fake_player_description, true, R.drawable.ic_note, ) @Serializable data object Browser : Preset( - R.string.ui_audioRecorder_state_recording_fake_browser_title, - R.string.ui_audioRecorder_state_recording_fake_browser_description, + R.string.ui_recorder_state_recording_fake_browser_title, + R.string.ui_recorder_state_recording_fake_browser_description, true, R.drawable.ic_download, ) @Serializable data object VPN : Preset( - R.string.ui_audioRecorder_state_recording_fake_vpn_title, - R.string.ui_audioRecorder_state_recording_fake_vpn_description, + R.string.ui_recorder_state_recording_fake_vpn_title, + R.string.ui_recorder_state_recording_fake_vpn_description, false, R.drawable.ic_vpn, ) diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt index 9ff5c99de..f46dd45bf 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt @@ -58,7 +58,7 @@ data class RecorderNotificationHelper( return PendingIntent.getService( context, requestCode, - Intent(context, AudioRecorderService::class.java).apply { + Intent(context, context::class.java).apply { action = "changeState" putExtra("newState", newState.name) }, @@ -89,10 +89,24 @@ data class RecorderNotificationHelper( .setChronometerCountDown(false) } + private fun getStringForRecorder(audioRes: Int, videoRes: Int): String = + when (context::class.java) { + AudioRecorderService::class.java -> context.getString(audioRes) + + VideoRecorderService::class.java -> context.getString(videoRes) + + else -> "" + } + fun buildStartingNotification(): Notification { return createBaseNotification() - .setContentTitle(context.getString(R.string.ui_audioRecorder_state_recording_title)) - .setContentText(context.getString(R.string.ui_audioRecorder_state_recording_description)) + .setContentTitle( + getStringForRecorder( + R.string.ui_audioRecorder_state_recording_title, + R.string.ui_videoRecorder_state_recording_title, + ) + ) + .setContentText(context.getString(R.string.ui_recorder_state_recording_description)) .build() } @@ -109,37 +123,35 @@ data class RecorderNotificationHelper( .toInstant() ).time, ) - .addAction( - R.drawable.ic_cancel, - context.getString(R.string.ui_audioRecorder_action_delete_label), - getNotificationChangeStateIntent(RecorderState.STOPPED, 1), - ) .addAction( R.drawable.ic_pause, - context.getString(R.string.ui_audioRecorder_action_pause_label), + context.getString(R.string.ui_recorder_action_pause_label), getNotificationChangeStateIntent(RecorderState.PAUSED, 2), ) .setContentTitle( details?.title - ?: context.getString(R.string.ui_audioRecorder_state_recording_title) + ?: getStringForRecorder( + R.string.ui_audioRecorder_state_recording_title, + R.string.ui_videoRecorder_state_recording_title, + ) ) .setContentText( details?.description - ?: context.getString(R.string.ui_audioRecorder_state_recording_description) + ?: context.getString(R.string.ui_recorder_state_recording_description) ) .build() } fun buildPausedNotification(start: LocalDateTime): Notification { return createBaseNotification() - .setContentTitle(context.getString(R.string.ui_audioRecorder_state_paused_title)) - .setContentText(context.getString(R.string.ui_audioRecorder_state_paused_description)) + .setContentTitle(context.getString(R.string.ui_recorder_state_paused_title)) + .setContentText(context.getString(R.string.ui_recorder_state_paused_description)) .setOngoing(false) .setUsesChronometer(false) .setWhen(Date.from(start.atZone(ZoneId.systemDefault()).toInstant()).time) .addAction( R.drawable.ic_play, - context.getString(R.string.ui_audioRecorder_action_resume_label), + context.getString(R.string.ui_recorder_action_resume_label), getNotificationChangeStateIntent(RecorderState.RECORDING, 3), ) .build() diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index 463010d71..fa9f2a809 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -1,14 +1,13 @@ package app.myzel394.alibi.services import android.annotation.SuppressLint +import android.app.ActivityManager import android.app.Notification import android.content.Intent -import android.content.pm.ServiceInfo import android.os.Binder -import android.os.Build import android.os.IBinder import androidx.core.app.NotificationManagerCompat -import androidx.core.app.ServiceCompat +import androidx.core.content.ContextCompat.getSystemService import androidx.lifecycle.LifecycleService import app.myzel394.alibi.NotificationHelper import app.myzel394.alibi.enums.RecorderState diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt index c3dcc5486..11ea5cb22 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt @@ -1,18 +1,12 @@ package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.tween -import androidx.compose.foundation.Image import androidx.compose.foundation.background 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.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons @@ -20,7 +14,6 @@ import androidx.compose.material.icons.filled.Circle import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text -import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -31,12 +24,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction @@ -45,10 +34,8 @@ import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.PreviewIcon import app.myzel394.alibi.ui.effects.rememberForceUpdate -import com.maxkeppeler.sheets.input.models.InputText import java.time.Duration import java.time.LocalDateTime -import java.time.Period @Composable fun EditNotificationInput( @@ -167,13 +154,13 @@ fun EditNotificationInput( horizontalArrangement = Arrangement.spacedBy(16.dp), ) { Text( - stringResource(R.string.ui_audioRecorder_action_delete_label), + stringResource(R.string.ui_recorder_action_delete_label), color = MaterialTheme.colorScheme.secondary, fontSize = MaterialTheme.typography.bodyMedium.fontSize, fontWeight = FontWeight.Bold, ) Text( - stringResource(R.string.ui_audioRecorder_action_pause_label), + stringResource(R.string.ui_recorder_action_pause_label), color = MaterialTheme.colorScheme.secondary, fontSize = MaterialTheme.typography.bodyMedium.fontSize, fontWeight = FontWeight.Bold, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt index 7aacc9713..8134073ea 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt @@ -90,7 +90,7 @@ fun NotificationEditor( } else { val defaultTitle = stringResource(R.string.ui_audioRecorder_state_recording_title) val defaultDescription = - stringResource(R.string.ui_audioRecorder_state_recording_description) + stringResource(R.string.ui_recorder_state_recording_description) LaunchedEffect(Unit) { notificationModel.initialize( diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt index e9cda6661..ebd9bb29c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt @@ -2,7 +2,6 @@ package app.myzel394.alibi.ui.components.RecorderScreen.atoms import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Error -import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults @@ -25,10 +24,10 @@ fun BatchesInaccessibleDialog( ) }, title = { - Text(stringResource(R.string.ui_audioRecorder_error_recording_title)) + Text(stringResource(R.string.ui_recorder_error_recording_title)) }, text = { - Text(stringResource(R.string.ui_audioRecorder_error_batchesInaccessible_description)) + Text(stringResource(R.string.ui_recorder_error_batchesInaccessible_description)) }, confirmButton = { Button( diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/ConfirmDeletionDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/ConfirmDeletionDialog.kt index f43a657a2..a060ade81 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/ConfirmDeletionDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/ConfirmDeletionDialog.kt @@ -28,10 +28,10 @@ fun ConfirmDeletionDialog( onDismiss() }, title = { - Text(stringResource(R.string.ui_audioRecorder_action_delete_confirm_title)) + Text(stringResource(R.string.ui_recorder_action_delete_confirm_title)) }, text = { - Text(stringResource(R.string.ui_audioRecorder_action_delete_confirm_message)) + Text(stringResource(R.string.ui_recorder_action_delete_confirm_message)) }, icon = { Icon( @@ -40,7 +40,7 @@ fun ConfirmDeletionDialog( ) }, confirmButton = { - val label = stringResource(R.string.ui_audioRecorder_action_delete_label) + val label = stringResource(R.string.ui_recorder_action_delete_label) Button( modifier = Modifier .semantics { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/DeleteButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/DeleteButton.kt index 24e54638f..70a0a863e 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/DeleteButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/DeleteButton.kt @@ -33,7 +33,7 @@ fun DeleteButton( }, ) } - val label = stringResource(R.string.ui_audioRecorder_action_delete_label) + val label = stringResource(R.string.ui_recorder_action_delete_label) Button( modifier = Modifier .semantics { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/PauseResumeButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/PauseResumeButton.kt index b7ef23784..3268af356 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/PauseResumeButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/PauseResumeButton.kt @@ -18,8 +18,8 @@ fun PauseResumeButton( isPaused: Boolean, onChange: () -> Unit, ) { - val pauseLabel = stringResource(R.string.ui_audioRecorder_action_pause_label) - val resumeLabel = stringResource(R.string.ui_audioRecorder_action_resume_label) + val pauseLabel = stringResource(R.string.ui_recorder_action_pause_label) + val resumeLabel = stringResource(R.string.ui_recorder_action_resume_label) FloatingActionButton( modifier = Modifier diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt index 374dda86f..4f3031fd2 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt @@ -25,10 +25,10 @@ fun RecorderErrorDialog( ) }, title = { - Text(stringResource(R.string.ui_audioRecorder_error_recording_title)) + Text(stringResource(R.string.ui_recorder_error_recording_title)) }, text = { - Text(stringResource(R.string.ui_audioRecorder_error_recording_description)) + Text(stringResource(R.string.ui_recorder_error_recording_description)) }, dismissButton = { Button( @@ -43,7 +43,7 @@ fun RecorderErrorDialog( onClick = onSave, colors = ButtonDefaults.textButtonColors(), ) { - Text(stringResource(R.string.ui_audioRecorder_action_save_label)) + Text(stringResource(R.string.ui_recorder_action_save_label)) } } ) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt index d8756ccaf..763bc3281 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt @@ -28,7 +28,7 @@ fun RecorderProcessingDialog() { }, title = { Text( - stringResource(R.string.ui_audioRecorder_action_save_processing_dialog_title), + stringResource(R.string.ui_recorder_action_save_processing_dialog_title), ) }, text = { @@ -36,7 +36,7 @@ fun RecorderProcessingDialog() { horizontalAlignment = Alignment.CenterHorizontally, ) { Text( - stringResource(R.string.ui_audioRecorder_action_save_processing_dialog_description), + stringResource(R.string.ui_recorder_action_save_processing_dialog_description), ) Spacer(modifier = Modifier.height(32.dp)) LinearProgressIndicator() diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt index b881635cd..8dcb8752e 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt @@ -1,13 +1,7 @@ package app.myzel394.alibi.ui.components.RecorderScreen.atoms -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Save import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -22,7 +16,7 @@ fun SaveButton( modifier: Modifier = Modifier, onSave: () -> Unit, ) { - val label = stringResource(R.string.ui_audioRecorder_action_save_label) + val label = stringResource(R.string.ui_recorder_action_save_label) Button( modifier = Modifier diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt index 83dbe1dd8..f7fce5ed9 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt @@ -97,8 +97,8 @@ fun RecorderEventsHandler( } } - val successMessage = stringResource(R.string.ui_audioRecorder_action_save_success) - val openMessage = stringResource(R.string.ui_audioRecorder_action_save_openFolder) + val successMessage = stringResource(R.string.ui_recorder_action_save_success) + val openMessage = stringResource(R.string.ui_recorder_action_save_openFolder) fun openFolder(uri: Uri) { val intent = Intent(Intent.ACTION_VIEW, uri) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt index f29b04938..a3dbea470 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.ClickableText import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Save @@ -36,7 +35,6 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import app.myzel394.alibi.R @@ -67,11 +65,11 @@ fun StartRecording( val context = LocalContext.current val label = stringResource( - R.string.ui_audioRecorder_action_start_description_2, + R.string.ui_recorder_action_start_description_2, appSettings.maxDuration / 1000 / 60 ) val annotatedDescription = buildAnnotatedString { - append(stringResource(R.string.ui_audioRecorder_action_start_description_1)) + append(stringResource(R.string.ui_recorder_action_start_description_1)) withStyle(SpanStyle(background = MaterialTheme.colorScheme.surfaceVariant)) { pushStringAnnotation( @@ -81,7 +79,7 @@ fun StartRecording( append(label) } - append(stringResource(R.string.ui_audioRecorder_action_start_description_3)) + append(stringResource(R.string.ui_recorder_action_start_description_3)) } var showQuickMaxDurationSelector by rememberSaveable { @@ -127,7 +125,7 @@ fun StartRecording( ) { if (appSettings.lastRecording?.hasRecordingsAvailable(context) == true) { val label = stringResource( - R.string.ui_audioRecorder_action_saveOldRecording_label, + R.string.ui_recorder_action_saveOldRecording_label, DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL) .format(appSettings.lastRecording.recordingStart), ) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index b91dee1a3..6d8d1b8eb 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -14,18 +14,18 @@ Bitte erteile die Berechtigung, um fortzufahren Du wirst zu den Einstellungen weitergeleitet, um dort die Berechtigung zu erteilen. Aufnahme starten - Aufnahme vom %s speichern - Löschen - Aufnahme Löschen? - Bist du sicher, dass du diese Aufnahme löschen möchtest? - Aufnahme pausieren - Aufnahme fortsetzten - Aufnahme speichern + Aufnahme vom %s speichern + Löschen + Aufnahme Löschen? + Bist du sicher, dass du diese Aufnahme löschen möchtest? + Aufnahme pausieren + Aufnahme fortsetzten + Aufnahme speichern Alibi wird im Hintergrund weiterhin aufnehmen und die letzten %s Minuten auf Wunsch speichern - Bearbeiten - Audio wird bearbeitet, Alibi nicht schließen! Du wirst automatisch aufgefordert, die Datei zu speichern, wenn diese fertig bearbeitet ist + Bearbeiten + Audio wird bearbeitet, Alibi nicht schließen! Du wirst automatisch aufgefordert, die Datei zu speichern, wenn diese fertig bearbeitet ist Aufnahme läuft - Alibi nimmt im Hintergrund weiter auf + Alibi nimmt im Hintergrund weiter auf Wilkommen zu Alibi! Alibi funktioniert wird eine Dashcam für dein Handy. Es ermöglicht dir, Ton im Hintergrund kontinuierlich aufzunehmen und die letzten 30 Minuten auf Wunsch zu speichern. Du bist für die Nutzung dieser App verantwortlich! @@ -51,10 +51,10 @@ Kodierer Das Ausgabeformat wurde geändert, da das aktuelle mit diesem Encoder inkompatibel war Automatisch - Aufnahme pausiert - Audio-Aufnahme wurde pausiert - Es ist ein Fehler aufgetreten - Alibi stieß bei der Aufnahme auf einen Fehler. Soll die Aufnahme gespeichert werden? + Aufnahme pausiert + Audio-Aufnahme wurde pausiert + Es ist ein Fehler aufgetreten + Alibi stieß bei der Aufnahme auf einen Fehler. Soll die Aufnahme gespeichert werden? Sprache Ändern Einstellungen importieren diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index fabc9a621..fbdb47aae 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -49,10 +49,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index c85aa2f87..377384842 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -14,18 +14,18 @@ Devam etmek için izni verin İzin vermek için uygulama ayarlarına yönlendirileceksiniz. Kayda Başla - %s tarihinden Kaydı Kaydet - Sil - Kayıt Silinsin mi? - Bu kaydı silmek istediğinizden emin misiniz? - Kaydı Duraklat - Kayda Devam Et - Kaydı Kaydet + %s tarihinden Kaydı Kaydet + Sil + Kayıt Silinsin mi? + Bu kaydı silmek istediğinizden emin misiniz? + Kaydı Duraklat + Kayda Devam Et + Kaydı Kaydet Alibi arka planda kayıt yapmaya devam edecek ve son %s dakikayı depolayacaktır - İşleniyor - Ses işleniyor, Alibi\'yi kapatmayın! Dosya hazır olduğunda dosyayı kaydetmeniz istenecektir + İşleniyor + Ses işleniyor, Alibi\'yi kapatmayın! Dosya hazır olduğunda dosyayı kaydetmeniz istenecektir Ses Kaydediliyor - Alibi arka planda kayıt yapmaya devam eder + Alibi arka planda kayıt yapmaya devam eder Alibi\'ye Hoş Geldiniz! Alibi, telefonunuz için bir araç kamerası gibidir. Sürekli olarak sesinizi kaydetmenize ve ihtiyaç duyduğunuzda son 30 dakikayı kaydetmenize olanak tanır. Bu uygulamanın kullanımından tamamen siz sorumlusunuz @@ -51,10 +51,10 @@ Kodlayıcı Çıktı formatı, mevcut kodlayıcıyla uyumsuz olduğu için değiştirildi Otomatik - Kayıt Duraklatıldı - Ses Kaydı duraklatıldı - Bir hata oluştu - Alibi, kayıt sırasında bir hata ile karşılaştı. Ses kaydını kaydetmeyi denemek ister misiniz? + Kayıt Duraklatıldı + Ses Kaydı duraklatıldı + Bir hata oluştu + Alibi, kayıt sırasında bir hata ile karşılaştı. Ses kaydını kaydetmeyi denemek ister misiniz? Dil Değiştir Import Settings diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 73e0687a7..437d3a0e1 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -14,18 +14,18 @@ Please grant the permission to continue You will be redirected to the app settings to grant the permission there. Start Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause Recording - Resume Recording - Save Recording + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause Recording + Resume Recording + Save Recording Alibi will continue recording in the background and store the last %s minutes at your request - Processing - Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready + Processing + Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready Recording Audio - Alibi keeps recording in the background + Alibi keeps recording in the background Welcome to Alibi! Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. You are solely responsible for the use of this app @@ -51,10 +51,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Audio Recording has been paused + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 132b2367b..12dd7efc5 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -14,18 +14,18 @@ 请授予权限以继续 您将被重定向到应用程序设置页面,在那里可以授予相关权限 开始录音 - Save Recording from %s - 删除 - 确定删除记录? - 您确定要删除此录音吗? - 暂停录制 - 恢复录制 - 保存录音 + Save Recording from %s + 删除 + 确定删除记录? + 您确定要删除此录音吗? + 暂停录制 + 恢复录制 + 保存录音 Alibi will continue recording in the background and store the last %s minutes at your request - 处理中 - 正在处理音频,请勿关闭Alibi! 一旦文件准备好,您将自动收到保存文件的提示 + 处理中 + 正在处理音频,请勿关闭Alibi! 一旦文件准备好,您将自动收到保存文件的提示 录制中 - Alibi会在后台持续记录 + Alibi会在后台持续记录 欢迎使用Alibi! Alibi就像你手机上的行车记录仪一样。它会不断地录制音频,并在您需要时保存最近的30分钟录音。 您需要自行承担使用该应用程序所带来的风险和后果。 @@ -51,10 +51,10 @@ 编码器 Output Format has been changed because the current one was incompatible with this encoder 自动 - 录制暂停 - 音频录制已暂停 - An error occured - Alibi encountered an error during recording. Would you like to try saving the recording? + 录制暂停 + 音频录制已暂停 + An error occured + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Import Settings diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fce7f8ccf..b8efd9efd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,28 +19,40 @@ You will be redirected to the app settings to grant the permission there. Start Audio Recording - Save Recording from %s - Delete - Delete Recording? - Are you sure you want to delete this recording? - Pause - Resume - Save + Recording Audio - Alibi will continue recording in the background and store the last\u0020 - %s minutes - \u0020at your request + Save Recording from %s + Delete + Delete Recording? + Are you sure you want to delete this recording? + Pause + Resume + Save - Processing - Processing your recording, do not close Alibi! You will automatically be prompted to save the file once it\'s ready + Recording has been saved successfully! + Open + + Alibi will continue recording in the background and store the last\u0020 + %s minutes + \u0020at your request + + Processing + Processing your recording, do not close Alibi! You will automatically be prompted to save the file once it\'s ready + + Alibi keeps recording in the background + + Current Weather + 14° with light chance of rain + Playing Audio + Now playing: Despacito + Downloading attachments.zip + Downloading file... + Connected to VPN + Connection Secured - Recording Audio - Alibi keeps recording in the background - Current Weather - 14° with light chance of rain Welcome to Alibi! - Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it. + Alibi is like a dashcam for your phone. It allows you to record audio and video continuously and save the last 30 minutes at your request. You are solely responsible for the use of this app Alibi does not take any responsibility for the use of this app. You are solely responsible. Use it at your own risk. Start Alibi @@ -52,9 +64,7 @@ Max duration Set the maximum duration of the recording Batch duration - Record a single batch for this duration. Alibi records multiple batches and deletes the oldest one. When exporting the audio, all batches will be merged together - Force exact duration - Force to strip the output file to be the exactly specified duration. If this is disabled, the output file may be a bit longer due to batches of audio samples being encoded together. + Record a single batch for this duration. Alibi records multiple batches and deletes the oldest one. When exporting the recording, all batches will be merged together Bitrate A higher bitrate means better quality but also larger file size Set the bitrate for the audio recording @@ -65,10 +75,10 @@ Encoder Output Format has been changed because the current one was incompatible with this encoder Auto - Recording paused - Audio Recording has been paused - An error occurred - Alibi encountered an error during recording. Would you like to try saving the recording? + Recording paused + Alibi is paused + An error occurred + Alibi encountered an error during recording. Would you like to try saving the recording? Language Change Device Microphone @@ -92,12 +102,6 @@ Due to Android\'s restrictions, Alibi has to show a notification while recording. To hide the fact that you\'re using Alibi, you can customize the notification. Alternatively, you can also simply disable notifications Create own notification - Playing Audio - Now playing: Despacito - Downloading attachments.zip - Downloading file... - Connected to VPN - Connection Secured Apply Preset \"%s\" Show Duration Update notification @@ -122,8 +126,6 @@ By default, Alibi will save the recording batches into its private, encrypted file storage. You can change this and specify an external, unencrypted folder. This will allow you to access the batches manually. ONLY DO THIS IF YOU KNOW WHAT YOU ARE DOING! Yes, change folder Use private, encrypted storage - Recording has been saved successfully! - Open Audio Recording Only applies to audio recordings Video Recording @@ -176,7 +178,8 @@ Permission required To access the DCIM folder, you need to grant Alibi the permission to access external storage. Alibi will only use this permission to write your recordings to the DCIM folder. Grant permission - Alibi couldn\'t access or write to the batches folder. Try to choose a different folder or use the internal storage instead. The recording has been aborted. + Alibi couldn\'t access or write to the batches folder. Try to choose a different folder or use the internal storage instead. The recording has been aborted. File Manager app not found Alibi couldn\'t find a file manager app on your phone. Please install a file manager app and try again. If this message still appears, you can try using a custom batches folder in the advanced settings section. Alibi may not fully work on your device. + Recording Video \ No newline at end of file From 386d3cb7335aeba8dbe7df3e5a745c74dcbb7893 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 4 Jan 2024 00:13:49 +0100 Subject: [PATCH 147/176] feat: Add progress bar support for video --- .../myzel394/alibi/helpers/BatchesFolder.kt | 32 +++- .../myzel394/alibi/helpers/MediaConverter.kt | 113 ++++++++++--- .../atoms/RecorderProcessingDialog.kt | 9 +- .../organisms/RecorderEventsHandler.kt | 149 ++++++++++-------- 4 files changed, 211 insertions(+), 92 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index c250ad900..9b80a2566 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -23,8 +23,10 @@ import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import app.myzel394.alibi.ui.utils.PermissionHelper +import com.arthenica.ffmpegkit.FFprobeKit +import com.arthenica.ffmpegkit.FFprobeSession import kotlinx.coroutines.CompletableDeferred -import kotlin.reflect.KFunction3 +import kotlin.reflect.KFunction4 abstract class BatchesFolder( open val context: Context, @@ -32,7 +34,7 @@ abstract class BatchesFolder( open val customFolder: DocumentFile? = null, open val subfolderName: String = ".recordings", ) { - abstract val concatenationFunction: KFunction3, String, String, CompletableDeferred> + abstract val concatenationFunction: KFunction4, String, String, (Int) -> Unit, CompletableDeferred> abstract val ffmpegParameters: Array abstract val scopedMediaContentUri: Uri abstract val legacyMediaFolder: File @@ -245,11 +247,13 @@ abstract class BatchesFolder( abstract fun cleanup() - open suspend fun concatenate( + suspend fun concatenate( recordingStart: LocalDateTime, extension: String, disableCache: Boolean = false, onNextParameterTry: (String) -> Unit = {}, + durationPerBatchInMilliseconds: Long = 0, + onProgress: (Float) -> Unit = {}, ): String { if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) { return getOutputFileForFFmpeg( @@ -264,6 +268,20 @@ abstract class BatchesFolder( try { val filePaths = getBatchesForFFmpeg() + + // Casting here to float so it doesn't need to redo it on every progress update + var fullTime: Float? = null + + runCatching { + // `fullTime` is not accurate as the last batch might be shorter, + // but it's good enough for the progress bar + val lastBatchTime = (FFprobeKit.execute( + "-i ${filePaths.last()} -show_entries format=duration -v quiet -of csv=\"p=0\"", + ).output.toFloat() * 1000).toLong() + fullTime = + ((durationPerBatchInMilliseconds * (filePaths.size - 1)) + lastBatchTime).toFloat() + } + val outputFile = getOutputFileForFFmpeg( date = recordingStart, extension = extension, @@ -272,8 +290,12 @@ abstract class BatchesFolder( concatenationFunction( filePaths, outputFile, - parameter, - ).await() + parameter + ) { time -> + if (fullTime != null) { + onProgress(time / fullTime!!) + } + }.await() return outputFile } catch (e: MediaConverter.FFmpegException) { continue diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index ce1f77e4b..715bae614 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -10,6 +10,71 @@ import com.arthenica.ffmpegkit.ReturnCode import kotlinx.coroutines.CompletableDeferred import java.io.File import java.util.UUID +import kotlin.math.log + +// Abstract class for concatenating audio and video files +// The concatenator runs in its own thread to avoid unresponsiveness. +// You may be wondering why we simply not iterate over the FFMPEG_PARAMETERS +// in this thread and then call each FFmpeg initiation just right after it? +// The answer: It's easier; We don't have to deal with the `getBatchesForFFmpeg` function, because +// the batches are only usable once and we if iterate in this thread over the FFMPEG_PARAMETERS +// we would need to refetch the batches here, which is more messy. +// This is okay, because in 99% of the time the first or second parameter will work, +// and so there is no real performance loss. +abstract class Concatenator( + private val inputFiles: Iterable, + private val outputFile: String, + private val extraCommand: String +) : Thread() { + abstract fun concatenate(): CompletableDeferred + + class FFmpegException(message: String) : Exception(message) +} + +data class AudioConcatenator( + private val inputFiles: Iterable, + private val outputFile: String, + private val extraCommand: String +) : Concatenator( + inputFiles, + outputFile, + extraCommand +) { + override fun concatenate(): CompletableDeferred { + val completer = CompletableDeferred() + + val filePathsConcatenated = inputFiles.joinToString("|") + val command = + "-protocol_whitelist saf,concat,content,file,subfile" + + " -i 'concat:$filePathsConcatenated'" + + " -y" + + extraCommand + + " $outputFile" + + FFmpegKit.executeAsync( + command + ) { session -> + if (!ReturnCode.isSuccess(session!!.returnCode)) { + Log.d( + "Audio Concatenation", + String.format( + "Command failed with state %s and rc %s.%s", + session.state, + session.returnCode, + session.failStackTrace, + ) + ) + + completer.completeExceptionally(Exception("Failed to concatenate audios")) + } else { + completer.complete(Unit) + } + } + + return completer + } +} + class MediaConverter { companion object { @@ -17,6 +82,7 @@ class MediaConverter { inputFiles: Iterable, outputFile: String, extraCommand: String = "", + onProgress: (Int) -> Unit = { }, ): CompletableDeferred { val completer = CompletableDeferred() @@ -63,6 +129,7 @@ class MediaConverter { inputFiles: Iterable, outputFile: String, extraCommand: String = "", + onProgress: (Int) -> Unit = { }, ): CompletableDeferred { val completer = CompletableDeferred() @@ -75,32 +142,40 @@ class MediaConverter { " -i ${listFile.absolutePath}" + extraCommand + " -strict normal" + + // TODO: Check if those params work + " -nostats" + + " -loglevel error" + " -y" + " $outputFile" FFmpegKit.executeAsync( - command - ) { session -> - runCatching { - listFile.delete() - } - - if (!ReturnCode.isSuccess(session!!.returnCode)) { - Log.d( - "Video Concatenation", - String.format( - "Command failed with state %s and rc %s.%s", - session.state, - session.returnCode, - session.failStackTrace, + command, + { session -> + runCatching { + listFile.delete() + } + + if (ReturnCode.isSuccess(session!!.returnCode)) { + completer.complete(Unit) + } else { + Log.d( + "Video Concatenation", + String.format( + "Command failed with state %s and rc %s.%s", + session.state, + session.returnCode, + session.failStackTrace, + ) ) - ) - completer.completeExceptionally(FFmpegException("Failed to concatenate videos")) - } else { - completer.complete(Unit) + completer.completeExceptionally(FFmpegException("Failed to concatenate videos")) + } + }, + {}, + { statistics -> + onProgress(statistics.time) } - } + ) return completer } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt index 763bc3281..7c03ac668 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt @@ -17,7 +17,9 @@ import androidx.compose.ui.unit.dp import app.myzel394.alibi.R @Composable -fun RecorderProcessingDialog() { +fun RecorderProcessingDialog( + progress: Float?, +) { AlertDialog( onDismissRequest = { }, icon = { @@ -39,7 +41,10 @@ fun RecorderProcessingDialog() { stringResource(R.string.ui_recorder_action_save_processing_dialog_description), ) Spacer(modifier = Modifier.height(32.dp)) - LinearProgressIndicator() + if (progress != null) + LinearProgressIndicator(progress = progress) + else + LinearProgressIndicator() } }, confirmButton = {} diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt index f7fce5ed9..727f1fa7f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt @@ -9,6 +9,8 @@ import androidx.compose.material3.SnackbarResult import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableDoubleStateOf +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -32,6 +34,8 @@ import app.myzel394.alibi.ui.models.VideoRecorderModel import app.myzel394.alibi.ui.utils.rememberFileSaverDialog import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlin.concurrent.thread typealias RecorderModel = BaseRecorderModel< RecordingInformation, @@ -54,6 +58,8 @@ fun RecorderEventsHandler( var showRecorderError by remember { mutableStateOf(false) } var showBatchesInaccessibleError by remember { mutableStateOf(false) } + var processingProgress by remember { mutableFloatStateOf(0.0f) } + val saveAudioFile = rememberFileSaverDialog(settings.audioRecorderSettings.getMimeType()) { if (settings.deleteRecordingsImmediately) { runCatching { @@ -135,81 +141,89 @@ fun RecorderEventsHandler( // Give the user some time to see the processing dialog delay(100) - try { - val recording = - // When new recording created - recorder.recorderService?.getRecordingInformation() - // When recording is loaded from lastRecording - ?: settings.lastRecording - ?: throw Exception("No recording information available") - val batchesFolder = when (recorder.javaClass) { - AudioRecorderModel::class.java -> AudioBatchesFolder.importFromFolder( - recording.folderPath, - context - ) - - VideoRecorderModel::class.java -> VideoBatchesFolder.importFromFolder( - recording.folderPath, - context - ) - - else -> throw Exception("Unknown recorder type") - } - - batchesFolder.concatenate( - recording.recordingStart, - recording.fileExtension, - ) - - // Save file - val name = batchesFolder.getName( - recording.recordingStart, - recording.fileExtension, - ) + thread { + runBlocking { + try { + val recording = + // When new recording created + recorder.recorderService?.getRecordingInformation() + // When recording is loaded from lastRecording + ?: settings.lastRecording + ?: throw Exception("No recording information available") + val batchesFolder = when (recorder.javaClass) { + AudioRecorderModel::class.java -> AudioBatchesFolder.importFromFolder( + recording.folderPath, + context + ) + + VideoRecorderModel::class.java -> VideoBatchesFolder.importFromFolder( + recording.folderPath, + context + ) + + else -> throw Exception("Unknown recorder type") + } - when (batchesFolder.type) { - BatchesFolder.BatchType.INTERNAL -> { - when (batchesFolder) { - is AudioBatchesFolder -> { - saveAudioFile( - batchesFolder.asInternalGetOutputFile( - recording.recordingStart, - recording.fileExtension, - ), name - ) + batchesFolder.concatenate( + recording.recordingStart, + recording.fileExtension, + durationPerBatchInMilliseconds = settings.intervalDuration, + onProgress = { percentage -> + processingProgress = percentage } - - is VideoBatchesFolder -> { - saveVideoFile( - batchesFolder.asInternalGetOutputFile( - recording.recordingStart, - recording.fileExtension, - ), name - ) + ) + + // Save file + val name = batchesFolder.getName( + recording.recordingStart, + recording.fileExtension, + ) + + when (batchesFolder.type) { + BatchesFolder.BatchType.INTERNAL -> { + when (batchesFolder) { + is AudioBatchesFolder -> { + saveAudioFile( + batchesFolder.asInternalGetOutputFile( + recording.recordingStart, + recording.fileExtension, + ), name + ) + } + + is VideoBatchesFolder -> { + saveVideoFile( + batchesFolder.asInternalGetOutputFile( + recording.recordingStart, + recording.fileExtension, + ), name + ) + } + } } - } - } - BatchesFolder.BatchType.CUSTOM -> { - showSnackbar(batchesFolder.customFolder!!.uri) + BatchesFolder.BatchType.CUSTOM -> { + showSnackbar(batchesFolder.customFolder!!.uri) - if (settings.deleteRecordingsImmediately) { - batchesFolder.deleteRecordings() - } - } + if (settings.deleteRecordingsImmediately) { + batchesFolder.deleteRecordings() + } + } - BatchesFolder.BatchType.MEDIA -> { - showSnackbar() + BatchesFolder.BatchType.MEDIA -> { + showSnackbar() - if (settings.deleteRecordingsImmediately) { - batchesFolder.deleteRecordings() + if (settings.deleteRecordingsImmediately) { + batchesFolder.deleteRecordings() + } + } } + } catch (error: Exception) { + Log.getStackTraceString(error) + } finally { + isProcessing = false } } - } catch (error: Exception) { - Log.getStackTraceString(error) - } finally { - isProcessing = false } } @@ -306,8 +320,11 @@ fun RecorderEventsHandler( } if (isProcessing) - RecorderProcessingDialog() + RecorderProcessingDialog( + progress = processingProgress, + ) + // TODO: Add thread for concatenation if (showRecorderError) RecorderErrorDialog( onClose = { From fc9e6d772129ded5773466d39bb269e4d4606635 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 4 Jan 2024 20:59:18 +0100 Subject: [PATCH 148/176] feat: Add progress info to audio recorder --- .../myzel394/alibi/helpers/MediaConverter.kt | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index 715bae614..c9b3519c5 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -9,6 +9,7 @@ import com.arthenica.ffmpegkit.FFmpegKitConfig import com.arthenica.ffmpegkit.ReturnCode import kotlinx.coroutines.CompletableDeferred import java.io.File +import java.lang.Compiler.command import java.util.UUID import kotlin.math.log @@ -90,29 +91,36 @@ class MediaConverter { val command = "-protocol_whitelist saf,concat,content,file,subfile" + " -i 'concat:$filePathsConcatenated'" + - " -y" + extraCommand + + " -nostats" + + " -loglevel error" + + " -y" + " $outputFile" FFmpegKit.executeAsync( - command - ) { session -> - if (!ReturnCode.isSuccess(session!!.returnCode)) { - Log.d( - "Audio Concatenation", - String.format( - "Command failed with state %s and rc %s.%s", - session.state, - session.returnCode, - session.failStackTrace, + command, + { session -> + if (!ReturnCode.isSuccess(session!!.returnCode)) { + Log.d( + "Audio Concatenation", + String.format( + "Command failed with state %s and rc %s.%s", + session.state, + session.returnCode, + session.failStackTrace, + ) ) - ) - completer.completeExceptionally(Exception("Failed to concatenate audios")) - } else { - completer.complete(Unit) + completer.completeExceptionally(Exception("Failed to concatenate audios")) + } else { + completer.complete(Unit) + } + }, + {}, + { statistics -> + onProgress(statistics.time) } - } + ) return completer } @@ -142,7 +150,6 @@ class MediaConverter { " -i ${listFile.absolutePath}" + extraCommand + " -strict normal" + - // TODO: Check if those params work " -nostats" + " -loglevel error" + " -y" + From b1fc546f3bc3899c7ee193927348349666f7f559 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 4 Jan 2024 21:39:59 +0100 Subject: [PATCH 149/176] feat: Request external storage permission on recording start if not granted already --- .../molecules/AudioRecordingStart.kt | 73 ++++++---- .../molecules/VideoRecordingStart.kt | 128 ++++++++++-------- 2 files changed, 120 insertions(+), 81 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt index 12feaece9..5b554e08f 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.InsertDriveFile import androidx.compose.material.icons.filled.Mic import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults @@ -29,8 +30,10 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.models.AudioRecorderModel +import app.myzel394.alibi.ui.utils.PermissionHelper @Composable fun AudioRecordingStart( @@ -52,38 +55,54 @@ fun AudioRecordingStart( } PermissionRequester( - permission = Manifest.permission.RECORD_AUDIO, - icon = Icons.Default.Mic, + permission = Manifest.permission.WRITE_EXTERNAL_STORAGE, + icon = Icons.Default.InsertDriveFile, onPermissionAvailable = { startRecording = true } - ) { trigger -> - val label = stringResource(R.string.ui_audioRecorder_action_start_label) - - Button( - onClick = trigger, - modifier = Modifier - .semantics { - contentDescription = label + ) { triggerExternalStorage -> + PermissionRequester( + permission = Manifest.permission.RECORD_AUDIO, + icon = Icons.Default.Mic, + onPermissionAvailable = { + if (!SUPPORTS_SCOPED_STORAGE && !PermissionHelper.hasGranted( + context, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + ) { + triggerExternalStorage() + } else { + startRecording = true } - .size(250.dp) - .clip(shape = CircleShape), - colors = ButtonDefaults.outlinedButtonColors(), - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, + } + ) { triggerRecordAudio -> + val label = stringResource(R.string.ui_audioRecorder_action_start_label) + + Button( + onClick = triggerRecordAudio, + modifier = Modifier + .semantics { + contentDescription = label + } + .size(250.dp) + .clip(shape = CircleShape), + colors = ButtonDefaults.outlinedButtonColors(), ) { - Icon( - Icons.Default.Mic, - contentDescription = null, - modifier = Modifier - .size(80.dp), - ) - Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) - Text( - label, - style = MaterialTheme.typography.titleSmall, - ) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + Icons.Default.Mic, + contentDescription = null, + modifier = Modifier + .size(80.dp), + ) + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + label, + style = MaterialTheme.typography.titleSmall, + ) + } } } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt index 60b529fe3..364cc3ee5 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt @@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt +import androidx.compose.material.icons.filled.InsertDriveFile import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults @@ -44,6 +45,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE +import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.models.VideoRecorderModel import app.myzel394.alibi.ui.utils.PermissionHelper @@ -78,65 +80,83 @@ fun VideoRecordingStart( ) } + val hasGrantedStorage = SUPPORTS_SCOPED_STORAGE || PermissionHelper.hasGranted( + context, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + PermissionRequester( - permission = Manifest.permission.CAMERA, - icon = Icons.Default.CameraAlt, + permission = Manifest.permission.WRITE_EXTERNAL_STORAGE, + icon = Icons.Default.InsertDriveFile, onPermissionAvailable = { showSheet = true - }, - ) { trigger -> - val label = stringResource(R.string.ui_videoRecorder_action_start_label) + } + ) { triggerExternalStorage -> + PermissionRequester( + permission = Manifest.permission.CAMERA, + icon = Icons.Default.CameraAlt, + onPermissionAvailable = { + showSheet = true + }, + ) { triggerCamera -> + val label = stringResource(R.string.ui_videoRecorder_action_start_label) - Column( - modifier = Modifier - .size(250.dp) - .clip(CircleShape) - .semantics { - contentDescription = label - } - .combinedClickable( - interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(color = MaterialTheme.colorScheme.primary), - onClick = { - if (PermissionHelper.hasGranted( - context, - Manifest.permission.CAMERA - ) && PermissionHelper.hasGranted( - context, - Manifest.permission.RECORD_AUDIO - ) - ) { - videoRecorder.startRecording(context, appSettings) - } else { - showSheet = true - } - }, - onLongClick = { - showSheet = true - }, - ), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { - Icon( - Icons.Default.CameraAlt, - contentDescription = null, + Column( modifier = Modifier - .size(80.dp), - tint = MaterialTheme.colorScheme.primary, - ) - Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) - Text( - label, - style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.primary, - ) - Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) - Text( - stringResource(R.string.ui_videoRecorder_action_configure_label), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) + .size(250.dp) + .clip(CircleShape) + .semantics { + contentDescription = label + } + .combinedClickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberRipple(color = MaterialTheme.colorScheme.primary), + onClick = { + if (!hasGrantedStorage) { + triggerExternalStorage() + return@combinedClickable + } + + if (PermissionHelper.hasGranted( + context, + Manifest.permission.CAMERA + ) && PermissionHelper.hasGranted( + context, + Manifest.permission.RECORD_AUDIO + ) + ) { + videoRecorder.startRecording(context, appSettings) + } else { + showSheet = true + } + }, + onLongClick = { + showSheet = true + }, + ), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Icon( + Icons.Default.CameraAlt, + contentDescription = null, + modifier = Modifier + .size(80.dp), + tint = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + label, + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + stringResource(R.string.ui_videoRecorder_action_configure_label), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } } } } \ No newline at end of file From e8df9fbc2801cc35a8c2e583041bcdfb93c69f2a Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:43:23 +0100 Subject: [PATCH 150/176] fix: Improve save folder --- .../main/java/app/myzel394/alibi/db/AppSettings.kt | 11 +++++++++++ .../components/SettingsScreen/Tiles/SaveFolderTile.kt | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 25783be35..1f45583e0 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -1,5 +1,6 @@ package app.myzel394.alibi.db +import android.Manifest import android.content.Context import android.media.MediaRecorder import android.os.Build @@ -8,6 +9,9 @@ import androidx.camera.video.QualitySelector import app.myzel394.alibi.R import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder +import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE +import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE +import app.myzel394.alibi.ui.utils.PermissionHelper import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import java.time.LocalDateTime @@ -102,6 +106,13 @@ data class AppSettings( // To disable biometric authentication, set the instance to null. fun isAppLockEnabled() = appLockSettings != null + fun requiresExternalStoragePermission(context: Context): Boolean { + return !SUPPORTS_SCOPED_STORAGE && (saveFolder == RECORDER_MEDIA_SELECTED_VALUE && !PermissionHelper.hasGranted( + context, + Manifest.permission.WRITE_EXTERNAL_STORAGE + )) + } + enum class Theme { SYSTEM, LIGHT, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 7b8976500..62229f272 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -94,6 +94,7 @@ fun SaveFolderTile( } scope.launch { + println("================= Saving saveFolder: $path") dataStore.updateData { it.setSaveFolder(path) } @@ -390,7 +391,7 @@ fun ExternalPermissionRequiredDialog( AlertDialog( icon = { Icon( - Icons.Default.Warning, + Icons.Default.PermMedia, contentDescription = null, ) }, From c6932fd31dd6659978c3f874abcde20e8ac75b09 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:43:50 +0100 Subject: [PATCH 151/176] feat: Add progress bar; Closes #64 --- .../myzel394/alibi/helpers/BatchesFolder.kt | 9 +++++++- .../myzel394/alibi/helpers/MediaConverter.kt | 4 ---- .../atoms/RecorderProcessingDialog.kt | 6 ++--- .../molecules/AudioRecordingStart.kt | 23 +++++++++++++++---- .../molecules/VideoRecordingStart.kt | 7 +----- .../organisms/RecorderEventsHandler.kt | 2 +- .../components/atoms/PermissionRequester.kt | 23 ++++++++++++++++--- 7 files changed, 51 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 9b80a2566..c1033f582 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -253,7 +253,7 @@ abstract class BatchesFolder( disableCache: Boolean = false, onNextParameterTry: (String) -> Unit = {}, durationPerBatchInMilliseconds: Long = 0, - onProgress: (Float) -> Unit = {}, + onProgress: (Float?) -> Unit = {}, ): String { if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) { return getOutputFileForFFmpeg( @@ -265,6 +265,7 @@ abstract class BatchesFolder( for (parameter in ffmpegParameters) { Log.i("Concatenation", "Trying parameter $parameter") onNextParameterTry(parameter) + onProgress(null) try { val filePaths = getBatchesForFFmpeg() @@ -293,7 +294,13 @@ abstract class BatchesFolder( parameter ) { time -> if (fullTime != null) { + println("**************** check24") + println(time) + println(fullTime) onProgress(time / fullTime!!) + } else { + onProgress(null) + println("----------------- nonono") } }.await() return outputFile diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index c9b3519c5..d3fd4dd9f 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -92,8 +92,6 @@ class MediaConverter { "-protocol_whitelist saf,concat,content,file,subfile" + " -i 'concat:$filePathsConcatenated'" + extraCommand + - " -nostats" + - " -loglevel error" + " -y" + " $outputFile" @@ -150,8 +148,6 @@ class MediaConverter { " -i ${listFile.absolutePath}" + extraCommand + " -strict normal" + - " -nostats" + - " -loglevel error" + " -y" + " $outputFile" diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt index 7c03ac668..25f8ce75c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt @@ -41,10 +41,10 @@ fun RecorderProcessingDialog( stringResource(R.string.ui_recorder_action_save_processing_dialog_description), ) Spacer(modifier = Modifier.height(32.dp)) - if (progress != null) - LinearProgressIndicator(progress = progress) - else + if (progress == null) LinearProgressIndicator() + else + LinearProgressIndicator(progress = progress) } }, confirmButton = {} diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt index 5b554e08f..747c7c866 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt @@ -18,6 +18,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -34,6 +35,10 @@ import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.utils.PermissionHelper +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext @Composable fun AudioRecordingStart( @@ -54,6 +59,17 @@ fun AudioRecordingStart( } } + println("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww app: ${appSettings.saveFolder}") + val requiresExternalPerm = rememberSaveable { + appSettings.requiresExternalStoragePermission(context) + } + println("hasGranted ${requiresExternalPerm}") + val scope = rememberCoroutineScope() + + fun test() { + println("appSäääääääääääääääääääääääääääääääättings ${appSettings.saveFolder}") + } + PermissionRequester( permission = Manifest.permission.WRITE_EXTERNAL_STORAGE, icon = Icons.Default.InsertDriveFile, @@ -65,11 +81,8 @@ fun AudioRecordingStart( permission = Manifest.permission.RECORD_AUDIO, icon = Icons.Default.Mic, onPermissionAvailable = { - if (!SUPPORTS_SCOPED_STORAGE && !PermissionHelper.hasGranted( - context, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) - ) { + test() + if (appSettings.requiresExternalStoragePermission(context)) { triggerExternalStorage() } else { startRecording = true diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt index 364cc3ee5..97160292e 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt @@ -80,11 +80,6 @@ fun VideoRecordingStart( ) } - val hasGrantedStorage = SUPPORTS_SCOPED_STORAGE || PermissionHelper.hasGranted( - context, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) - PermissionRequester( permission = Manifest.permission.WRITE_EXTERNAL_STORAGE, icon = Icons.Default.InsertDriveFile, @@ -112,7 +107,7 @@ fun VideoRecordingStart( interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple(color = MaterialTheme.colorScheme.primary), onClick = { - if (!hasGrantedStorage) { + if (appSettings.requiresExternalStoragePermission(context)) { triggerExternalStorage() return@combinedClickable } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt index 727f1fa7f..30df7eec8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt @@ -58,7 +58,7 @@ fun RecorderEventsHandler( var showRecorderError by remember { mutableStateOf(false) } var showBatchesInaccessibleError by remember { mutableStateOf(false) } - var processingProgress by remember { mutableFloatStateOf(0.0f) } + var processingProgress by remember { mutableStateOf(null) } val saveAudioFile = rememberFileSaverDialog(settings.audioRecorderSettings.getMimeType()) { if (settings.deleteRecordingsImmediately) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/PermissionRequester.kt b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/PermissionRequester.kt index 59be98374..7e98f79cd 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/PermissionRequester.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/PermissionRequester.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.compose.runtime.getValue +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector @@ -45,15 +46,22 @@ fun PermissionRequester( var isGranted by remember { mutableStateOf(PermissionHelper.hasGranted(context, permission)) } var visibleDialog by remember { mutableStateOf(null) } + + var _runFunc by rememberSaveable { mutableStateOf(false) } + val requestPermission = rememberLauncherForActivityResult( contract = ActivityResultContracts.RequestPermission(), onResult = { isPermissionGranted: Boolean -> isGranted = isPermissionGranted if (isGranted) { - onPermissionAvailable() + _runFunc = true } else { - if (ActivityCompat.shouldShowRequestPermissionRationale(context as Activity, permission)) { + if (ActivityCompat.shouldShowRequestPermissionRationale( + context as Activity, + permission + ) + ) { visibleDialog = VisibleDialog.REQUEST } else { visibleDialog = VisibleDialog.PERMANENTLY_DENIED @@ -64,12 +72,21 @@ fun PermissionRequester( fun callback() { if (isGranted) { - onPermissionAvailable() + _runFunc = true } else { requestPermission.launch(permission) } } + // No idea but this hacky way is required to make sure the callback + // `onPermissionAvailable` can access other values such as the app settings. + LaunchedEffect(_runFunc) { + if (_runFunc) { + _runFunc = false + onPermissionAvailable() + } + } + if (visibleDialog == VisibleDialog.REQUEST) { AlertDialog( onDismissRequest = { From 0372b44901c953bf0fedd2484f463295abf4f85b Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:45:34 +0100 Subject: [PATCH 152/176] chore: remove debug logs --- .../java/app/myzel394/alibi/helpers/BatchesFolder.kt | 4 ---- .../RecorderScreen/molecules/AudioRecordingStart.kt | 12 ------------ .../SettingsScreen/Tiles/SaveFolderTile.kt | 1 - 3 files changed, 17 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index c1033f582..cb892439d 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -294,13 +294,9 @@ abstract class BatchesFolder( parameter ) { time -> if (fullTime != null) { - println("**************** check24") - println(time) - println(fullTime) onProgress(time / fullTime!!) } else { onProgress(null) - println("----------------- nonono") } }.await() return outputFile diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt index 747c7c866..58a755198 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt @@ -59,17 +59,6 @@ fun AudioRecordingStart( } } - println("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww app: ${appSettings.saveFolder}") - val requiresExternalPerm = rememberSaveable { - appSettings.requiresExternalStoragePermission(context) - } - println("hasGranted ${requiresExternalPerm}") - val scope = rememberCoroutineScope() - - fun test() { - println("appSäääääääääääääääääääääääääääääääättings ${appSettings.saveFolder}") - } - PermissionRequester( permission = Manifest.permission.WRITE_EXTERNAL_STORAGE, icon = Icons.Default.InsertDriveFile, @@ -81,7 +70,6 @@ fun AudioRecordingStart( permission = Manifest.permission.RECORD_AUDIO, icon = Icons.Default.Mic, onPermissionAvailable = { - test() if (appSettings.requiresExternalStoragePermission(context)) { triggerExternalStorage() } else { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 62229f272..546d6462b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -94,7 +94,6 @@ fun SaveFolderTile( } scope.launch { - println("================= Saving saveFolder: $path") dataStore.updateData { it.setSaveFolder(path) } From 08a6d557f8a3f39eae90f040600f674db7a22fff Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 5 Jan 2024 19:12:53 +0100 Subject: [PATCH 153/176] fix: Fix recording for Android API 30 --- app/src/main/AndroidManifest.xml | 2 + .../alibi/helpers/AudioBatchesFolder.kt | 9 ++++- .../myzel394/alibi/helpers/BatchesFolder.kt | 38 ++++++++++--------- .../alibi/helpers/VideoBatchesFolder.kt | 20 +++++----- .../alibi/services/AudioRecorderService.kt | 2 +- .../alibi/services/VideoRecorderService.kt | 3 +- .../java/app/myzel394/alibi/ui/Constants.kt | 5 ++- .../SettingsScreen/Tiles/SaveFolderTile.kt | 5 ++- 8 files changed, 50 insertions(+), 34 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4e2c5ab3f..2f83478ca 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,6 +27,8 @@ + diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt index b709f4f0f..21ba37337 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -64,7 +64,7 @@ class AudioBatchesFolder( val mediaUri = getOrCreateMediaFile( name = getName(date, extension), mimeType = "audio/$extension", - relativePath = Environment.DIRECTORY_DCIM + "/" + MEDIA_SUBFOLDER_NAME, + relativePath = BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_SUBFOLDER_NAME, ) return FFmpegKitConfig.getSafParameterForWrite( @@ -146,8 +146,13 @@ class AudioBatchesFolder( } val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER_NAME + "/audio_recordings" + val BASE_SCOPED_STORAGE_RELATIVE_PATH = + (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + Environment.DIRECTORY_RECORDINGS + else + Environment.DIRECTORY_PODCASTS) val SCOPED_STORAGE_RELATIVE_PATH = - Environment.DIRECTORY_DCIM + "/" + MEDIA_RECORDINGS_SUBFOLDER + BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_RECORDINGS_SUBFOLDER // Parameters to be passed in descending order // Those parameters first try to concatenate without re-encoding diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index cb892439d..516279526 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -250,11 +250,13 @@ abstract class BatchesFolder( suspend fun concatenate( recordingStart: LocalDateTime, extension: String, - disableCache: Boolean = false, + disableCache: Boolean? = null, onNextParameterTry: (String) -> Unit = {}, durationPerBatchInMilliseconds: Long = 0, onProgress: (Float?) -> Unit = {}, ): String { + val disableCache = disableCache ?: (type != BatchType.INTERNAL) + if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) { return getOutputFileForFFmpeg( date = recordingStart, @@ -462,7 +464,7 @@ abstract class BatchesFolder( context.contentResolver.query( scopedMediaContentUri, arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME), - "${MediaStore.MediaColumns.DISPLAY_NAME} = '$name'", + "${MediaStore.MediaColumns.DISPLAY_NAME} = '$name' AND ${Media.RELATIVE_PATH} = '$relativePath'", null, null, )!!.use { cursor -> @@ -482,27 +484,29 @@ abstract class BatchesFolder( } if (uri == null) { - // Create empty output file to be able to write to it - uri = context.contentResolver.insert( - scopedMediaContentUri, - ContentValues().apply { - put( - MediaStore.MediaColumns.DISPLAY_NAME, - name - ) - put( - MediaStore.MediaColumns.MIME_TYPE, - mimeType - ) + try { + // Create empty output file to be able to write to it + uri = context.contentResolver.insert( + scopedMediaContentUri, + ContentValues().apply { + put( + MediaStore.MediaColumns.DISPLAY_NAME, + name + ) + put( + MediaStore.MediaColumns.MIME_TYPE, + mimeType + ) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { put( Media.RELATIVE_PATH, relativePath, ) } - } - )!! + )!! + } catch (e: Exception) { + Log.e("Media", "Failed to create file", e) + } } return uri!! diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index 7517c4536..20a69e471 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -7,6 +7,7 @@ import android.os.Build import android.os.Environment import android.os.ParcelFileDescriptor import android.provider.MediaStore +import androidx.annotation.RequiresApi import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles import app.myzel394.alibi.ui.MEDIA_SUBFOLDER_NAME @@ -108,17 +109,16 @@ class VideoBatchesFolder( } } + @RequiresApi(Build.VERSION_CODES.Q) fun asMediaGetScopedStorageContentValues(name: String) = ContentValues().apply { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - put( - MediaStore.Video.Media.IS_PENDING, - 1 - ) - put( - MediaStore.Video.Media.RELATIVE_PATH, - SCOPED_STORAGE_RELATIVE_PATH, - ) - } + put( + MediaStore.Video.Media.IS_PENDING, + 1 + ) + put( + MediaStore.Video.Media.RELATIVE_PATH, + SCOPED_STORAGE_RELATIVE_PATH, + ) put( MediaStore.Video.Media.DISPLAY_NAME, diff --git a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt index 604ec9811..ee39570da 100644 --- a/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/AudioRecorderService.kt @@ -162,7 +162,7 @@ class AudioRecorderService : } private fun getNameForMediaFile() = - "${batchesFolder.mediaPrefix}$counter.${settings.videoRecorderSettings.fileExtension}" + "${batchesFolder.mediaPrefix}$counter.${settings.audioRecorderSettings.fileExtension}" // ==== Actual recording related ==== private fun createRecorder(): MediaRecorder { diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index d9f50a262..0dd9cd2a8 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -25,6 +25,7 @@ import app.myzel394.alibi.db.RecordingInformation import app.myzel394.alibi.enums.RecorderState import app.myzel394.alibi.helpers.BatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder +import app.myzel394.alibi.ui.SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope @@ -231,7 +232,7 @@ class VideoRecorderService : private fun prepareVideoRecording() = videoCapture!!.output .let { - if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && SUPPORTS_SCOPED_STORAGE) { + if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS) { it.prepareRecording( this, FileDescriptorOutputOptions.Builder( diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index 32e871b96..68afb9db4 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -11,11 +11,14 @@ val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q val MEDIA_SUBFOLDER_NAME = "alibi" -val SUPPORTS_SCOPED_STORAGE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O +val SUPPORTS_SCOPED_STORAGE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q +val SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O val MEDIA_RECORDINGS_PREFIX = "alibi-recording-" val RECORDER_MEDIA_SELECTED_VALUE = "_'media" val RECORDER_INTERNAL_SELECTED_VALUE = "_'internal" +// TODO: Check API 24 + // You are not allowed to change the constants below. // If you do so, you will be blocked on GitHub. const val REPO_URL = "https://github.com/Myzel394/Alibi" diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 546d6462b..c24aa51a0 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -55,6 +55,7 @@ import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import app.myzel394.alibi.ui.SHEET_BOTTOM_OFFSET +import app.myzel394.alibi.ui.SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import app.myzel394.alibi.ui.components.atoms.MessageBox import app.myzel394.alibi.ui.components.atoms.MessageType @@ -256,7 +257,7 @@ fun SelectionSheet( SUPPORTS_SCOPED_STORAGE || PermissionHelper.hasGranted( context, - Manifest.permission.READ_EXTERNAL_STORAGE + Manifest.permission.WRITE_EXTERNAL_STORAGE ) ) { updateValue(RECORDER_MEDIA_SELECTED_VALUE) @@ -276,7 +277,7 @@ fun SelectionSheet( showCustomFolderWarning = true }, ) - if (!SUPPORTS_SCOPED_STORAGE) { + if (!SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS) { Column( modifier = Modifier.padding(horizontal = 32.dp, vertical = 12.dp), horizontalAlignment = Alignment.CenterHorizontally, From 298ce13369c56996101290bcb4fa321ed438b06c Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 5 Jan 2024 19:39:19 +0100 Subject: [PATCH 154/176] fix: Fix recorder for Android level 33 --- app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt | 3 +-- .../java/app/myzel394/alibi/services/VideoRecorderService.kt | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index 516279526..b89651d86 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -24,7 +24,6 @@ import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE import app.myzel394.alibi.ui.utils.PermissionHelper import com.arthenica.ffmpegkit.FFprobeKit -import com.arthenica.ffmpegkit.FFprobeSession import kotlinx.coroutines.CompletableDeferred import kotlin.reflect.KFunction4 @@ -464,7 +463,7 @@ abstract class BatchesFolder( context.contentResolver.query( scopedMediaContentUri, arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME), - "${MediaStore.MediaColumns.DISPLAY_NAME} = '$name' AND ${Media.RELATIVE_PATH} = '$relativePath'", + "${MediaStore.MediaColumns.DISPLAY_NAME} = '$name'", null, null, )!!.use { cursor -> diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index 0dd9cd2a8..bbee4faa7 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -243,6 +243,7 @@ class VideoRecorderService : ).build() ) } else if (batchesFolder.type == BatchesFolder.BatchType.MEDIA) { + // TODO: Add screen with help where the recordings are saved if (SUPPORTS_SCOPED_STORAGE) { val name = getNameForMediaFile() From 3cbf822b88d128bbb2360bd32a8ae0b1a761197b Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 5 Jan 2024 19:56:20 +0100 Subject: [PATCH 155/176] fix: Small improvements --- app/src/main/AndroidManifest.xml | 1 - .../app/myzel394/alibi/helpers/AudioBatchesFolder.kt | 6 +++--- .../java/app/myzel394/alibi/helpers/BatchesFolder.kt | 3 +++ .../app/myzel394/alibi/helpers/VideoBatchesFolder.kt | 10 ++++++---- app/src/main/java/app/myzel394/alibi/ui/Constants.kt | 2 -- .../RecorderScreen/organisms/RecorderEventsHandler.kt | 1 - 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f83478ca..87cbf80c9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,7 +26,6 @@ - viaCustomFolder(context, DocumentFile.fromTreeUri(context, Uri.parse(folder))!!) } + val BASE_LEGACY_STORAGE_FOLDER = Environment.DIRECTORY_PODCASTS val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER_NAME + "/audio_recordings" val BASE_SCOPED_STORAGE_RELATIVE_PATH = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index b89651d86..c6ab74b55 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -289,11 +289,14 @@ abstract class BatchesFolder( extension = extension, ) + // TODO: Smoother transition from start to status concatenationFunction( filePaths, outputFile, parameter ) { time -> + // The progressbar for the conversion is calculated based on the + // current time of the conversion and the total time of the batches. if (fullTime != null) { onProgress(time / fullTime!!) } else { diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index 20a69e471..8a6b22ddb 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -32,7 +32,7 @@ class VideoBatchesFolder( override val ffmpegParameters = FFMPEG_PARAMETERS override val scopedMediaContentUri: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI override val legacyMediaFolder = File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), + Environment.getExternalStoragePublicDirectory(BASE_LEGACY_STORAGE_FOLDER), MEDIA_RECORDINGS_SUBFOLDER, ) @@ -59,7 +59,7 @@ class VideoBatchesFolder( val mediaUri = getOrCreateMediaFile( name = getName(date, extension), mimeType = "video/$extension", - relativePath = Environment.DIRECTORY_DCIM + "/" + MEDIA_SUBFOLDER_NAME, + relativePath = BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_SUBFOLDER_NAME, ) return FFmpegKitConfig.getSafParameterForWrite( @@ -68,7 +68,7 @@ class VideoBatchesFolder( )!! } else { val path = arrayOf( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), + Environment.getExternalStoragePublicDirectory(BASE_LEGACY_STORAGE_FOLDER), MEDIA_SUBFOLDER_NAME, getName(date, extension) ).joinToString("/") @@ -143,9 +143,11 @@ class VideoBatchesFolder( ) } + val BASE_LEGACY_STORAGE_FOLDER = Environment.DIRECTORY_DCIM val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER_NAME + "/video_recordings" + val BASE_SCOPED_STORAGE_RELATIVE_PATH = Environment.DIRECTORY_DCIM val SCOPED_STORAGE_RELATIVE_PATH = - Environment.DIRECTORY_DCIM + "/" + MEDIA_RECORDINGS_SUBFOLDER + BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_RECORDINGS_SUBFOLDER // Parameters to be passed in descending order // Those parameters first try to concatenate without re-encoding diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index 68afb9db4..60c75bf81 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -17,8 +17,6 @@ val MEDIA_RECORDINGS_PREFIX = "alibi-recording-" val RECORDER_MEDIA_SELECTED_VALUE = "_'media" val RECORDER_INTERNAL_SELECTED_VALUE = "_'internal" -// TODO: Check API 24 - // You are not allowed to change the constants below. // If you do so, you will be blocked on GitHub. const val REPO_URL = "https://github.com/Myzel394/Alibi" diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt index 30df7eec8..8fe96b308 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt @@ -324,7 +324,6 @@ fun RecorderEventsHandler( progress = processingProgress, ) - // TODO: Add thread for concatenation if (showRecorderError) RecorderErrorDialog( onClose = { From 60ee8d9395fe7fcb2a004ff67e0469d5d12c1e64 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 5 Jan 2024 20:16:53 +0100 Subject: [PATCH 156/176] refactor: Outsource function into rememberOpenUri --- .../organisms/RecorderEventsHandler.kt | 7 ++----- .../app/myzel394/alibi/ui/effects/open-uri.kt | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/effects/open-uri.kt diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt index 8fe96b308..d01c5ada5 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/RecorderEventsHandler.kt @@ -28,6 +28,7 @@ import app.myzel394.alibi.services.IntervalRecorderService import app.myzel394.alibi.ui.components.RecorderScreen.atoms.BatchesInaccessibleDialog import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecorderErrorDialog import app.myzel394.alibi.ui.components.RecorderScreen.atoms.RecorderProcessingDialog +import app.myzel394.alibi.ui.effects.rememberOpenUri import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.models.BaseRecorderModel import app.myzel394.alibi.ui.models.VideoRecorderModel @@ -106,11 +107,7 @@ fun RecorderEventsHandler( val successMessage = stringResource(R.string.ui_recorder_action_save_success) val openMessage = stringResource(R.string.ui_recorder_action_save_openFolder) - fun openFolder(uri: Uri) { - val intent = Intent(Intent.ACTION_VIEW, uri) - - context.startActivity(intent) - } + val openFolder = rememberOpenUri() fun showSnackbar() { scope.launch { diff --git a/app/src/main/java/app/myzel394/alibi/ui/effects/open-uri.kt b/app/src/main/java/app/myzel394/alibi/ui/effects/open-uri.kt new file mode 100644 index 000000000..9e0ef365f --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/effects/open-uri.kt @@ -0,0 +1,18 @@ +package app.myzel394.alibi.ui.effects + +import android.content.Intent +import android.net.Uri +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +@Composable +fun rememberOpenUri(): (uri: Uri) -> Unit { + val context = LocalContext.current + + return fun(uri: Uri) { + val intent = Intent(Intent.ACTION_VIEW, uri) + + context.startActivity(intent) + } +} + From 986bf1d98a3ff34235317081ea7c80e6b045ac32 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 5 Jan 2024 20:17:12 +0100 Subject: [PATCH 157/176] feat: Add open folder for custom folders in SaveFolderTile --- .../SettingsScreen/Tiles/SaveFolderTile.kt | 35 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 2 files changed, 36 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index c24aa51a0..16b75470c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -50,6 +50,8 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.core.net.toUri +import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings @@ -61,6 +63,7 @@ import app.myzel394.alibi.ui.components.atoms.MessageBox import app.myzel394.alibi.ui.components.atoms.MessageType import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.components.atoms.SettingsTile +import app.myzel394.alibi.ui.effects.rememberOpenUri import app.myzel394.alibi.ui.utils.PermissionHelper import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog import kotlinx.coroutines.launch @@ -172,6 +175,38 @@ fun SaveFolderTile( textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth(), ) + + + val openFolder = rememberOpenUri() + + when (settings.saveFolder) { + null -> {} + RECORDER_MEDIA_SELECTED_VALUE -> {} + // Custom folder + else -> + Button( + onClick = { + openFolder( + DocumentFile.fromTreeUri( + context, + settings.saveFolder.toUri(), + )!!.uri + ) + }, + shape = MaterialTheme.shapes.small, + // TODO: Adjust padding everywhere + contentPadding = ButtonDefaults.TextButtonContentPadding, + colors = ButtonDefaults.filledTonalButtonColors( + contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + ), + ) { + Text( + stringResource(R.string.ui_settings_option_saveFolder_openFolder_label), + fontSize = MaterialTheme.typography.bodySmall.fontSize, + ) + } + } } } ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b8efd9efd..b373d12d9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -182,4 +182,5 @@ File Manager app not found Alibi couldn\'t find a file manager app on your phone. Please install a file manager app and try again. If this message still appears, you can try using a custom batches folder in the advanced settings section. Alibi may not fully work on your device. Recording Video + Open Folder in Files \ No newline at end of file From caeb966598e3073c6938ffdd2807ffd9ad6ef691 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 5 Jan 2024 21:57:24 +0100 Subject: [PATCH 158/176] fix: Fix batches folder concatenation --- .../java/app/myzel394/alibi/helpers/BatchesFolder.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index c6ab74b55..a665e85db 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -277,11 +277,21 @@ abstract class BatchesFolder( runCatching { // `fullTime` is not accurate as the last batch might be shorter, // but it's good enough for the progress bar + + // Using the code below results in a nasty bug: + // since we use ffmpeg to extract the duration, the saf parameter is already + // "used up" and we can't use it again for the actual concatenation + // Since an accurate progress bar is less important than speed, + // we currently don't use this code + /* val lastBatchTime = (FFprobeKit.execute( "-i ${filePaths.last()} -show_entries format=duration -v quiet -of csv=\"p=0\"", ).output.toFloat() * 1000).toLong() fullTime = ((durationPerBatchInMilliseconds * (filePaths.size - 1)) + lastBatchTime).toFloat() + */ + // We use an approximation for the duration of the batches + fullTime = (durationPerBatchInMilliseconds * filePaths.size).toFloat() } val outputFile = getOutputFileForFFmpeg( From 42f826279645df6dcc49d1f72badbf1e8ee6cb8d Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 5 Jan 2024 21:57:49 +0100 Subject: [PATCH 159/176] feat: Add explanation to SaveFolderTile.kt for DCIM folder --- .../SettingsScreen/Tiles/SaveFolderTile.kt | 167 ++++++++++++++++-- .../app/myzel394/alibi/ui/effects/open-uri.kt | 5 +- app/src/main/res/values/strings.xml | 4 + 3 files changed, 164 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 16b75470c..bd8bd0cf6 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -3,6 +3,10 @@ package app.myzel394.alibi.ui.components.SettingsScreen.Tiles import android.Manifest import android.content.Intent import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.MediaStore +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -15,10 +19,12 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material.icons.filled.Cancel import androidx.compose.material.icons.filled.Folder import androidx.compose.material.icons.filled.InsertDriveFile import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Mic import androidx.compose.material.icons.filled.PermMedia import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.AlertDialog @@ -55,10 +61,14 @@ import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.helpers.AudioBatchesFolder +import app.myzel394.alibi.helpers.VideoBatchesFolder +import app.myzel394.alibi.ui.MEDIA_SUBFOLDER_NAME import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import app.myzel394.alibi.ui.SHEET_BOTTOM_OFFSET import app.myzel394.alibi.ui.SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE +import app.myzel394.alibi.ui.components.SettingsScreen.atoms.FolderBreadcrumbs import app.myzel394.alibi.ui.components.atoms.MessageBox import app.myzel394.alibi.ui.components.atoms.MessageType import app.myzel394.alibi.ui.components.atoms.PermissionRequester @@ -129,6 +139,16 @@ fun SaveFolderTile( ) } + var showDCIMFolderHelpSheet by remember { mutableStateOf(false) } + + if (showDCIMFolderHelpSheet) { + MediaFoldersExplanationDialog( + onDismiss = { + showDCIMFolderHelpSheet = false + } + ) + } + SettingsTile( title = stringResource(R.string.ui_settings_option_saveFolder_title), description = stringResource(R.string.ui_settings_option_saveFolder_explanation), @@ -181,20 +201,12 @@ fun SaveFolderTile( when (settings.saveFolder) { null -> {} - RECORDER_MEDIA_SELECTED_VALUE -> {} - // Custom folder - else -> + RECORDER_MEDIA_SELECTED_VALUE -> { Button( onClick = { - openFolder( - DocumentFile.fromTreeUri( - context, - settings.saveFolder.toUri(), - )!!.uri - ) + showDCIMFolderHelpSheet = true }, shape = MaterialTheme.shapes.small, - // TODO: Adjust padding everywhere contentPadding = ButtonDefaults.TextButtonContentPadding, colors = ButtonDefaults.filledTonalButtonColors( contentColor = MaterialTheme.colorScheme.onTertiaryContainer, @@ -202,11 +214,144 @@ fun SaveFolderTile( ), ) { Text( - stringResource(R.string.ui_settings_option_saveFolder_openFolder_label), + stringResource(R.string.ui_settings_option_saveFolder_explainMediaFolder_label), fontSize = MaterialTheme.typography.bodySmall.fontSize, ) } + } + // Custom folder + else -> + // Doesn't seem to reliably work on all devices, 30 & 33 + // has been tested; so we just show the button for versions + // above 30 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) + Button( + onClick = { + openFolder( + DocumentFile.fromTreeUri( + context, + settings.saveFolder.toUri(), + )!!.uri + ) + }, + shape = MaterialTheme.shapes.small, + // TODO: Adjust padding everywhere + contentPadding = ButtonDefaults.TextButtonContentPadding, + colors = ButtonDefaults.filledTonalButtonColors( + contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + containerColor = MaterialTheme.colorScheme.tertiaryContainer, + ), + ) { + Text( + stringResource(R.string.ui_settings_option_saveFolder_openFolder_label), + fontSize = MaterialTheme.typography.bodySmall.fontSize, + ) + } + } + } + } + ) +} + +@Composable +fun MediaFoldersExplanationDialog( + onDismiss: () -> Unit, +) { + val context = LocalContext.current + val openFolder = rememberOpenUri() + + AlertDialog( + onDismissRequest = onDismiss, + icon = { + Icon( + Icons.Default.PermMedia, + contentDescription = null, + ) + }, + title = { + Text(stringResource(R.string.ui_settings_option_saveFolder_explainMediaFolder_label)) + }, + confirmButton = { + Button( + onClick = onDismiss, + ) { + Text(stringResource(R.string.dialog_close_neutral_label)) + } + }, + text = { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(32.dp), + ) { + Text( + stringResource(R.string.ui_settings_option_saveFolder_explainMediaFolder_generalExplanation), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurface, + ) + // I tried adding support for opening the folder directly by tapping on the + // breadcrumbs, but couldn't get it to work. + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp) + .clip(MaterialTheme.shapes.medium) + .background(MaterialTheme.colorScheme.surfaceVariant) + .padding(16.dp) + ) { + Icon( + Icons.Default.Mic, + contentDescription = null, + ) + FolderBreadcrumbs( + folders = listOf( + if (SUPPORTS_SCOPED_STORAGE) + AudioBatchesFolder.BASE_SCOPED_STORAGE_RELATIVE_PATH + else + AudioBatchesFolder.BASE_LEGACY_STORAGE_FOLDER, + MEDIA_SUBFOLDER_NAME + ) + ) + Box {} + } + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp) + .clip(MaterialTheme.shapes.medium) + .background(MaterialTheme.colorScheme.surfaceVariant) + .padding(16.dp) + ) { + Icon( + Icons.Default.CameraAlt, + contentDescription = null, + ) + FolderBreadcrumbs( + folders = listOf( + if (SUPPORTS_SCOPED_STORAGE) + VideoBatchesFolder.BASE_SCOPED_STORAGE_RELATIVE_PATH + else + VideoBatchesFolder.BASE_LEGACY_STORAGE_FOLDER, + MEDIA_SUBFOLDER_NAME + ) + ) + Box {} + } } + Text( + stringResource(R.string.ui_settings_option_saveFolder_explainMediaFolder_subfoldersExplanation), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurface, + ) } } ) diff --git a/app/src/main/java/app/myzel394/alibi/ui/effects/open-uri.kt b/app/src/main/java/app/myzel394/alibi/ui/effects/open-uri.kt index 9e0ef365f..910679a21 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/effects/open-uri.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/effects/open-uri.kt @@ -2,6 +2,7 @@ package app.myzel394.alibi.ui.effects import android.content.Intent import android.net.Uri +import android.provider.DocumentsContract import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext @@ -10,7 +11,9 @@ fun rememberOpenUri(): (uri: Uri) -> Unit { val context = LocalContext.current return fun(uri: Uri) { - val intent = Intent(Intent.ACTION_VIEW, uri) + val intent = Intent(Intent.ACTION_VIEW, uri).apply { + setDataAndType(uri, DocumentsContract.Document.MIME_TYPE_DIR) + } context.startActivity(intent) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b373d12d9..0e6cd29ac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -183,4 +183,8 @@ Alibi couldn\'t find a file manager app on your phone. Please install a file manager app and try again. If this message still appears, you can try using a custom batches folder in the advanced settings section. Alibi may not fully work on your device. Recording Video Open Folder in Files + Where are my batches stored? + To view your batches, open the Files app, go to the internal storage and then you will find your batches in following folders: + The final merged recordings will be saved in those folders. Each recording creates a subfolder to store the short batches in ("%s" for audio recordings, "%s" for video recordings). To view the individual batches, you may need to enable hidden files in the Files app. + Tap on a folder to open it in the Files app \ No newline at end of file From 300825e20fcbec6ad00d381ddef26567c482c44d Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 5 Jan 2024 21:58:29 +0100 Subject: [PATCH 160/176] feat: Add explanation to SaveFolderTile.kt for DCIM folder --- .../myzel394/alibi/helpers/AudioBatchesFolder.kt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt index 50fa1cf7a..c571ebbb4 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -30,11 +30,8 @@ class AudioBatchesFolder( ) { override val concatenationFunction = ::concatenateAudioFiles override val ffmpegParameters = FFMPEG_PARAMETERS - override val scopedMediaContentUri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI - override val legacyMediaFolder = File( - Environment.getExternalStoragePublicDirectory(BASE_LEGACY_STORAGE_FOLDER), - MEDIA_RECORDINGS_SUBFOLDER, - ) + override val scopedMediaContentUri: Uri = SCOPED_MEDIA_CONTENT_URI + override val legacyMediaFolder = LEGACY_MEDIA_FOLDER private var customFileFileDescriptor: ParcelFileDescriptor? = null private var mediaFileFileDescriptor: ParcelFileDescriptor? = null @@ -154,6 +151,15 @@ class AudioBatchesFolder( val SCOPED_STORAGE_RELATIVE_PATH = BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_RECORDINGS_SUBFOLDER + // Don't use those values directly, use the constants from the instance. + // Those values are only used inside the `SaveFolderTile` + val SCOPED_MEDIA_CONTENT_URI = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + val LEGACY_MEDIA_FOLDER = File( + Environment.getExternalStoragePublicDirectory(BASE_LEGACY_STORAGE_FOLDER), + MEDIA_RECORDINGS_SUBFOLDER, + ) + + // Parameters to be passed in descending order // Those parameters first try to concatenate without re-encoding // if that fails, it'll try several fallback methods From e6d31a0ba9f93552296fab1ac8865c24a6433c53 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 6 Jan 2024 13:54:04 +0100 Subject: [PATCH 161/176] fix: Small improvements --- .../main/java/app/myzel394/alibi/helpers/MediaConverter.kt | 6 ++++-- .../app/myzel394/alibi/services/VideoRecorderService.kt | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index d3fd4dd9f..f9cb02ca9 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -90,6 +90,8 @@ class MediaConverter { val filePathsConcatenated = inputFiles.joinToString("|") val command = "-protocol_whitelist saf,concat,content,file,subfile" + + " -safe 0" + + " -strict normal" + " -i 'concat:$filePathsConcatenated'" + extraCommand + " -y" + @@ -143,11 +145,11 @@ class MediaConverter { val command = "-protocol_whitelist saf,concat,content,file,subfile" + - " -f concat" + " -safe 0" + + " -strict normal" + + " -f concat" + " -i ${listFile.absolutePath}" + extraCommand + - " -strict normal" + " -y" + " $outputFile" diff --git a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt index bbee4faa7..0dd9cd2a8 100644 --- a/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/VideoRecorderService.kt @@ -243,7 +243,6 @@ class VideoRecorderService : ).build() ) } else if (batchesFolder.type == BatchesFolder.BatchType.MEDIA) { - // TODO: Add screen with help where the recordings are saved if (SUPPORTS_SCOPED_STORAGE) { val name = getNameForMediaFile() From cfc3572cfa9bdbb615ee46b82b976c08513a8f48 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 6 Jan 2024 16:36:35 +0100 Subject: [PATCH 162/176] fix(ui): Unify buttons --- .../alibi/helpers/AudioBatchesFolder.kt | 3 ++- .../myzel394/alibi/helpers/BatchesFolder.kt | 2 -- .../myzel394/alibi/helpers/MediaConverter.kt | 1 - .../alibi/helpers/VideoBatchesFolder.kt | 3 ++- .../java/app/myzel394/alibi/ui/AsLockedApp.kt | 5 ++-- .../java/app/myzel394/alibi/ui/Constants.kt | 3 +++ .../AboutScreen/atoms/DonationsTile.kt | 6 +++-- .../AboutScreen/atoms/GPGKeyOverview.kt | 4 +-- .../atoms/LandingElement.kt | 10 +++---- .../organisms/NotificationEditor.kt | 1 + .../atoms/BatchesInaccessibleDialog.kt | 6 ++--- .../atoms/ConfirmDeletionDialog.kt | 6 +++-- .../RecorderScreen/atoms/DeleteButton.kt | 4 +-- .../atoms/MicrophoneSelectionButton.kt | 1 + .../atoms/RecorderErrorDialog.kt | 11 +++----- .../RecorderScreen/atoms/SaveButton.kt | 4 +-- .../RecorderScreen/atoms/TorchStatus.kt | 1 + .../molecules/MicrophoneSelection.kt | 5 ++-- .../molecules/QuickMaxDurationSelector.kt | 3 ++- .../organisms/StartRecording.kt | 5 ++-- .../SettingsScreen/Tiles/ImportExport.kt | 15 ++++++----- .../SettingsScreen/Tiles/SaveFolderTile.kt | 26 ++++++++++--------- .../WelcomeScreen/atoms/ExplanationPage.kt | 1 + .../WelcomeScreen/atoms/ResponsibilityPage.kt | 1 + .../components/atoms/ExampleListRoulette.kt | 6 ++--- .../components/atoms/PermissionRequester.kt | 15 ++++++----- app/src/main/res/values/strings.xml | 2 +- 27 files changed, 80 insertions(+), 70 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt index c571ebbb4..46c50f0a7 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/AudioBatchesFolder.kt @@ -9,6 +9,7 @@ import android.provider.MediaStore import androidx.annotation.RequiresApi import androidx.documentfile.provider.DocumentFile import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateAudioFiles +import app.myzel394.alibi.ui.AUDIO_RECORDING_BATCHES_SUBFOLDER_NAME import app.myzel394.alibi.ui.MEDIA_SUBFOLDER_NAME import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE @@ -21,7 +22,7 @@ class AudioBatchesFolder( override val context: Context, override val type: BatchType, override val customFolder: DocumentFile? = null, - override val subfolderName: String = ".audio_recordings", + override val subfolderName: String = AUDIO_RECORDING_BATCHES_SUBFOLDER_NAME, ) : BatchesFolder( context, type, diff --git a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt index a665e85db..70c12f5b0 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/BatchesFolder.kt @@ -113,7 +113,6 @@ abstract class BatchesFolder( } fun getBatchesForFFmpeg(): List { - // TODO: There is probably a better way to do this iteratively, look at it if you have time return when (type) { BatchType.INTERNAL -> ((getInternalFolder() @@ -299,7 +298,6 @@ abstract class BatchesFolder( extension = extension, ) - // TODO: Smoother transition from start to status concatenationFunction( filePaths, outputFile, diff --git a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt index f9cb02ca9..988193104 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/MediaConverter.kt @@ -90,7 +90,6 @@ class MediaConverter { val filePathsConcatenated = inputFiles.joinToString("|") val command = "-protocol_whitelist saf,concat,content,file,subfile" + - " -safe 0" + " -strict normal" + " -i 'concat:$filePathsConcatenated'" + extraCommand + diff --git a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt index 8a6b22ddb..22f0201a6 100644 --- a/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt +++ b/app/src/main/java/app/myzel394/alibi/helpers/VideoBatchesFolder.kt @@ -13,6 +13,7 @@ import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles import app.myzel394.alibi.ui.MEDIA_SUBFOLDER_NAME import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE +import app.myzel394.alibi.ui.VIDEO_RECORDING_BATCHES_SUBFOLDER_NAME import com.arthenica.ffmpegkit.FFmpegKitConfig import java.io.File import java.time.LocalDateTime @@ -21,7 +22,7 @@ class VideoBatchesFolder( override val context: Context, override val type: BatchType, override val customFolder: DocumentFile? = null, - override val subfolderName: String = ".video_recordings", + override val subfolderName: String = VIDEO_RECORDING_BATCHES_SUBFOLDER_NAME, ) : BatchesFolder( context, type, diff --git a/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt b/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt index feb013e70..429b24add 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/AsLockedApp.kt @@ -15,6 +15,7 @@ import androidx.compose.material.icons.filled.Fingerprint import androidx.compose.material.icons.filled.Lock import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ElevatedButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold @@ -126,12 +127,12 @@ fun AsLockedApp( style = MaterialTheme.typography.bodyLarge, ) } - Button( + ElevatedButton( modifier = Modifier .fillMaxWidth() .height(BIG_PRIMARY_BUTTON_SIZE), onClick = ::openAuthentication, - colors = ButtonDefaults.filledTonalButtonColors(), + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, ) { Icon( Icons.Default.Lock, diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index 60c75bf81..8d7f7a9fb 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -17,6 +17,9 @@ val MEDIA_RECORDINGS_PREFIX = "alibi-recording-" val RECORDER_MEDIA_SELECTED_VALUE = "_'media" val RECORDER_INTERNAL_SELECTED_VALUE = "_'internal" +val VIDEO_RECORDING_BATCHES_SUBFOLDER_NAME = ".video_recordings" +val AUDIO_RECORDING_BATCHES_SUBFOLDER_NAME = ".audio_recordings" + // You are not allowed to change the constants below. // If you do so, you will be blocked on GitHub. const val REPO_URL = "https://github.com/Myzel394/Alibi" diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AboutScreen/atoms/DonationsTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AboutScreen/atoms/DonationsTile.kt index 1944e38b9..ad4496305 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AboutScreen/atoms/DonationsTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AboutScreen/atoms/DonationsTile.kt @@ -33,8 +33,10 @@ import androidx.compose.material.icons.filled.CurrencyYuan import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -130,11 +132,11 @@ fun DonationsTile() { Column { val uriHandler = LocalUriHandler.current - Button( + TextButton( onClick = { uriHandler.openUri(GITHUB_SPONSORS_URL) }, - colors = ButtonDefaults.textButtonColors(), + contentPadding = ButtonDefaults.TextButtonWithIconContentPadding, modifier = Modifier.fillMaxWidth(), ) { Image( diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AboutScreen/atoms/GPGKeyOverview.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AboutScreen/atoms/GPGKeyOverview.kt index 572f88891..7c8b4ca58 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AboutScreen/atoms/GPGKeyOverview.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AboutScreen/atoms/GPGKeyOverview.kt @@ -16,6 +16,7 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -62,12 +63,11 @@ fun GPGKeyOverview() { ) .padding(8.dp), ) - Button( + TextButton( onClick = { val clip = ClipData.newPlainText("text", PUBLIC_KEY) clipboardManager.setPrimaryClip(clip) }, - colors = ButtonDefaults.textButtonColors(), modifier = Modifier .fillMaxWidth() ) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt index 01c5f1e66..a272129b0 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt @@ -19,9 +19,11 @@ import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Notifications import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -82,10 +84,7 @@ fun LandingElement( stringResource(R.string.ui_settings_customNotifications_landing_description), style = MaterialTheme.typography.bodySmall, ) - Button( - onClick = onOpenEditor, - colors = ButtonDefaults.filledTonalButtonColors(), - ) { + FilledTonalButton(onClick = onOpenEditor) { Icon( Icons.Default.Edit, contentDescription = null, @@ -100,9 +99,8 @@ fun LandingElement( } } } - Button( + TextButton( onClick = context::openNotificationsSettings, - colors = ButtonDefaults.textButtonColors(), ) { Text( stringResource(R.string.ui_settings_customNotifications_landing_help_hideNotifications), diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt index 8134073ea..6a4bd0e83 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt @@ -178,6 +178,7 @@ fun NotificationEditor( notificationModel.asNotificationSettings() ) }, + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, modifier = Modifier .fillMaxWidth() .padding(horizontal = HORIZONTAL_PADDING) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt index ebd9bb29c..034ab14c4 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BatchesInaccessibleDialog.kt @@ -7,6 +7,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import app.myzel394.alibi.R @@ -30,10 +31,7 @@ fun BatchesInaccessibleDialog( Text(stringResource(R.string.ui_recorder_error_batchesInaccessible_description)) }, confirmButton = { - Button( - onClick = onClose, - colors = ButtonDefaults.textButtonColors(), - ) { + TextButton(onClick = onClose) { Text(stringResource(R.string.dialog_close_neutral_label)) } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/ConfirmDeletionDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/ConfirmDeletionDialog.kt index a060ade81..a8bad8791 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/ConfirmDeletionDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/ConfirmDeletionDialog.kt @@ -11,6 +11,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -46,6 +47,7 @@ fun ConfirmDeletionDialog( .semantics { contentDescription = label }, + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, onClick = { onConfirm() }, @@ -61,15 +63,15 @@ fun ConfirmDeletionDialog( }, dismissButton = { val label = stringResource(R.string.dialog_close_cancel_label) - Button( + TextButton( modifier = Modifier .semantics { contentDescription = label }, + contentPadding = ButtonDefaults.TextButtonWithIconContentPadding, onClick = { onDismiss() }, - colors = ButtonDefaults.textButtonColors(), ) { Icon( Icons.Default.Cancel, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/DeleteButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/DeleteButton.kt index 70a0a863e..e0180cde9 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/DeleteButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/DeleteButton.kt @@ -4,6 +4,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -34,7 +35,7 @@ fun DeleteButton( ) } val label = stringResource(R.string.ui_recorder_action_delete_label) - Button( + TextButton( modifier = Modifier .semantics { contentDescription = label @@ -43,7 +44,6 @@ fun DeleteButton( onClick = { showDeleteDialog = true }, - colors = ButtonDefaults.textButtonColors(), ) { Text( label, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneSelectionButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneSelectionButton.kt index 35500132f..8ce21dd1a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneSelectionButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/MicrophoneSelectionButton.kt @@ -50,6 +50,7 @@ fun MicrophoneSelectionButton( .fillMaxWidth() .height(64.dp), colors = if (selected) ButtonDefaults.buttonColors() else ButtonDefaults.textButtonColors(), + contentPadding = if (selected) ButtonDefaults.ButtonWithIconContentPadding else ButtonDefaults.TextButtonContentPadding, ) { Row( verticalAlignment = Alignment.CenterVertically, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt index 4f3031fd2..92da6195c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderErrorDialog.kt @@ -7,6 +7,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import app.myzel394.alibi.R @@ -31,18 +32,12 @@ fun RecorderErrorDialog( Text(stringResource(R.string.ui_recorder_error_recording_description)) }, dismissButton = { - Button( - onClick = onClose, - colors = ButtonDefaults.textButtonColors(), - ) { + TextButton(onClick = onClose) { Text(stringResource(R.string.dialog_close_cancel_label)) } }, confirmButton = { - Button( - onClick = onSave, - colors = ButtonDefaults.textButtonColors(), - ) { + TextButton(onClick = onSave) { Text(stringResource(R.string.ui_recorder_action_save_label)) } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt index 8dcb8752e..28d960da9 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/SaveButton.kt @@ -4,6 +4,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -18,14 +19,13 @@ fun SaveButton( ) { val label = stringResource(R.string.ui_recorder_action_save_label) - Button( + TextButton( modifier = Modifier .semantics { contentDescription = label } .then(modifier), onClick = onSave, - colors = ButtonDefaults.textButtonColors(), ) { Text( label, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt index cc240469f..1a50bb16e 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/TorchStatus.kt @@ -23,6 +23,7 @@ fun TorchStatus( Button( onClick = onChange, colors = if (enabled) ButtonDefaults.filledTonalButtonColors() else ButtonDefaults.outlinedButtonColors(), + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, ) { Icon( if (enabled) Icons.Default.FlashlightOff else Icons.Default.FlashlightOn, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt index 1ec491520..b82ba4187 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/MicrophoneSelection.kt @@ -19,6 +19,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -188,14 +189,14 @@ fun MicrophoneSelection( } if (shownMicrophones.isNotEmpty() || (settings.audioRecorderSettings.showAllMicrophones && hiddenMicrophones.isNotEmpty())) { - Button( + TextButton( onClick = { scope.launch { showSelection = true sheetState.show() } }, - colors = ButtonDefaults.textButtonColors(), + contentPadding = ButtonDefaults.TextButtonWithIconContentPadding, ) { MicrophoneTypeInfo( type = audioRecorder.selectedMicrophone?.type diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt index d494a1e48..46434dc16 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/QuickMaxDurationSelector.kt @@ -14,6 +14,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope @@ -66,7 +67,7 @@ fun QuickMaxDurationSelector( Column { for (duration in EXAMPLE_MAX_DURATIONS) { - Button( + TextButton( onClick = { scope.launch { sheetState.hide() diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt index a3dbea470..4f54a7094 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt @@ -20,6 +20,7 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -129,7 +130,7 @@ fun StartRecording( DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL) .format(appSettings.lastRecording.recordingStart), ) - Button( + TextButton( modifier = Modifier .padding(16.dp) .fillMaxWidth() @@ -137,8 +138,8 @@ fun StartRecording( .semantics { contentDescription = label }, - colors = ButtonDefaults.textButtonColors(), onClick = onSaveLastRecording, + contentPadding = ButtonDefaults.TextButtonWithIconContentPadding, ) { Icon( Icons.Default.Save, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/ImportExport.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/ImportExport.kt index f0d78031a..6ab7b6dc5 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/ImportExport.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/ImportExport.kt @@ -12,10 +12,12 @@ import androidx.compose.material.icons.filled.Upload import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -96,8 +98,8 @@ fun ImportExport( duration = SnackbarDuration.Short, ) } - }, + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, ) { Icon( Icons.Default.CheckCircle, @@ -109,11 +111,10 @@ fun ImportExport( } }, dismissButton = { - Button( + TextButton( onClick = { settingsToBeImported = null }, - colors = ButtonDefaults.textButtonColors(), ) { Text(stringResource(R.string.dialog_close_cancel_label)) } @@ -126,11 +127,11 @@ fun ImportExport( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth(), ) { - Button( + FilledTonalButton( onClick = { openFile("application/json") }, - colors = ButtonDefaults.filledTonalButtonColors(), + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, ) { Icon( Icons.Default.Download, @@ -140,7 +141,7 @@ fun ImportExport( Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing)) Text(stringResource(R.string.ui_settings_option_import_label)) } - Button( + FilledTonalButton( onClick = { val rawContent = settings.exportToString() @@ -149,7 +150,7 @@ fun ImportExport( saveFile(tempFile, "alibi_settings.json") }, - colors = ButtonDefaults.filledTonalButtonColors(), + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, ) { Icon( Icons.Default.Upload, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index bd8bd0cf6..5245aa26c 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -39,6 +39,7 @@ import androidx.compose.material3.SheetState import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -63,11 +64,13 @@ import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.helpers.AudioBatchesFolder import app.myzel394.alibi.helpers.VideoBatchesFolder +import app.myzel394.alibi.ui.AUDIO_RECORDING_BATCHES_SUBFOLDER_NAME import app.myzel394.alibi.ui.MEDIA_SUBFOLDER_NAME import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE import app.myzel394.alibi.ui.SHEET_BOTTOM_OFFSET import app.myzel394.alibi.ui.SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE +import app.myzel394.alibi.ui.VIDEO_RECORDING_BATCHES_SUBFOLDER_NAME import app.myzel394.alibi.ui.components.SettingsScreen.atoms.FolderBreadcrumbs import app.myzel394.alibi.ui.components.atoms.MessageBox import app.myzel394.alibi.ui.components.atoms.MessageType @@ -235,7 +238,6 @@ fun SaveFolderTile( ) }, shape = MaterialTheme.shapes.small, - // TODO: Adjust padding everywhere contentPadding = ButtonDefaults.TextButtonContentPadding, colors = ButtonDefaults.filledTonalButtonColors( contentColor = MaterialTheme.colorScheme.onTertiaryContainer, @@ -272,9 +274,7 @@ fun MediaFoldersExplanationDialog( Text(stringResource(R.string.ui_settings_option_saveFolder_explainMediaFolder_label)) }, confirmButton = { - Button( - onClick = onDismiss, - ) { + Button(onClick = onDismiss) { Text(stringResource(R.string.dialog_close_neutral_label)) } }, @@ -348,7 +348,11 @@ fun MediaFoldersExplanationDialog( } } Text( - stringResource(R.string.ui_settings_option_saveFolder_explainMediaFolder_subfoldersExplanation), + stringResource( + R.string.ui_settings_option_saveFolder_explainMediaFolder_subfoldersExplanation, + AUDIO_RECORDING_BATCHES_SUBFOLDER_NAME, + VIDEO_RECORDING_BATCHES_SUBFOLDER_NAME + ), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurface, ) @@ -533,18 +537,16 @@ fun CustomFolderWarningDialog( Text(text = text) }, confirmButton = { - Button( - onClick = onConfirm, - ) { + Button(onClick = onConfirm) { Text( text = stringResource(R.string.ui_settings_option_saveFolder_warning_action_confirm), ) } }, dismissButton = { - Button( + TextButton( onClick = onDismiss, - colors = ButtonDefaults.textButtonColors(), + contentPadding = ButtonDefaults.TextButtonWithIconContentPadding, ) { Icon( Icons.Default.Cancel, @@ -596,9 +598,9 @@ fun ExternalPermissionRequiredDialog( } }, dismissButton = { - Button( + TextButton( onClick = onDismiss, - colors = ButtonDefaults.textButtonColors(), + contentPadding = ButtonDefaults.TextButtonWithIconContentPadding, ) { Icon( Icons.Default.Cancel, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/ExplanationPage.kt b/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/ExplanationPage.kt index 0cf5a1bc0..70051fe9d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/ExplanationPage.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/ExplanationPage.kt @@ -64,6 +64,7 @@ fun ExplanationPage( .padding(16.dp) .fillMaxWidth() .height(BIG_PRIMARY_BUTTON_SIZE), + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, ) { Icon( Icons.Default.ChevronRight, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/ResponsibilityPage.kt b/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/ResponsibilityPage.kt index 377d24f55..3b2b526f1 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/ResponsibilityPage.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/WelcomeScreen/atoms/ResponsibilityPage.kt @@ -64,6 +64,7 @@ fun ResponsibilityPage( .padding(16.dp) .fillMaxWidth() .height(BIG_PRIMARY_BUTTON_SIZE), + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, ) { Icon( Icons.Default.Check, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/ExampleListRoulette.kt b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/ExampleListRoulette.kt index 0e837f0fd..c3e69e3f1 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/ExampleListRoulette.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/ExampleListRoulette.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.LazyRow import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -27,13 +28,10 @@ fun ExampleListRoulette( items(items.size) { val item = items[it] - Button( + TextButton( onClick = { onItemSelected(item) }, - colors = ButtonDefaults.textButtonColors(), - shape = ButtonDefaults.textShape, - contentPadding = ButtonDefaults.TextButtonContentPadding, ) { renderValue(item) } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/PermissionRequester.kt b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/PermissionRequester.kt index 7e98f79cd..a493c02a6 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/PermissionRequester.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/PermissionRequester.kt @@ -18,6 +18,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf @@ -109,7 +110,8 @@ fun PermissionRequester( onClick = { visibleDialog = null callback() - } + }, + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, ) { Icon( Icons.Default.Check, @@ -121,11 +123,11 @@ fun PermissionRequester( } }, dismissButton = { - Button( + TextButton( onClick = { visibleDialog = null }, - colors = ButtonDefaults.textButtonColors(), + contentPadding = ButtonDefaults.TextButtonWithIconContentPadding, ) { Icon( Icons.Default.Cancel, @@ -167,7 +169,8 @@ fun PermissionRequester( onClick = { visibleDialog = null context.openAppSystemSettings() - } + }, + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, ) { Icon( Icons.Default.OpenInNew, @@ -179,11 +182,11 @@ fun PermissionRequester( } }, dismissButton = { - Button( + TextButton( onClick = { visibleDialog = null }, - colors = ButtonDefaults.textButtonColors(), + contentPadding = ButtonDefaults.TextButtonWithIconContentPadding, ) { Icon( Icons.Default.Cancel, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0e6cd29ac..f8173a791 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -185,6 +185,6 @@ Open Folder in Files Where are my batches stored? To view your batches, open the Files app, go to the internal storage and then you will find your batches in following folders: - The final merged recordings will be saved in those folders. Each recording creates a subfolder to store the short batches in ("%s" for audio recordings, "%s" for video recordings). To view the individual batches, you may need to enable hidden files in the Files app. + The final merged recordings will be saved in those folders. Each recording creates a subfolder to store the short batches in (\"%s\" for audio recordings, \"%s\" for video recordings). To view the individual batches, you may need to enable hidden files in the Files app. Tap on a folder to open it in the Files app \ No newline at end of file From e9eb7089a9f93e67684a6bcad11216a1116d3131 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 6 Jan 2024 16:56:37 +0100 Subject: [PATCH 163/176] fix: Keep screen on when processing --- .../components/RecorderScreen/atoms/RecorderProcessingDialog.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt index 25f8ce75c..fe5297a44 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RecorderProcessingDialog.kt @@ -15,11 +15,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import app.myzel394.alibi.R +import app.myzel394.alibi.ui.utils.KeepScreenOn @Composable fun RecorderProcessingDialog( progress: Float?, ) { + KeepScreenOn() AlertDialog( onDismissRequest = { }, icon = { From f06264072e925373d32920698727c76b80ecf3d5 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 6 Jan 2024 16:56:52 +0100 Subject: [PATCH 164/176] fix: Remove padding so button is visible on smaller devices --- .../ui/components/RecorderScreen/organisms/StartRecording.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt index 4f54a7094..575f22cec 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/StartRecording.kt @@ -132,7 +132,6 @@ fun StartRecording( ) TextButton( modifier = Modifier - .padding(16.dp) .fillMaxWidth() .height(BIG_PRIMARY_BUTTON_SIZE) .semantics { From 20e79b9c834aae5d14c3c165bf2a62dbded250e1 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 6 Jan 2024 16:57:16 +0100 Subject: [PATCH 165/176] fix: Only show preview message if camera permission granted --- .../molecules/VideoRecorderPreparationSheet.kt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index e8061eef3..4f7074b27 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -58,6 +58,7 @@ import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.effects.rememberPrevious import app.myzel394.alibi.ui.models.VideoRecorderModel import app.myzel394.alibi.ui.utils.CameraInfo +import app.myzel394.alibi.ui.utils.PermissionHelper import kotlin.math.abs @OptIn( @@ -209,13 +210,16 @@ fun VideoRecorderPreparationSheet( ) } } - Text( - stringResource( - R.string.ui_videoRecorder_action_preview_label - ), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) + + if (PermissionHelper.hasGranted(context, Manifest.permission.CAMERA)) { + Text( + stringResource( + R.string.ui_videoRecorder_action_preview_label + ), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } } } } From c0b165160812f5b578e360e3724c8db23ff200b9 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 6 Jan 2024 17:35:15 +0100 Subject: [PATCH 166/176] fix: Make recorder start screen more adaptive --- .../RecorderScreen/atoms/BigButton.kt | 86 ++++++++ .../molecules/AudioRecordingStart.kt | 32 +-- .../VideoRecorderPreparationSheet.kt | 200 +++++++++--------- .../molecules/VideoRecordingStart.kt | 91 +++----- 4 files changed, 225 insertions(+), 184 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BigButton.kt diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BigButton.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BigButton.kt new file mode 100644 index 000000000..e2f9c3f08 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/BigButton.kt @@ -0,0 +1,86 @@ +package app.myzel394.alibi.ui.components.RecorderScreen.atoms + +import android.Manifest +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Mic +import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.dp +import app.myzel394.alibi.R +import app.myzel394.alibi.ui.utils.PermissionHelper + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun BigButton( + label: String, + icon: ImageVector, + description: String? = null, + onClick: () -> Unit, + onLongClick: () -> Unit = {}, +) { + BoxWithConstraints { + val isLarge = maxWidth > 500.dp + + Column( + modifier = Modifier + .size(if (isLarge) 250.dp else 200.dp) + .clip(CircleShape) + .semantics { + contentDescription = label + } + .combinedClickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberRipple(color = MaterialTheme.colorScheme.primary), + onClick = onClick, + onLongClick = onLongClick, + ), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Icon( + icon, + contentDescription = null, + modifier = Modifier + .size(if (isLarge) 80.dp else 60.dp), + tint = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + label, + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + ) + if (description != null) { + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + description, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt index 58a755198..c5612c2c1 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/AudioRecordingStart.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.unit.dp import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.BigButton import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.models.AudioRecorderModel import app.myzel394.alibi.ui.utils.PermissionHelper @@ -77,34 +78,11 @@ fun AudioRecordingStart( } } ) { triggerRecordAudio -> - val label = stringResource(R.string.ui_audioRecorder_action_start_label) - - Button( + BigButton( + label = stringResource(R.string.ui_audioRecorder_action_start_label), + icon = Icons.Default.Mic, onClick = triggerRecordAudio, - modifier = Modifier - .semantics { - contentDescription = label - } - .size(250.dp) - .clip(shape = CircleShape), - colors = ButtonDefaults.outlinedButtonColors(), - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Icon( - Icons.Default.Mic, - contentDescription = null, - modifier = Modifier - .size(80.dp), - ) - Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) - Text( - label, - style = MaterialTheme.typography.titleSmall, - ) - } - } + ) } } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt index 4f7074b27..aaacbfb81 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecorderPreparationSheet.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight @@ -16,7 +17,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material.icons.filled.Mic @@ -113,112 +116,119 @@ fun VideoRecorderPreparationSheet( cameraSelector = videoSettings.cameraSelector, ) } else { - Column( - modifier = Modifier - .padding(horizontal = 16.dp) - .padding(bottom = SHEET_BOTTOM_OFFSET, top = 24.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(30.dp), - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - Icon( - Icons.Default.CameraAlt, - contentDescription = null, - modifier = Modifier - .size(80.dp), - ) - Text( - stringResource(R.string.ui_videoRecorder_action_start_settings_label), - style = MaterialTheme.typography.labelLarge, - ) - } - PermissionRequester( - permission = Manifest.permission.RECORD_AUDIO, - icon = Icons.Default.Mic, - onPermissionAvailable = { - videoSettings.enableAudio = !videoSettings.enableAudio - }, - ) { trigger -> - GlobalSwitch( - label = stringResource(R.string.ui_videoRecorder_action_start_settings_enableAudio_label), - checked = videoSettings.enableAudio, - onCheckedChange = { - trigger() - } - ) - } - - Column( - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - Text( - stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_selection_label), - style = MaterialTheme.typography.labelLarge, - textAlign = TextAlign.Start, - modifier = Modifier.fillMaxWidth() - ) - CamerasSelection( - cameras = cameras, - videoSettings = videoSettings, - ) - } - - val label = - stringResource(R.string.ui_videoRecorder_action_start_settings_start_label) + BoxWithConstraints { + val constraints = this Column( - verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(bottom = SHEET_BOTTOM_OFFSET, top = 24.dp) + .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(30.dp), ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + if (constraints.maxHeight > 600.dp) { + Icon( + Icons.Default.CameraAlt, + contentDescription = null, + modifier = Modifier + .size(80.dp), + ) + } + Text( + stringResource(R.string.ui_videoRecorder_action_start_settings_label), + style = MaterialTheme.typography.labelLarge, + ) + } PermissionRequester( - permission = Manifest.permission.CAMERA, - icon = Icons.Default.CameraAlt, + permission = Manifest.permission.RECORD_AUDIO, + icon = Icons.Default.Mic, onPermissionAvailable = { - onStartRecording() - } + videoSettings.enableAudio = !videoSettings.enableAudio + }, ) { trigger -> - Row( - modifier = Modifier - .fillMaxWidth() - .height(BIG_PRIMARY_BUTTON_SIZE) - .clip(CircleShape) - .background(MaterialTheme.colorScheme.primary) - .padding(16.dp) - .semantics { - contentDescription = label - } - .pointerInput(Unit) { - detectTapGestures( - onLongPress = { - onPreviewVisible() - }, - onTap = { - trigger() - } - ) - }, - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - label, - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onPrimary, - ) - } + GlobalSwitch( + label = stringResource(R.string.ui_videoRecorder_action_start_settings_enableAudio_label), + checked = videoSettings.enableAudio, + onCheckedChange = { + trigger() + } + ) } - if (PermissionHelper.hasGranted(context, Manifest.permission.CAMERA)) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { Text( - stringResource( - R.string.ui_videoRecorder_action_preview_label - ), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, + stringResource(R.string.ui_videoRecorder_action_start_settings_cameraLens_selection_label), + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Start, + modifier = Modifier.fillMaxWidth() ) + CamerasSelection( + cameras = cameras, + videoSettings = videoSettings, + ) + } + + val label = + stringResource(R.string.ui_videoRecorder_action_start_settings_start_label) + + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + PermissionRequester( + permission = Manifest.permission.CAMERA, + icon = Icons.Default.CameraAlt, + onPermissionAvailable = { + onStartRecording() + } + ) { trigger -> + Row( + modifier = Modifier + .fillMaxWidth() + .height(BIG_PRIMARY_BUTTON_SIZE) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.primary) + .padding(16.dp) + .semantics { + contentDescription = label + } + .pointerInput(Unit) { + detectTapGestures( + onLongPress = { + onPreviewVisible() + }, + onTap = { + trigger() + } + ) + }, + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + label, + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onPrimary, + ) + } + } + + if (PermissionHelper.hasGranted(context, Manifest.permission.CAMERA)) { + Text( + stringResource( + R.string.ui_videoRecorder_action_preview_label + ), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } } } } diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt index 97160292e..d6acb1e0b 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/molecules/VideoRecordingStart.kt @@ -46,6 +46,7 @@ import app.myzel394.alibi.R import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE +import app.myzel394.alibi.ui.components.RecorderScreen.atoms.BigButton import app.myzel394.alibi.ui.components.atoms.PermissionRequester import app.myzel394.alibi.ui.models.VideoRecorderModel import app.myzel394.alibi.ui.utils.PermissionHelper @@ -87,71 +88,37 @@ fun VideoRecordingStart( showSheet = true } ) { triggerExternalStorage -> - PermissionRequester( - permission = Manifest.permission.CAMERA, + BigButton( + label = stringResource(R.string.ui_videoRecorder_action_start_label), + description = stringResource(R.string.ui_videoRecorder_action_configure_label), icon = Icons.Default.CameraAlt, - onPermissionAvailable = { + onLongClick = { + if (appSettings.requiresExternalStoragePermission(context)) { + triggerExternalStorage() + return@BigButton + } + showSheet = true }, - ) { triggerCamera -> - val label = stringResource(R.string.ui_videoRecorder_action_start_label) - - Column( - modifier = Modifier - .size(250.dp) - .clip(CircleShape) - .semantics { - contentDescription = label - } - .combinedClickable( - interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(color = MaterialTheme.colorScheme.primary), - onClick = { - if (appSettings.requiresExternalStoragePermission(context)) { - triggerExternalStorage() - return@combinedClickable - } + onClick = { + if (appSettings.requiresExternalStoragePermission(context)) { + triggerExternalStorage() + return@BigButton + } - if (PermissionHelper.hasGranted( - context, - Manifest.permission.CAMERA - ) && PermissionHelper.hasGranted( - context, - Manifest.permission.RECORD_AUDIO - ) - ) { - videoRecorder.startRecording(context, appSettings) - } else { - showSheet = true - } - }, - onLongClick = { - showSheet = true - }, - ), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { - Icon( - Icons.Default.CameraAlt, - contentDescription = null, - modifier = Modifier - .size(80.dp), - tint = MaterialTheme.colorScheme.primary, - ) - Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) - Text( - label, - style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.primary, - ) - Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) - Text( - stringResource(R.string.ui_videoRecorder_action_configure_label), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - } + if (PermissionHelper.hasGranted( + context, + Manifest.permission.CAMERA + ) && PermissionHelper.hasGranted( + context, + Manifest.permission.RECORD_AUDIO + ) + ) { + videoRecorder.startRecording(context, appSettings) + } else { + showSheet = true + } + }, + ) } } \ No newline at end of file From be2a1b97850a50b68ca50477b90750c3cf255621 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 6 Jan 2024 17:35:29 +0100 Subject: [PATCH 167/176] fix: Make SaveFolderTile.kt scrollable --- .../ui/components/SettingsScreen/Tiles/SaveFolderTile.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt index 5245aa26c..30c09fb74 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/Tiles/SaveFolderTile.kt @@ -18,6 +18,8 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraAlt import androidx.compose.material.icons.filled.Cancel @@ -413,7 +415,8 @@ fun SelectionSheet( Column( modifier = Modifier .padding(horizontal = 16.dp) - .padding(bottom = SHEET_BOTTOM_OFFSET), + .padding(bottom = SHEET_BOTTOM_OFFSET) + .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(24.dp), ) { From e8aa6ccc950fe475fefac92c6856819ae7e8ce3d Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 6 Jan 2024 17:39:24 +0100 Subject: [PATCH 168/176] fix: Make RealtimeAudioVisualizer size more adaptive --- .../RecorderScreen/atoms/RealTimeAudioVisualizer.kt | 9 ++------- .../RecorderScreen/organisms/AudioRecordingStatus.kt | 9 ++++++++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RealTimeAudioVisualizer.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RealTimeAudioVisualizer.kt index 52cb54276..e1bd20fc3 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RealTimeAudioVisualizer.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/atoms/RealTimeAudioVisualizer.kt @@ -4,8 +4,6 @@ import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.Canvas -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -34,6 +32,7 @@ private const val GROW_END = BOX_DIFF * 4 @Composable fun RealtimeAudioVisualizer( + modifier: Modifier = Modifier, audioRecorder: AudioRecorderModel, ) { val scope = rememberCoroutineScope() @@ -69,11 +68,7 @@ fun RealtimeAudioVisualizer( audioRecorder.setMaxAmplitudesAmount(ceil(screenWidth.toInt() / BOX_DIFF).toInt() + 1) } - Canvas( - modifier = Modifier - .fillMaxWidth() - .height(300.dp), - ) { + Canvas(modifier = modifier) { val height = this.size.height / 2f val width = this.size.width diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt index eb93d46b1..3ca5f4d42 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/RecorderScreen/organisms/AudioRecordingStatus.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn import androidx.compose.material3.Divider import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -53,7 +54,13 @@ fun AudioRecordingStatus( verticalArrangement = Arrangement.SpaceBetween, ) { Box {} - RealtimeAudioVisualizer(audioRecorder = audioRecorder) + RealtimeAudioVisualizer( + audioRecorder = audioRecorder, + modifier = Modifier + .fillMaxSize() + .widthIn(max = 300.dp) + .weight(1f), + ) RecordingStatus( recordingTime = audioRecorder.recordingTime, From afd114fd2b0c1eb7a09b599fb9506493f467bccf Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 6 Jan 2024 17:53:26 +0100 Subject: [PATCH 169/176] fix: Improve AboutScreen --- .../main/java/app/myzel394/alibi/ui/screens/AboutScreen.kt | 5 +++++ app/src/main/res/values/strings.xml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/AboutScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/AboutScreen.kt index 8082fe5ff..c6faee1fa 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/AboutScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/AboutScreen.kt @@ -1,5 +1,6 @@ package app.myzel394.alibi.ui.screens +import android.widget.Space import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -7,9 +8,11 @@ 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.Spacer 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.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll @@ -150,6 +153,7 @@ fun AboutScreen( contentDescription = null, modifier = Modifier.size(ButtonDefaults.IconSize.times(1.2f)) ) + Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Text( stringResource(R.string.ui_about_contribute_development), fontWeight = FontWeight.Bold, @@ -186,6 +190,7 @@ fun AboutScreen( contentDescription = null, modifier = Modifier.size(ButtonDefaults.IconSize.times(1.2f)) ) + Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) Text( stringResource(R.string.ui_about_contribute_translation), fontWeight = FontWeight.Bold, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f8173a791..1536b9b9f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -109,7 +109,7 @@ About Alibi Support Alibi In my free time I develop Alibi and other free open source software. It would mean the world to me if you could help me in any way :) - Developing features or fixing bugs + Develop new features or fix bugs Open link in browser: %s Translate Alibi into your language Make a donation From 54a608ad71bec9ac3b9fa63abffc97b3684e15c1 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 7 Jan 2024 18:51:00 +0100 Subject: [PATCH 170/176] chore(fastlane): Update images --- .../en-US/images/phoneScreenshots/01.png | Bin 644674 -> 775268 bytes .../en-US/images/phoneScreenshots/02.png | Bin 1095939 -> 699936 bytes .../en-US/images/phoneScreenshots/03.png | Bin 1392051 -> 1467672 bytes .../en-US/images/phoneScreenshots/04.png | Bin 598325 -> 838969 bytes .../en-US/images/phoneScreenshots/05.png | Bin 0 -> 1126979 bytes .../en-US/images/phoneScreenshots/06.png | Bin 0 -> 503184 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 fastlane/metadata/android/en-US/images/phoneScreenshots/05.png create mode 100644 fastlane/metadata/android/en-US/images/phoneScreenshots/06.png diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/01.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/01.png index 17f71ee8c8b3c8d858feb49359705d3f6faa241b..8d304beab11e9a8db0f7dc3984bccffc6a83b3e9 100644 GIT binary patch literal 775268 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≶Ms{E=A*TD6qzf6l+vHd=~~7L@l@*| zEVnI}GJ~XVk0^oDM2oVPqa636NfP~afN(X&iu)3{a74ojzeNS;C|MXz!T_UC7L~xw z)GjE(zv2(F7Tr+BUyfKG$ii%WBnqPvXV4niHpDy{k?^5R7(bDXE9*7EN};Qb=_UJv+ zDU5r8gA7|MW(S%e;c_a2(jvG@DvT?KsjhIs6;=~R`RJ?*+Kn{O4b%~Pd)ZUQ?GNHMPv$c!qHT1=QU!PU`ic@5 zeBmG`KRk@@YnGeh6H)xOzG(fBEb#50IhJ|JtJ5$EU}wM1>u>XQA$j5*fCoj%)id79 zb5pSaqbV@l1wTJ9fMJwsdT*4J2&U> zo)*+7C`WWlAXyf3h#wH3S&j^q`8%cq`W^ywo2&bajcmYj5>qE^RlwV^a;lXZ;8>f= z+rGW4t5GCpfB2vNAw0%o&}=9lOh6Ch{X9^h%WCI zIj;7Zl|m7Q-OJ`JtY8DR!T!!GlBeXoC8%AglNMbjFM!dP$p>5KaZ_D-f<#EX+6p+z zcU9sozGI$7Z7_zlEdR>2n2lxt+sB~}aLbQPbz?wNaP;Np+8LZ$gp)0;=$E}${!-5~ zfYuY|hUjX8$pd#0fT!(J0W3at)PjQZqF=IGr09#Q?})^=%%1f9`3bm0H{IQ$ghKHU z6b>^!j_LDim$&@pO-^iv`ew&O+)jDirI#-A5ws_gs*mm z98rC?!ESU!sHfy8I+V#B=@Qp6>ti(txYykXfFi-8RkjS)vq5XRi;%PJ3>AMZpfObF z_;~SG0nHq7M!R%OEY+_eH?=biR3jk(z7WqLAeRZ6{WYdU$I#%#g>E|q2&gQ;b;V?| zdHq=()%@vWJ;19u(9vPQi8Gp>fmvMj6bydM4CyZSo{`vrijPQE;Nr^l?0mdz-9Hid zRp0c+;?$!E18%SVVpeII*xQkgW-tN2CIaj``}OkAw;HfR=U8;dbUmXSFjV?xXoNCziZ(@FG+sXv$u0Afb?e5?3x z>9r4(J-MhigGlqa?TZx%T8W@W=2&V6Sps79ZsFd3^tpve-a?ycn}t|0@Tp8e6X08A zB2owqH=_2Lxq}#pHg!vDFV9_KVbMEKE5cm8$04T1W#aJ3-|IA9Q&{muo<}APmcH)` z+MNke8}kO?BiC6=_eLHS<5VJANQ|*sAlyH{bp~x0cK~DL zDwN33QggCpIlav$ULL@%aY~XrqxdrZd$i(CxHrEF_l+_zI!uaGp^V+YbOa21eFs4b ziXy%HzWe|95NG4-}!;#u|<~C z2ts8%C{7v#lc#YGHJI)UJ`31NxR-lE!i$$+sKvMOgnRa9C5h}P=9!DzIfx$DftmCyGfW6o;vALf^S#GBTo$V>KO-q0z*Q%glfZe&c8HMi1T;czUcWE ziWry^q~lXd)d^|$d#J&WA59ccv4533$JI+l09NjKzzJGKUle+c43s?*$WZf z?H=i1e95U7z&2FEjg|z?FF^T0498bBcJS}sY~agj8h}zLTzSS3BtOGzhi1IeO*$EC z%3s@cde7gTNKf4Ujy-i{QWD2DSM`k}kmU#UMTIs?>V45~NW4bYB}t9m0N53h8(e#o z3weAjL3m&3o`{WPxZ;HI^LQ5#bW~3N6D%zID4hzG=+lJK2;T{%3|;*}4_t-g>J!ih zbNOh#@TK=>j*6#ZuhmQaQvKmz>Ph%e>jD8@TxlMqH z+P5y8w#OskDytk4fNme~q}Nv8Jo*?kM3^i0UKB4lu=0FKK=+u?tBYPP*Fg|r@T_zm zCK+gL8Va_&seOWTadGvEX0`ntETd(V&qB#X-Uylwzd7W8HCJj{x$>=4=sgpq^o}5y z#DI;!!QqX$EIh@;&IsOuzf1=eF5eIXKJd)y?n-~`pws>(GU`ahT+8Td-^Bk`QaAPW zX#52RHubgQ_uQsgMQcLT*orbJ7?#4#VaHizK1djX1G~;NiQ+&P(Qs=x3t-~u%7f^s?%`;mz&NqM>GS74?8ZJ%9Q0v!#2d>il)AtHZ-1t z{cF_}(|`7Zpg>jPO=3M0gRq*9kFwIgn9?HE`7Rec0Np!NKmTayZ7NrQ#A@fb8COZ? zZJp&|7iYsr6^wyle0~LgvXWO0BVx+F{;nNbfX4b;Ut(2`DuyHrO^H_V<@W6UZ1u9 zRm5*=DE*GktGZlwmHA}tt=*Fy15v==^*)BcKg-||;wXm4xke8uy*(AxXHvAi0|OS!jC=AdBo@Uz5Nm?HYR z^i^}L@)<_*#a-@XyzAzayJL7rddZ6=`VUWVHYhSfFG8&{v>^Qh6HKJo$U#z4>Pfx% zl?h-cG-*c51szakRSE~8Lm$Xc*B;D82yb9%kk`_4MiATnw?8c;tnJEYbO$OHS3T?r zz3^k_?k!o~HGe&{4tV9%7z)D>qCBM3N}?H%cH;J*g-`M3ox9}ynQhZV2#BQ!bGy9rnMkcy?CFB~oo)e3wx z$#kctgEux|`@xIy*(q1_24&$L_Y=$B*w+x@i+lDe4_e*F<;gA)v0{@>C>AI1BMwVR z;y?Sd4|D0>)Vtkv(m3lLi4M9Pj@;=4r7GO|W&8;)T_mwJHOY9(FCQ}b38Zo=p;4|H z75o0B-5q$El+?=TQ#JJP;T}0sYEtS52mvv8Vdjfeaf?THjs}nC)>_j_sC4XTB&2=x zJ|B`kJ_896%Dom;p{Z>#ICgT~LYe?`?{#Ys6_|VQ?eIW-A&Zumi~xh|Sg3D__Lvw= z{!x@Q^+A~?@%8cIwHb1ixu6pQzr|c4oFdBhXB^*NT^Z<~>x)&SokWqZ0imyB>?)Nj z9t8t!n<5(4L;&q$gjx-%n?q^*@R2z`6>3!Gidh|m6qnXm0={B1x!pu&ma)OQjAT#x z!|d%dYVQ%1QU;XuLi-Ibe=$ZJuFD61L?VtBCw*R z4Kv^AS}PsD0Ec4rN3UMjpRr+j6K{4&;^YTk!_q6FG$PX~51KUN{i*1vm=|yFlv+SE zduk#j1B@oT!ruyW-JcvtoU>k?v}WlFVh8wd_vKdS+sbxb`k4KofcCCItKh_wJ*tY) zJ)v21@sqPE3@?dVIuCLyp7_aCi5yCiJ31Mqi*bw=TL`mxIWi7QHO%j~OgTq_@oi`hG3+o6@{^S*SEs%i=cpg|vcSFRVuWUtlZ(I* zTnMl)nKK|X9E=K*n+?Uz-Tb_gZ(D-EPr!p*>J=Fey;{z9$}B7(Rg7{Y^1yN8d?o9! zSU&es-3d(HmEIj|@zpagr>@5^(GN0>}at)Xf2#d-7K7Z#u}B4 zcS0bU;B+j607C}L^r6J&W=r%@qO-G8_4|47p(f;XCV`8 z%k92q7I@$DR2n!fhiHyEs6WYo#3aSI6D{16l&`0GMk(H|*tEJ!_@K(mX8ZKDwL0s^ zU!98*5FK~M*2u`2-%nf@gVQ$!MVL3_Gn}m-6W_1uq=%D&KlJca`~-YcHta~jx^;^4 zXWxX%kmt}hKTMuMXod*)j1J=66E@%L50&`wYS3DDU4&*lnc1{lY&o+vnOfV}Ju4eE ze1-tiIruL5?slzYI9$Z!IV2{(?2}gDjXR&Nv3EW&AGV`hs~%ECB#cTKWyBtzGnGgA1vs zd;~6mJsTwx!N#tylc`e#S%FKt)f*z=`c60saX<0OV*qA1=^T7N{d`frv&Y!IDgzyu zkRm`W`F>7w0Lu1YH1?;L4O#gaesX1^w(<&%RwW>mMA*87L&vMWgU$>EU`ow{9-gfhoEc z-xV64XUL9I5iZYk2sm|2N90y)zVYl!-Y_$yYx-uqKT;Nl+5y^yPwf^NUsuG|g<%HP1@9T^7$X zzR>8Jy`-x%`e=-%tO2~01^%wO;1tPq&0|26B|;6*FnuHC@~e|IY1Z9h;TUEt*-7FM z(58Whz%ENc7DY9P+KG?%Sb8EjA2zscKo1X(x(MY|00)j3+<{;|B;q0j1R&JW9svR9 zqo>I^NoJV8eBR!`iv;&E@+Z}ozMGf=9}~>g?G)FbZ;N@rCA^qG0=6Thv+(*=4aYFC zfRRYv_*Boz^(lwA? zjtIjl**Ex%Qw7DZl5pHldk?(A;$< z6@=2bqA?B_eT#NHi6zGa>g@gR@j3af;!3HE=W6%E$x^hrXI3oM?`ukIW>vEAx_+TI za`AH^&Fzc?x6$hpJ^|_`DYd#MBJMFU3BmsIaG3F~#HgK2;0CH`$IRZ*#Qm)fx^2)z zEAUH+L7}hkF*2;i;59|Dd|N1dNHp=9p6SzRR;pEcu%9Gfx_tKEK*J#i^PWNqK0eaJ z!^63)j~}1JO>BNz!v?BL2BOBzDo-Yh-)RY6nHIKt4-dY`A+U1Y)Rfg%jvY*i13eDw z-f-%IgHHRb$n9Ew>#++xi}Gtp#@{2N7!eVz(^neaf8*64wCe4jNJ*>}P5ipIuouuE zb77XmYvYZ9(Qf_8;6_V(zXjpP^KFH8^f7i#-fRyPX&xxiL^Qwe?G0>HVZ$)j1n@B3 z;zxS#V#5@z%aL0@P4u7QXv57^MqkaYy(cETL;ULnt+84UC4 zt3uOTHcY#2eX`!T@Whcure{~x{y3QFs-?vSv?1CG9Ad~ta$`!{nxY1#&QI>j=ovB{se*Sz zZ7fGevG2z1!%eVQNAC`YqSFknh_=+P<@cEF} zsNrR%;|8DmzE=M9R~UXUU@;`U^vbvN{v{)!O%`}K3)?`y6&SPW$ZKbSD#PoP3ah4% z)34hE(Hj;FYubJu2UDz%1m6b36nk}lUmGcP7&QS#5g?la`18j{_yS__uy4({Ej~uD zgb%s;dA_{bm-Y$7SpjY5f<6u*V~!!Q88Ks?V@VwdB2*3J3p@N*2r>8iePjM#OY`gP zi6bF2G=&h7oa4h6j>SWa^m@8C@?TfrfZZ?$ABu*lTKTqH2TfnR&b%vW5lq?QJK32+ z?I-an4iq-JG@XF*UW86nP3!!MUErTmqy!lJFqk6epZA3ihnyftBQE0Z%ki6|Fa#*1 z_R$WA1tWugLhmj@?jGO~XAV41RS+0qECMtLz%qd8tLP0zDS}_f{7t0vaQEM^8T<_Q zE`ZP-t#A%GL0c7BZ3LONG~)*HrCa#GuAES*Zxx+<`4{<>_PI*d*B!*vCoE5LLQfon zuh0?rzB>Ks=F{6m6UD4pPYHCo4<)t0L_-iFwwegd=Mrp}%_q=IO!wONwEt7%dFSjhTE6DtNT7J`i##t&pM<}G?@4YX(#a`e@+<1;;*V7txZNd)M{?y z>q6lSxE2E`7j%dStZ7j)H*&o`n0Dy&x?+fP5Z{kz%+=AWb zKHbermOsER{&AOjq0~PZ-IHw|&#n4|`1rUewLxR^Ns7bY<8O|K@DQO+kUdFR2$lqR zv3dr14=O)aHOGU$CkMajUEr}|@gsEkmc*ECd*s>|go7-*?PCaAN-0Sf@py zR&plb>5nW7n$nEs(%v%j_V&+3gSWMcCmR>H_G1lKR!x@ROc4#P_Oq6(Kj%A2VL zh%nWt3(M+DwuVa;xzAHMIwm`>yzHO2uWXA?47DLkpaI}iZ3-`yi+XMcoyvVYNTtP`)1~Z@E3ZdEUPK>V24EU^*`SSi`gH#8{TQ z&u%C9Zk>Xk)JJNb2y3P@Dsd6B`TRK;81(b&8VX!I@uR+Hl*t*tYUu3v+IjmkI{Ldk zeJ1@ewms9mbx>d}zj5OJK~&UnN5|7}O zS=rjU0w{&n6}*cIGUZ#`d)||@-yQ5dn>@ZkDYiaR)kf3tvP|aW!%sDRP&b^|Axc+N zIsZ~_P?0l`CWy!i%u}%%erSzG_4iiP6#8>@Cv6Ix?{5#{_C3r|Fabie&1? ztF!g(anWu@=}?(U-xIU4Kk1W9u71>y0x)$;oVt!oV9>(x1KrS@G9}aWS*AOLtv}Lb z>GY$MzkVGKwSWlaT?@KPCI4WtGHFs@i{ozr7WB^0^9~s|E!5wC%|0EuFq@qv@8PsB zy6^fU#CAGGU(3ZKjg3lfs1z^za93#3(~HQ>N8+CR-pHc?&s)5$q_yn%wbg9$)k`$K zZ+-Hgxo?)bPi)Fp!?M%89v1g#lTsJ5dMIX;PpdFYb@I1LyrkRmGpckZ^Y@xtDk#1i z4gQCcH`8h0^L^rFU;6H2UKee!RCUMnPS>6D4kK@BHn;c7S53052JVUiK_o`qFi?aJ zK3m?^Z%9PNh#`4KRanEYEn~JY99xrle?`WQDs<&QWu)+6Y2-bV2eoEzg~A1VjAS|u z9?`_E`L2kK@3&K+jWY1XoJPbp+xnQw$jU^lyh-ii2p5gY;+1hb09Zx5Na)l(NX6n# z9;vI+9XpEqeh>W~^>{$!TGzp7yW>E4)kmhR?872@k-uF{xJ-pA)Uj*q7X#5w zbQft0jbIMSghs`dl9iVA@6}1LYfcCJ`YC>LZfYe9$!SgI;*P3a{Sy|#tx~>7Je~(P zo3MEEucv!Tf}aWB9^D`r+g-LUxxwMh!on00iySxx&@1?;xjMHU^xF?}1k9M8bLg>4 zPIKmyDE}-)7aSp4frD-%EXTnG_P#_^+ia2#3LT!8+#la}uh_rU|v$f1bwijsH3gz=PI zlt5TsKBs{AA`w3}IAEcb>0_RV+X|!{Ac1%H+r{7#hEKB%3cX_Wr`MWPg31PGx8 zPnj5f*nJWZRCk3VW|Z-j?=M^gFg-wuGT?w1{Vkn5SF51CHN}9-Y3pZ%B8xJFMaFL@ zlsKYBH$KgIxon>n^xfC|K5Xq`ry6Gz7TN$q=xiMClul@$7@m>n9qyUgM_} zg;k3>XZcGiN&o!+gDT-J@46-{EBTgl=TPrsV#d+;<_{ScC;7%kc7zXX_s5r4@@#|y zE4-)$k+2gNkLZaNC-VD?;a#W3T5uwC65R%#rA7w`#&G2S!n*N!jN<%KBn8YF*?-I@`tBq&z~u9p>3Cp)LTeY*l7V1 zyQZ7xCNkTn(RLcu+M*JbKVQ3-e=YUV1y6l5>!BwHxP5MpSHh~leqgVAzImZpw8L;R ziHOZL#izYfe*ahOod0Z002Isr6+EF$KX22JPknnX;Itp4ox#OqkZ#WzQ-`U?6Q0`> z6?S+B9!xy{_1d^mEsbs0?g^&(Tdc1c{*0ZPrMmlX&8Qyd8HWliZN2v5wcAlP8I~^i zz9D(a=e^VZfkwLpM7yR6Rwh$JTxl}WzkYMr$5fNr)7+gzE6q2vaff{3)0bE0mY!z6 zAGOL_UVa&s2$&21KO?B)bEf`Ep{%$S#(n@Y-#d|m`nX5Me~xWL?fyF$*J|9WO>5ng-!3NEU#wedeKan0qPRO5TH=}GNkU33Ca^nf{~B)a zs&&+<>-x;CazP`pu~ouC6)$GNq{mv zlUFfaX0+GBv=E_KnuATgWVR~Dce|`+Y|;Y-Wefoy!c%U5O|4jev?FWfUeQ6=S;Ihz_rL>9i8f) zTl^?!M$S!O;$S<`+X*y{P79rD!rRNjQH0R0)N2t}@>+5$li$lnK-ai0m`4B^ZDIx| z1pN}+rFJGA_Nh0vm*4FzwkBCq4@G?06zx&S8*E+Mc{fuxR0n!AtzFbsCQa4R7CA>G2Mzk_zJ_dsA+JTv{Jz%9TV}Hjrpn%?M0uzD3Y9C7ll1Tbs zAh_U%;Y9eJ~x0ycOIc*5y>%)i2lvtRu-F0oVG8-{v zv?3=BdCsf)vS@iyb9oOK<<~(;70+~7Q}79Bm>s?P;VR`1UvCm||Efkou7 zJ2x+e&SBz0TJ`2UGPJg+z3RKRrFzP9IS@Qu{&6R2mh{ptxZ0t^iR9mquK|i$KzfG=A!9h>f={KBOEIk z){PNw%zp*67d^_0Q|nWSUtT$N;YHWm*L;^S(un-Ly0SpnqF7@Rt zwAXq#@wD~z#f(R*5n&j%vO1rP6H25GykLnsMC4}F$gQ*8yt!UD*|$7MPq@FYPpYua+;|@NU4D_ zcVzJlY3g050W$0pqkH(MI`^Z4hh>vQhL7Wy_+J#IiKFMy#cY0XK~q2Z5N?Ivc7UVN zvo?c&l~J0PjJRu5fIUlq^Ty8MMARc_#3eAl)kIe`AMqnYN-UZ^|Il65-+P>E%MDC> zk4g3!ky_qOHw*lrE%VM;Q82rXHkk%|p%%tDO1M~ql|R0yYp!UJuzuEHQ8uEN3keZd z2~F*+93k<7fJpQ1WAEw}r_GA|z zT!$s}W%4V61Sp~XaG0WVAx;Qtj3d!Tv+h!Kwk7pa?9e&|cw+>iWFM^Nc&i;v2y=iY`PpdMpg zWE6mFJUWAUqh-vZY%scfiipR5w%7JU3A_d1VU(cEU*Sx{X z=$S=&xPtq9|ocRo(##zi$gbhg$tHyjbORNN6BiG3!^Qh(?; z6l`mFK)~*|QSs>Ea69lQKV*P=8Dm1Cj)jmslse)BsE>1yPw^SgHW}+g=7_(E5ux%W z6}~TCZzItw;BU($rv2u(`;LP|6ekQU&{KHi}j&xf$jB z$e95ozzohZ+Q)DxE#EI#$e4jR-$9!)!($xKm(sujlVM#i>Dr;eMfpPY=GkSwc=>|; zdiB}A?N?27g0*NZ(ez62>7>R{Vmn8hA|j9Ta|6}n1Q-pR38U!9EvNwU=lNsWUS?Wr zJ{tI0E3BrDFv*ceU0pzvWR;HPs}RPZOpP?b(rf zPxE-AWXTVtR`SDvu6N#&fs~F#bNj^d?z!FX-^zVk&ckUxF3}+kh^cGYUya>m2Pqov z3Ee^CbNa#{Dxcs1ZU`+O^avWM`H3(dkTFO3yHN#gpPjCEPsY4ceJ>gR;)G7`;Mz|X zwih40712f&@~0(RRiI~Sai4@AS^RQK6SuBYE z+mpjR&{V!^&2#8eRcr5CpW$KW9)tLa?{U#fEPOOnq6Q!_JIwQ8zaRs|6hw;^Am}&1 zy#Yl}vGFW$k4vt9;3H4pCAFsBk==JVW{$(h>Cc31IER1k^l-fqe-Vl*IFVPS8n!-d)`WD_$eq}~ zb*Uh06MHh6D9nqc=e0Lkt@nIw;oa8ksI2p3TYnXPZ{FD*+w?BDIl^0h`@nDLt!un@ z`VMOWf&?JMcdjF7v!ulC$JS*pU^I4~UUxwc5gF&`#9u2-COL8yw>ZA0fDiWfGL8SW zP~niU=IK+SRuXyJV`a|sGtSF|L5a~HBAXDjMwKf|{-iXb@);duD|nz z7smnavM;co=-K@!C*~e=WxfScX8_^Zb6^8yUY0|@`z8TS#v7%7IT`q_hP&JUpxPaaEUyV`Y4=cWh{X*8W7qYCr z*5UPU(+@Y&$gJ%oCxAiAzGnG0hIx+xsILT+W)tvsCPkpfc^Q6OX*dJ5qO2z$?#G8+ zkrwAVnfTa@PAdm;2hS0Co9VeL=#o{{v@{P zzB2dw2f5NTepb9BVslgE^x6gz*jnP;N^f?01YS?lG@n~y^&)He;e5wRmk{fMU|;i6 zqrj`dsTP&=`q{;97v=ts!OPQ(AI`1i=Hszi?(=)g4sG>Prucj?9r`>xi#o@6SNLzp zB}q!H!kci(k`&7_;cNinbjGnsJ_D$sw3HeA(BiWOJj4*SWWTO>CzWH41%84`N2314 zcs2dv;-a0seKDKK)vND^hr{phc%K&+zuNhqwIg2DX8j*O*3YeRoFZ>n+BC^0fThb;NNf^ev+Wmls61v;eX92AoWn&?O+^F zW_S2>?!Ewe$^S2b z=B7L}mwKdtOESf$xZH{+|m`4F1BpCXyT_(?2 z${tfvH<3QrnpfM~sEwbDo0YxAXz7oqhbB(g|10FGq()ydy?a*>1&vin7^MdUQ~?*i zIFNbTjQBpilx^AZJ)vP(4W<00xyOx-Odm>5gtx{qO_v55?RNNDy(PxxY6()^QVHkd z4?K0`Pt*U@e}3_T77${L{)MVN^{f3TolUUnT_qdPv{V+8+0Oj{5xbnkUU8!cK}$l; z>;%c%0u@#sKfU_zUu29PO}Q$;)Ew>}(D+O!zfRgjPEjxoz3~vFm=r<4779}WsEn#( z1P9;(e%rNXWTk1e7E!+|yT#znsj@xwtAIA6GuKny`SjoRLq$L}6=v;9ml!UqLH|q8 z%6S=n>ZWM`xdv)%-b;CHGz+Zt?BIOT_H*2a9EJg&fyz4&12G$0a<+ z#lQa@Hw;W^To>1Uss}E&^u?l@ENN|%O-L4@+&5G6%(H;8qpSqw6tw~r7E0@cItq>r zEO67;mq}~CF79W1fv64=IfhjIEFF(L$jaw$mcon1@tmn;*L%%SfK^G*e_?q#1}BB( zx%Ag`$s$@aeR~{%eszx-c=xmsAgo}~q9Cq05@TI@pqyP3201JoFBWHcVVUnGFN!dQ zJEmUNLw4^JzdVootd}W5LjFPGUx+|gzpHPpi>eoty*BlWKL^eN<6T*!w>GSKRnuk5 z-l-IqN=rN~J}tVQ+jue=&gw4kPxJRdBoH%xo4@#0!u$R~fAxPS*S!rE0695F0Q0i^ z!qn;D4XfNJ%~|+BZpIs{`n&QRBlW81FZEUPnV)O|@OD00Zg^%*-}|N?=%na;FIB0E z?e@@)aCf<5&7iFf&XMq)kju zghnBi9{${AFit5hWH>=v#Zx}_qo||nCsjaAfYL;HGCa`?Ki=|DoT!5qs(W z{i4nCjrx@I35+fgb6ke`;9p#cl)=vRnjQIoz^6p#zi!&9-M{ArGO(@nhBkln;A1k>exv2{Jx=6s1I^wy2KnaTOU&zf{fgX(*sY#JZ<<;^e@2b|z>RKuWw60% zzy?S0LjMq`uYPXLH2~nj`ap?Pp^iLZ@czAW-<`{yQo9T2?VGuY9vv)^z%^b9KnJ{? zE_-Y=s2pOfnFeZwgM~sX9IX5r8!Pwlxuf*E81g*A8dT8m^@g{lCi>a|bm) z*&*&J#=;QEQHF~P00FN4!HplLL&93Dw(1A9TIBuKF0=Sf=+w16(9m!4kwKmhKlEF| z1J;>dRp}2uMQi61%=&=W2mac6y}EkaER{Om|aOPLY{(+S`h}nIPdoZ$X={ zPKzM%UHxXe)fg*3J&EfeU~)(71LIe}5%{T-?_PtQKa+mE@e}bBt(g#XF&}qB-c4F2 z$Bk;`o(Fu@uXmf=czdatvh0Z2hicw$PQ*7IBs|X3F8e(n&8ibwn;j&$BrzwzJ2BKq zsdR2)X-($!ZF;tewd&VK8-t!J@=FI4AkQ$k_WdGYH`o4TJ{dtXTKsW?Uj*oCF#1ta zw0Jw4#hfG}XOuJZ$I3~_40BteW@U@csg9f?Kv~Ww!ZOcYtHAM*Jv!iETtX+Xz=xqf z_oxcyPRTzBhqS+|^)piWQ4(%)!yK!Rc?ml0_-hk4G;l2+!p_m7l+L z(Jvs+jm->kvMe?x`9*$_F(0z%DNp{TUvc5Lw88I2iFEA*?bAYZk-U*;EO!c|n zi1=!D`@oCnY;~&Ydoq3L(bIr@cpe`qAlKiz_bJ$R<_r<)tz4UfO=OHHuMzXxS{UEZ=LJm>IwMExQz(qShG3I7{Y+lWsXV00Tv?K9bqgvjM z+?SR#f+?Te-E^#4uDvrE6kV#1rY}!*T9XAti|;D_KSS>eh0o9J2PWT1`;+CXsx4Uv z>&Km`^rHmDZ=>~BA@`s%+pDCbt6QAk9Ub(?LmaXcU;P>W+{7hL{>8lQBh5FwAYrp1 z>4uce+itxx-Kn1inZz7it7faJ+`g2uQEYzoLye^IJ%4$}+9?>WLjWn5XZ~@zex4%P z>4&CgeYT8>&-80nOTk|SGjFwW-SMDR1uhh`;L+AaL5PZN!Nsmz``gSf6cp2c_T*d(SEA5=~i-8k6OtmQ;=Kxs*i=GoR4;{8 zOn<;wRFXJ?h)rOMYb0}{Z=+*%e_vMDIE|1h3196ISpF4)HoP*ASxCFfp48^`5nNp8 z`ED>oC!j@1ec^<5VE)PH^ia@2lKA%Rz+5ptDmNJt=1GFZ=bSu88z#XS!8S z@deP83RdsclRshWKg`5m7iM|l-3JaR;3%QLcOGhzaEOdW9$OjEE{qr62)ccIf*Gs6 z$sm2Y)FRIDKgX4>SC?oU_NJgByT)*#x|X6sYd-v>rUK1qWzd307rB_7a+riiIUE&Ll8 zYSH#Rse#X0qWVl@q}9ci9GY3R4=?y{?g`|O5rCTXh7b-UG-E>T&*1(?NjuKrzU&8^ z85!&1Ki1v@9P76KAHS~4-g`w{X7(;*T_rPF*?SdI6cQ2_ve#usghC~o>{-apDyvXJ zM7C^x=ZEUP@8`Ln=efWCCxO`vz2#E`d>*(OD zzruKi@FN!CI4(q;zM%z7$SXuz^O}Ti{-aFD6ba`yTfe({{wXsv-2R5|G+s!K9Jmxw zyD4~*h3g)La)b>2=~t*X%oGOq zWctN{u?Q1F5vF5hX-UZ}RJ|Y+7cL~0ZPz3vuel?c&U$g6&{QyPr06*tYTSqMsELVy ziW@~qkn8Y}#p0@}`@yG@bd$o{^$*NfX0C`$6eYiT@wkBYK!f0&0hY)Ihwu0{cN|T~ zjHX-yCKvx}0I%gqK<;{mnM^qAG4r#NV(@1y(i94$&*?Nv^4b06K}~YBz}=JSiw=I< zBdyt9zy;_!kF9m7f`dYCn6YhK%4p4O9oYCdzhWqHGezj+Okey(*_}^o*#}Ev5?*&} zp-J+_5_|wwRC&deY$hp)S`o zx=5w`uQRvTeAn!cZUw2TVh|H*_*ngUEZ+0ZzSp~V?=rw$g0n+~EY}}?VOZc zUh&rA#PUE|?$Oz>Tpzs;E-KVPPa#nYgAR%{>#VB%CPlYnzSjy%JCB)WYJsDswuQR6 zL<6dAl!%-1#d=z&np$DSNr-D;{(M^Fuj+xqeCig zn?0`_V?X0Xw|rECC0zNqA=5_j68c(E(O6G*cK*x+7`^-YvwK*70zS4KN!`P5 z5$MS5kvAo813p2Zcm7rX>G_+}CmRBX?)RSsS-jsBgLsZ`{6D%*8Bf2gqE`P)#l72i zv0ZP_TibPamPfqelVeFmzPe{WuPL-|~kVn;kc0Ll7HWGRq>tzI}H4A`b&IoM4x zBCX05R4+TE?wb39iif+I<+wGzkCwU(skz=MK{F0wiy~eWXODpPvbnR}-SIHU`E!Cc zJ5bE<#2&PovT=x^6+j{RW#N!#4+~#h1cG>O!rRI`%P;nFCoHr3tI%4>sw$@~zG5j}m)t6b5LC_tKT@@aGzSdR`hQCy{|7Af5mY|$1_wV9YtHubyfPaqh^HdO z$6QS1ND^N6IBgm#ux2|WLOUD{xRNK@P`U|7#@{BKj)Q=lBEsppBequIIEG!6Lj5Qd z{yyu@;}~}STkuOJq#nN3hx1;WESA^+f;IIJ(0-*htUd!}?g88IjqmDB_c@dKn|(Jn zpVXHtl|`Ag5r5)OTBBkT`DSUOvp(-t_iGfvvX?sS=A2*wA%^Jf>z6`csN9n%SRFtX zskfU7d$kPa?dvioqIV0^LaAC=NSnXx+^%EhH!CGI7+*2*sC)aZG`kL~(WL@*ZFq`m%1C}ht2nSfts8oeeQOgWiKp6?5yeR_JL)(88Rgp%{Xl*4U_?x0qY~V^ zVsx**62!5xd*bCw*Mc;S(!)xvC@9H+?u3LuSwA#3??bS`{3u!h5;i=VM>{qcP^3|4HSFEz8=mh>g_1A)=Bi-(iQ zH-ff6Cgj`@nu+AT=sV5|nYDXE zBp&mn3MTZPu3`S6-R%lyv`Sh@7!RN>Mj|>1ha)x5UOda7|_H7E`LY zXKLm-6d|Uo`}Ik9Y_negX(JdH;OCs?mX~?Pc62X&s6KejsXhgf_25M|OxqM!mN{>{ zh~VqE+XEY(-cgD-uNe#8^w}uq7FrBaKv8#)B5x?LhMe!YdX9qT)Su=GjI7K~V$aHR zL8~C1)4jUPz0_A`26aD59zOpBRWkh&&Ep1_sUVmLW#(fGfw#Ngs7=Mc+Y+A?FIp4s zz9htuXiJ&CFd8`_dr0V0x~2FIH=uuR!voZ}Aq17sA>Tpk>V;-d8Rp{S!*VrU_T+<} zq#4PMrBYb|NvlCqM@EgrC>82Ez(1!-P1D=?_a8MAFE>g120!Y)o0>mA1v5&7MCC=u zu~7$_wP1Sko-8fI51HLLHPPV=<|66JOya1ht*1(z4}|Igtf_4L_91J20_V?`7BCw# zN$S|Wvsl5_ZyrRwif1QNXn3f_8#WU5@%udo66GQ=SY)N`VxiSnFdyt$CPcdx;{ztK znjh-BNd_x;i(dtK7)ox(BSj|%h3e9ms(pIhxB#W5lw;t_-qqUzC4Mg{+a#w!*hRqq z&%_5H^T}>b-WNLiP>}TT@rVrxS>O%d-KwWrYigTXk+ffife96C19W;+r$K-<^F2L7 zfGI{JcL;Q9L#D3e+ga04Y$k{ijfmho@m=PhuG?@JC%PyGQbD-6Wiqi)-aKEf5|6E{~ z_~{iWYPRQ3+o+u@$EUdSZDqYON0mQa|2PNP>`-XsN!bVg>&Ai5@( z0;4KjJcgwd)TD2+hH~gMa9wUqK9EVk%8I4YG;X`-g&%R?xkS?qu@4EWaQ&_|;Np6J zv7k`3bZoM52eIxYW--FA>7qDPWU|K&t?> zTC>vrm%IWP3WbCxu1l^rT1j4?@_T+-8~M7VA|GqM!smXzw7dd;K?r@!8N>TO#VD+| zuabxo#$PP=yAm}CrpmIO=?L#N}ehD2*teANweW`*Tpc|OH>PrcQ5H^JUA!<+j z-x;;dZy-Od<>LD0F3$Q>)M*VMI3FD`t)T22g{XqKH-NhUAN1n_ znAfW2n`Sc4+cnCWfOUZqMe9!)zG4Mjt%_Ck)ho_m=#_TEU*46EF}L^lG$;2}kHI_l zm@>NMrD*vt(lM2ScDv|Rzd3aJ)7ZAc<6wM|?Y*-Wn_e=Y(_C1dyDQUEc4Ic6=fO@x z&r43?LB;KEPP!5!LzR{d%Y&S#CsH29nEs{ zhU{cFce>oM2G_vQ-tRr5!9C6-d=;%!!majEoqHndN|m2tBM=F_K7LzCflHs*szM~&wX zrSB{@59XVEUdw*M-n~M~BF7YW>e6f0$jIU1d(tk^Fdp3~^1C(Jr?>T;q);8&D+_k& z{WZQ$6o8Zk)o1VU)Ht0VfE}#)^A75A1|9>i7g?oi;$~2{suzuJD?I*HH{E+}3Ym+P z!|5=PO%+1D;K5U26+BD;!Mch3_xBU_`^qeA;gFf$6Lte<`j>eQx|kPPo^wkcCsRTHdU%egzCSJfP!gSNO1ttueLp4e0Vw{!TE2ut>z-n=!a+WOj#a{d}!I-T>4 z;Ru$A4ZMYqbdS zS1re|=g&z}w#2@7{z4Zm6EKsg56U^2E*@<(sHA{A0Uw@9JMJ|%#*7Cp9x6*F0>TZK zRe0D?{dbba$xP3Xd*0IRf>V+PQ$)8~L2VmSw2^F-k%m2)fN(D6wk}TEsODjeynxAo zQOMI9TKWRV9H#%LIzV0(J)PtJ)-1KN(wYOe5uls`G|RJ}P@a4;pz+3li`j2y^kD>3 zm(ZRHe^gmY#U#j`Hm%+nR}iPY^XMemF;x8-RD#V~C_klWud?*OhVku1Y!xLmDYh0I zTeG67k5DX-!4x7{(?0hi_Wbe5>Gh9zJ}jWR2{M{-QKu;B zha@Rmna`ZS+T9Eu>IxihwGLt(ZL-SS-BmWMvl4sV$DJu}>MSJ#1DQ}Yz+(blmQ5MG z9!td`Yp0?wb-v#Jn&T#Q@JmofcLhWObwV%wq@jM zRsQM-DXV129BZpRP>-G9)$7-Gqt2R`^;O~MVTb5{GMrvzvijU6K7Ha^|IiKt3>qM% zQe~m=QPKwl(6|5F2ZLBe3cT}#tD8*v;z&4^otY+lGUNnN9K>q9wbRu*8RcYzG>qziB>Fj?0|nDLQxL>QvoRDCN0o+! z{ES%t%^smAzBWw{G=$~8l{x8$g7vB^Ux_-K-25S>d~(6F!G8po4#Kxczt!Umz>`qm z9!ya@GwU};U3Dpt1ql8dhp(8BUGT}5J;gGhJw;LMPkEkyHwuMA0X$zwn-V5=PDC+C zpXEg5MNHLAPpi~9g@Fuf)8Xzwr@iqiFSs3Z{_Cq_GkFZrR&YXgrBF;@be(30Sp>(} zdDsOZKtD11hBAgQvCF(xXo@i=rQo<6!+h8Ehm>YQ@@|BFJw+C+Gn)YWZQ)GXg z2;43Ivx}6ej1_A@eYNQo_`xxZrY^OFyYigf{w z{$}Y!Gke&|5od>!sJBtyDb0n_jOq!8WR{a|r_ylukpJzK z@<3}4>GRgphg&yNJ;5X_raptpKlilG$(MGMs^1&;X~B`)Z_Z3L3zqp6xYBu(;u)Gq zzXtavy_fV_u;19|^ryi86bQJ>iaCOX(GO3|hfKeKX*2(6n8?ZW+0wf3Q5ttq5xJaBbqMU7j&saBw`Oe=OXh$5|2}+{G#J10j+1PFCEx zT)eYHddCJGQ}#%$jiR3f-xbUc%qyuz#<#sX0k>YHP1)UFvg{w$-tkk0>ctUv1Wf%u zqNALZw9tkCP!JBaS6-oo_g}!*Z&&3-|(qAI&Rt5cC5O;wU}l;+G}A zw4*Mxo~m`v*@CeMlSY;0?}E;{<8y~yHwjG2$`Y54Qrmsrb#g1R3HM8Sy$FpRG;*Xc zSMOv|)n%8aU>2K$xE`za6ko-*E~z|IdiWCRXv#Z>7K2X!Mp} zGP%2uY14A?;cV|Uyf#XzFWW+;2X5vLlIf8LEURG&LQYOZw|8H!Z}8f9a*KT!cZFL=%)kl zSb%X30pdi*gUIA(H1Ip+Q3Cu1;vF-}iS6kJrmJI5$pqYHL*H3bFF_LR1Mfw4qb<6k>|%#KDkl+ zh%PwW4;l%=V~(@3Z>>`ENJF5*y#`edfNr4(!1_ujPQp8i>*&DFDxs&R<$q9FgH|Fz zxK7lg)Yeos2$m8+ME|khD^98W6>^s?ww^o|X&}|Q6h~5xdHof33)93d%X5c-V=j?R zooNbIe7B^Af?h+xg#(uC>N_kYzvE*I#cwqC-Xv_~K+P|p3GS#n5^%HbR71a3Jh)$J z^Lxd-U?EV}ngg7_(oGa2s!utLR9On8rnM}&!CJlgL-FD@5#}TCuD8x$F!&5=X9vmG zKRPT;`Hlg@r19n#wxH4RU$p>Hzt7<(5!D8!`OQhmd$q&+*_Bl&Rea46VD2-VsA?KE zLf5MWB}xkEjazIA4L;)2Kq0$spPi{uIxh%Bc7Dyf1Voj@t$Y|x+rjKzZ$47o<%Lx} zXg;jHlSAOpyxw~~V3>OW`!d7Q5GkgajJVJKVngR%%b#Wiyq_Jyvi!CNh6Oavonppm zp4_?pn-Qe*?r?o6G5!ZB*mPi3IC=|-rqeHY_3`PcB1h+NuQ6NE&_7id^(9@qqyP9X zvzmZ;1!cIZP|SV&1C><)&5PFoW9B^kTxMAo!M&b^H)h)xNJNBH8^eoXE}-a zdiXo&n1EQrU$5loy(`Ml-Q%L@KASE~Hk6)j1h(KAo2{lmwV zvGM_%S~bX8-B7Ns%D&zb)1W+ueOpRyA+tikW*ykB^Pm8)eiaEiOB7{~RhU;3E#vY% zTEsz}y>~m?;5zmroV2Yq#x41V!^|BG=kM-Df_IPXCoRa%*iM92azWnZ_=g)%n^61( zrtvA#B$!ixPQhxj_Gp%K*9h|#C4^}2;O0zvP%$R5Jqyl8?D9@rT!Si&cF}J{HP>=`yktaQQnoT#0?d6F-5~Z% z-vyyBYzo2eF;xwPo7Kp*I@31X$FtZH!wGrnAFc~#y1}~f$%{Ef|3xIgCK#}xW=7lP z)K;|_Gcaib>=jHbJFp;-j=E*EBAJTS=@1Axk>%1t8OZ2;15`9HAUZ~f=8siwQ_o{6 zKgIJTN$!xp!^UFJ?DftZ85oSBS-ReMxiJ5A;>q-|8$mc>pl}*+2yBh`n918`7ejx$ zA6^2G>UxBaG5vbEgfW+zMJqJ&EI_BC2!5f%&ys9`(7YwymDgIKUjfhcT<^%ls9b?+ z1-MjK&ne(quNSd`(v&`S-ZS6by7`SjPa)KN2txD<#{$FR&qXNhBj^BBEj#ztso$EQ z2a#YP3Il}h818H~6LbHr{;tp>wT=cBZU1F!Fyq5|b9^!lZSw^~+xHkJqZ0>If~fi~ zNM{8>l!e|wmBE>3nqW!1$4i<9OH!KIHRI(;AI8ozDp6mR7R)~S=&V%ikkg;d|7;%4 zK0E-x?@zUdCZZ3*QOJFFWyCR~00M_LP*|^WfUR=xo~--Aa9;xU7>BJy!zD7AWfaqC zPZtBqb->{Biux-sqGGN90C{Abr@kqBSxhXB>x652t3sq|`?mpqJ{pKSK>+Fo^1`4; z|1{}{6n(M?c60oyhbT@cn0kUk_-m;u-(nTuI+c8g3J|;=n++{)AfwxKNZ-TViO*}> zdAwo>+pXo#fPs%Xx%EAN3^b}qZ+5L?4+;&ubAHF%^L@z7`yDR-R3g>|mc~!#fpuGa zrYG~mt0ht^h8OB~&C+7w{^;Zb%pbSM@w5P519V724}ud6-h_%9v!-fz$S}WU1OY)5 zb953uMm3J>F_`@xMh=B_QuD+$bJMKM84Jd@fcm>B^z&KfAGV1$w^)(7wNjjdrhw|W z*VU_$B>~b6w7(pC)#8{Uv?BnL&>z!zpN}HH6d6SWg^W?KWMM0RPE@(`M3j8jp{GHk z@*o#SCj1tLy?HbW1dihlPFTATvE&1qW~xqx*tlkhQ}q+fy4+oV)Wlv^|6_l>bp1cL z#*kqZ!^YD`cI0nym_8Wc-xNer%7!A9nj+^zOpzU~to1utjlzQ6SYlb^*{+4&0|!0e z8*&Qs@MVqbtrJa|fFdpvgy2F>vWbsT z_PMuKpUPqsj9wsu_mWgD@fDJ2sr1anm_(7?X!L(Xl(kgap+JhEG>~zK;Tq95w20w? zAmzVcy;BL7pZ=li&Mi^44Am8!5lR zcm~{r0@&F3HS4xdujubg#n6jbV}p!>n9QvS-}pO31GrjKtW zBM`(V?BLr=^Vp8QJ^!D7d-d&HP84wI@55@%g>Xrj?iU9~w)o*Ohm3XCtUOa*>^33A z$TVZcn>z}5fWg1dJ~}oA4*F7aH*@CV$KF$2Y{93QfqkYn%pRKeyif-eSoKbHRM zBPBRX4TnCOa8Y~mdf}_YK_rYpGh;Qy>!DnrStbC!Qw#Evn<*a}>w)4kKL-PrE#Slj zUtxV|mKLWlUb4I7Non)Xc1fk*J#{7%-AL{19UqS>FF!M1oSlu6pPz64yinQ3Ci6Yv zxX1PUKq&Tzwqd4tg7Jqq3{AWx8Fs< zHK%IuL-yH`L~Uv}vww>OuJef*fYM;F3@DBwWjIT}Cb%P5@AA$ANlP{VT97>Y)bi_x zWtDsKi=a06a&DLZag?1fQsJrp$1)X+} zl3Rq+M5LLo8kGu*B{94_Q-ow;D13+bg6E@P68-dwk^q*#bxedv;=3DT!A`Q97xe86 z^xXAY9wbvFXd5;3S3PsmQ@W~jAX=jKsA0F|uxDw1tBY|j6M@X@n|v-|EGv@Tn7MkX zr)nLMY0AL7vZ6k()kecr-WAP27)nfz9g=x5x!Z#a4bz4xfonr)I8kyx!Vk|2j*Pzi z;hgvCTiD)2#RTUPxM{EL;Bs)EMT|Z3BPW9buQyj#i3$(lp6x@jnpp$y^Um0p_Qy|n z9=1+(SBwe1S5vIhyxekYWT1SiwSHH6I^BC-qWwkA6u1p7b=*A zrh^6#ck25~+_1@!iI+#Ox2?TpUe3AHmj55k$s z#dAqUb@^O&T%+4aoV`-6=ReEs75%#Q-Fb`EbNXU*_Ab*4-Km3yExzr8ih6}>9X1hU z_DeYmo)bL>@wKy_208K#Pi`}kY&V9+tqM8cjg~o>D)1V6u2Vhnz+$(3sFSatMky=* zK|}xbHayML_=?)yy49A}o~Ls9`>I)-*hm;09|c2@)1N}aZtymD2gISdQ%AE!m&dpWmr*pm@snWhVi9?*@*+!D(~HpGG7}K{fDca;4m5v@K{L9 z(ey*b{dt|8^*=mU_a_cSw|Dr`1tN{V?;XZplDl5|_HustN8cIOJ`uUCZRKq9fwA68 zc(H@w=V5SvSSUUcMLQjvolcI19eoWyH+ZiJ+$P2ddwUxV5IqoKI8!R(q`Cl#Il$&?+ zM=+T^mFnipdOh*%^NwQCLwnQfR5iP+$qhS>p6kV>+KbfD(?C?up^>P>T`_cK@bT(V zmU2LL7O_LxcZ9cDHplNS8RCRcARk!xREeL(TW!wty{(OXe?oZC>am}I4qKJCdyCRX zFJo2(7tg^Vx`B2KJhnFJW!Ncmp)SglJkHbjKt|mO@A#;$Q&lQ z>RV0ycG6z6qdAYXa=9*BlX}i-4&LGUgSB0G-op*o=n#g(QRNs!vkJ+Y{J@M-IfZDgMg+kS*g#)CmjZ47d0bn^O0N#vN#AQvzu-$haqVQs)qEl4 zhcw#iVV@RlzvdOSrSyHiyDuz?y2^l?``P7ALFx5So+5Ij(HoNau5_Ruub3eH$buES zztzUV4q?Y!zq$xS7!tgXLelQ|$Myx?NJ)u8SJEIiC+?(s?_5sDu+LwT>d#XfQ(yWr zjBigweLb0_g<6=K&{C^3Yi8KfPvk6}UG z595)M5i7#Hwaf0w6hrdzLUqZn=U?M@Ez@;g+`8Aq`iuke0}8+ou%h@wfVq)gI!vnF zF8`2y?dh|#@|QAc_(_nT(8rr{>n3Y4!LLI?TC##uI)y5)sF;3t@6=S^z$qcEif#|R z4)}}r6bu_pMF?AwyG&b2RXJ;vStcJR^bP@(oPdSoy#`e;<_OG7U{ZhdDLrKfjKF=R6GLhI z>U>Ft)j3hr)8HgxQf^1CHg8eXbJ(0>0`o1cnyPn`VPxRNtKTbN!G199R1Topl;DJ| zPjZNJM;w0^&%&xr>#xJ+rvP@*5iV*Dr<~GSHGTZq<4s{WA@}Pxn5xDN+H?n#ZcP_Y zIQxA<@I8d|?hB$=;NK%ofbVgr0@ZCD7wZ?R*d7%mbOz$`UE21YI275YQ}@fZQ@Ut( zeB2?WouMS>eo2>K4*1&woHl!$kaWVxEQ;mJ#WDO^DzxI%293h3SR7LIWye;E(IioM za2kjU{zPc_IUvfCn)u{=Syw2K%0dmg2k+;d;+?mH=t5F^u9dtNA-Q&XJ5qitq;{ji zCz_a?M)*V(IB3WanVM@~C;Bbg>Rg|c(Be;g#ReH~=L~++DU99|KcQ3jKD(+v-Z)<{ z@~Xna$fxhV-F7&WqT?)%+cUG#5%z9A^4y1O0$yhMdYNZtZfEvku9fk=Hx?LuTIG1J z_q*BF<_oDrkHm-Og{Ly*$D9n#KGIk>&wU@3Mg~mFW|!mIfKx^05Eyz=<9Q}pda3sE z+ca6eigA~V@i;d|Q+;Mxjm*pvy1Orp3=d<<%1*b(!YNU*=-rD=r9RKS2V4c~FmkfR zpU!{q+s1!kEr#NPd9}z_DUyAP+1Gz6c+x5=7S3OtO z3Ta(!Mt%&-%f!pq9`;N{Mf)13$wvGxNS+rC!`{YFe_G!Zt8w5)Q06EuUCb^Z%}`QLwm@N zXhzSv-PI}7W2Y#os=jt*Z8%P|~YiNt-AQQhP1I{J;3^Pxgn&{k0t>xnJ)V;SEwv$)RiTgFW*&)uv4=&OD| zi3iiu;Cyg{eqa0Jw%uC2r&-RsEPcj3r&XCGgrf^CxC!fFIie-n31fw$qF!Nm`1u)! zQZ7a`LKu7oM^lki0s{K{UJ9hXN+%a84`mPYLxaD8{1;kKr5f?mhNBz^wBVDa2wO}c z&wdQ!dizjc+d}{Wg^6eN zqCN#$O#|=4irJpPu?c^E6=BWZu*fgTyLbUW2xD|AMp;>*H=O<=j)A@DveY;qO6I%e zB-}nyL6ki!1a3KUiUuqW7R+h(5MYic^t532AQMPIg4CivJ{_Im>DDOLpZM004d9<4 zFzysrWmWTFU-vo(5jpKMG^--6iZ^Glk#(HFhbDbbYl|pgOZfZld_2#>3r17!kb@lo zPi8xT$`A$*0Na&S)ERwJ;ir`=x^QgdJaF0&@3691zK;@8Y3}$DW5h+)s5FESS+xib z&l^u0s*Tt=z^}KAppN`eUI^IW*FQJ-wV=E^@oy(d0h}Z)GLr^bAcl%nV*Nm^WyGg8 z`7maiT2Q@EtN1F~p6uoiKYOvW*B^7X`={C7-DF@X?-3G3b?~9Ce@&YXSA;C6#sA)o za8{t+xr5n#_9|t@3D@$uHjHJ}u1U`R$TH3eks@6ih(@$3dew~&YgT+n(0E2wH*~G+ z$=9Z)rdipw(Y==+zR#9k54;gfJ~MnH#fJWs)~n^ zk+Gz_oa@7f4=&&2fVSfp7;e2?*192Ij~mA+P7?#<@eEvCm{iOib8Q>pW2gljrct9+ zO*|-C=hl!ua;Dab7qIY;ed#TBl#j9bdM#h3FL5qQWRUl#X4UNn6?MwYzAgrJpjF?! z@Xuk-nD&y+Lz3-JAT&z6(_m1)wPlo}m%DS@f#BzU!?0b*4T<6X7~`^XB%(PD{zO1a zMc*_lw{cmoQL9$2NFS|UkQ1V(w(~XbRF`v(`VXoIARtp)Z!OSELU4ja3k_QN$l&9)l3b=8r(-?Uz~ zu6`$mln>_r27Eij;&&7p>WGNBrt#q@F-0t`3+Mm7TL6vFwH zQ=3!!n?6e8yX|+U3J(v8iCk421+lS1f+%ksMWmLZ@PqE2Htr}GV2VIe8E(x;>StiT zC5TzR49Y=C+rvmcv=iQx`X5GsqYx%CpK`OaGyOo^^UXlTm2B-ewe@HE11y?lA6BzZ zy=}L#7>Qh!>6al#I@tHo{@M(!qs<_{3mJ>UG9039QB=R4B{2Z0G>maX3@^{JcHS1& zfm0STUk$xfPZ&2vaB~YxjYTBTq?&@Xo#1MjDZzc zqmXX_Xr(~tT^i?%;VuVaWn*(4M1|hvwHt{`agJ%e2`bNWhex)#x`RHt?=`K?OjMek z(KmQEpS-r6$|v~X@XLlg_r%ue9*Q})ck}MmvQ=x%`>Rw9-YM4$@?Q*zkx(FWZuZe5 zTsa~4Q%Q+jJ5h;`@r=1D{|i)B0?!R#?5ZuABl3KC1SXG$bBfNbQ^7U=dbN48%Ob)<~Q(qq-#H^)UKywcd zUUT`dE6;YgQ!#G$NYlLdG_dkVULBHuxf8!w3pCpI$gE$`hEsY32F4MSnY_9n^HPjj za+VkMQo5}de)u4JLKdUut?S=kv?yS>_yzn<9=%AG^AV-=va|dK#ibpH!N}3wqXiuX94}gR3;ISMo?j#1LhS2_BjlAs$sEemNUOeRXem)n|M@$aX+3`*5 zf=Wa!4$%%V*+BGTX8Jsd{f!nHo+we&b-dJqN$XjYHkbnd$+_>d%nL7;8P_e_jEhBY zDV!qIUf9SorZCEV-~4gmyPI_XynFV*oQi~()5XFMaf6>9uX~v}l#TW~Rb&k~b$xPtR#A_imb+S_k}p0RkZbgya)nG)=51H*JjX%8Pj$ zi7Axmp}VB1Z0(Uc4=N*CdhgN`Lz)|G)xsr94m3C8UoGaJB=G1EA%Y7SW+MiqY9^)| zX$zos_iJxLx>ddB#2c?#aER$ZY|au=NE{7Qvwex{*s)c8P*&0L>XqNZ(vosdO-)Tn zMa3!Q^75(KzD$Z;JZ2BnTbaEg)4SixN=i%FA&;Hdy0haIztngAOlAbxT7CPTu>6ed zKoBv8a|idcRvqN%mjB^0sa9tLPARItI@f)xa8XhS}3U51mML|j3*Htz5XL5?@H z;7`WRc_Rl#?PeRF6rRVyr$L6x1wF_<1ZS8G5ifsnXn%i(>50mk6G91XXky|wFT*0O z3F%iawZ*?9P&DPu5kr+Ec5MkHv>QAY9XpJ8(4~I%93&`Vl{ZK1ydVQ01`I$f7)!e) zahNN!*8ePgJ;+zy(yUlN?!)*Y#r9CchFs{OU0*dR2@>X~3wd>LCNC}qz4>MJP~g%d zD3+!~;i|l4h@H`4jm><`5kZWZ!?iXQxl+x)UvL2jAJM#oZ!=#288!|GSw|XQ@>vp) z7wD;*WcYe0+ElIfu5pi|QQG&Aef7tOXm*qv2$ucc{e0iI&LEoKW)5}j$jsU5!Dy6aA6+M`1=vX;P!Z~^w7>(j6 zkrUWQu{8-$xh&%{-oxGKy^7Uu59$o{QzD6cr-W~`%<^UG#58Qz2z3^I++eNx61N#~ zY2IOs73>21^f9DeO{F2UmD=7)sf0N*-d-hTzb#S>U0Ku)54EcdGR9F(W#3%nDr7ZeZ^ ziky-nUo#-6tKa)h^>s?9e)&$xon`6)iyZs`e8dPiMIZ;5>qDfDzXCp(pEx+H0+5hF zaC8%N^y>q)cfM=9i{jIw!VDWkcOBoFBc`oI5p za-Hl$CIH*XXTz6W8F>;FEYJ8&9on2SVTWEtE~ohW_S1R*(} zGnC(Dey^ud=GZ13Df<t*?^e#HB=(*QrFM zc0M2rZML3(*!rn5C>om6*m7$SGZ&2u6o()n|CgZKGn(LREu+BM@;Xv|m^yz&)+s7i z{iil17uZMP{6=@juaL4ud!(_d>cK@*t*`yi)i(NYi4G>3PK;qVsimII{B47IBODT( zUaa`HW)R#a#s%)2@)9r#THp!L2WS;)?BM)dIV$;RQy|coghS*bDs!FYlum^K|aIk0!^G=($|F}FOocZ6*28lXfcXp2MS73Yv;*JGSZ7>Et%(5l; zZ~Ha@WMw$Ml3Q;`fw&P|8i1n4*n?pGzS=SIc;D-GztAsQv*S3hw(W-TXu|`(5=}iJ z#!PXTj#8$KunLqD#?^M(+4(mU^NFTy$9O*qJpJ$c`h)-*NzQw02`In4S8ezu5Zph_ zp%$)+jMlU64Dl;LSxDfOY=iG7z$YI!w`}4O(;(LX!a2i2NzA z?OqV4l`i=ddataM>g4Jx)QN+yzYu%)HW<7Z_(Fd1V|^FG;t*s=()@hB1jT9=sOGJ` z#ce5>4i~NQx2{HolG(a@>@LHu+WFX4@02AorxDW}y*pV6kMGgP$f!JQ0gi~_%SgtkjZ^aOx?9@W0G4+u#L z5tcuSPQJA~ZU4Yq7NCe-O700R9 zumuRIziOf)74m7rzK=zL_nYk19-mL|SHtM)w6gR|N62C{l;w6B16KJCC-A0wn6@`fSI=#6&{5!4 zJ*Fk3`iH`)9_w)>qS-t{ctV{_o`G3WHz}(Nwqjx(H|Kf|_`gVLjpAoVO7`*Z4Iuvu zSZI%FrvBwiSKfc$%=$2=4tUZ_<(M zb^?n3$)}*dd<+LaJa*6E!gB-C-`fL2%pD(^0|Ro7S2$>c9ZoBp#c2K31TPjIEAx0x z+@bte?>sVPV+xleW%J@?nQH}@E4a_^C+jPPlqNrE|r;%u!anV$I%HSD1z1rFg zJIL8x*`efHD+VfNM+fHJUZBE9A$5Pc?{~RzK1c2om4uyCAkwu)=~H-=CGfXJ6O+7A zq@mr$X7_<&c7`usutI`3^BA+^-#9!&R=;kJAD|Gz5paq0A@1V_PJF z<@?kAfd94eE$jF8#D^Y7(ur`<{3mP{e&-2J*_W7`1I!#G@#72Qfnd7bgT~?W#!rc! zuEa9T_i0?q$QNedeJY{=kST&=pwcG03JS0>|HQU(i=tj`77{&VZCAx@bAt=lOmaB5 zahYDP25t+l!|gO*bRgLN5SQn@d6v-93I5U2)8La8maNIspShaYn+f9pLnFFo<0p!m z)QpvSHVkbB&%gu2>_6;Y{e3_fFoQg zcD@Z3V>{D2MT~iD!GNa**(-Gm>nn1!Aa%dM_7^L84*hrTB&!=nP=sb(hRZ!8#@MPx zrc*Ksv6R9js)b1BLG$OKRq#KjsaOrkfcegB8p|}M@Y|4XUK-tTxD5oG4VsS7S~k==b^PG z_~K@{HU|pGFHG|P)i3=07h{984Ou^ekjeTL)g7zRc@Rlo(6zUCAcr+&Z5byf_p&kQ z&5fcX*!NE~0T@s%E+7nmn@$Edf}+9 zcj5`Dp`ECDbBeDg5~O|nPW(q5`{T*BSqP3V`*T`$8Ykw5fLxLjj7~EMK3yRsr`Eo9 zA94&jWGD2w??cZ!`A?pg1fP_cw{d^r7ej)VB=!xJaM6yM>6$H6I|!y!wES4j0q|nYPvUKRAf#z8e5Pp1e-5{8iXs1cV{GR$c-H^@nXwGW zfUxjkv1S_jrlD=o^Bv-}Wv!FkSR8U918HON!}esndeSx4C+Mj~QCVXS7oQEpXw(RR zwBBfb;`$CgM)ynPn?MLw_5Ksp&uRnG+y8QRdw`oqe0g+W9QJ)IpJ3p-E2LzVC{`>b~>6;CJz@&nF~*V_Y=sNNQ9};gkk9)qSFr z)9~brw*p2l;kF@jZ7>CLb0e2RBk4xn6=EHuPDAF%{EYzPZ4DsXs6TL>9~*Yt_~qc$ zUFUn5waXoLxtu||yliDJ_TC1|@fgqE6V$~ zzREG_IYwPUKOa;%Q^MfyvCSsmBdmLbzRe{5UC^mnQ9JwfGWQ%W`|Q1!XydbR$-nMg zpqapMi*sY*(SBI%X541SNpR-%Tg^suDTm7Xa)A5Q`ej0nVW8i_G5pR1!RuyWCLu{Q z*&PPvICk!^$h{%HVYQzuu}0xn5I>G#G9f8Lws zBH;-kY2`Cl?%F7PE1Ro2v2xf@e%8!$uieaZ^;6C}ZeZ1I|7H!xRzL%vO8gRi$|xe@ zu`oRqzBY~uv3t^rNDT+qh}`&(mQ(gfQWsel=8S8kC7|8{OME)*;A=}sRjVK(Q1iDD8g1_h_|L)73csg*H5=iA(06=Z0c=1<6kHq*LgIibJ5IihvAz}Ipn#3E8Q$tOJQIap% z7#w0Eii!kSUXs-#Y<*y*SMEc3koIM>SG+h!%=2IU8pJc7uekbLj< zFwQwqac>>m5CMTPp!*NzO^GZ=IV`0npx|oqifO6#S0I6eS%2*0e}JDJ~D)+qT;kRzZpRB6SrO&hhAaV&da zeJA`=6R2HPbJMTBco}rf{}sf5=`5e_T1$z?DWJ4g&SWf8(KzkWz@!v#l!HF%$0bWl*{@Dx7s2Hi_Ss&DR7_n4`hMjL5;2=NPJF_c$OEnYvdp>x!z9zi0L!*MnIHF2 z-CR(oXUHX!iHWIkch_sVysV7Z-QC^M%;sVUi!{T`253>ih>*+j^%%NmfB*iyvAH=A z_y)R0Mo+F~bGKPsnObpM_Db#y@M?8Wd${rWl~7CD^g1BbvMl(kRt-G?jJ6Mfjo_bt zwXpv`^JuBo oLvf!U&VO_pfpfa?L)T6oJ++XQUcoXrm=M`HeEc2T)+Uo!@H9xG9|Zsmk!p2)cpuBi{UilV0GI)WPzq2tNzf@2Q9BZ~u#Z#Tlf?^5 zH0mxnu%>FR4?MGQZqFv$YRE6pIIP1!3nh{eMs9FnV<^7Gk3ks2fjMhFj@bPix|{3q zO_cS>q|-8IzD8ICYD0XZ619R-%D(m?NAb5gfB~vHmf{I=#G4QL=vnBgm^8l?wmw{@ zXA|-@ZzqZtq-un+a2vQuDh(iM_4Lx8j&bub1j9FOIl^Yk!04T#TslGeEyyAqWIKn_4xbAHWzLhAK zp@`I?g%1WoXh(zG11uEy-E__l^~VMln`wPx{p11+E#Ldr^l@{laph*RBs6~;N(!AD07}Sl%vk8zib%h^Xdcxt3GZ=k4auE1gbTaL zA2;w1$~2f(8}Up{coRel+Qv9%wDkJpiku{vCtnS8&D>4>Y^{_ds|dLJFauJyqCBXu z6nF)yQ~k`5B1`a)9;fweG8ePr(px_E_ivzRF1^5`-&PwLJa>_0V{vrx@y?EBw}r1n z+8;Y33d4E`^dP;qQ^DCcH0C(8z?34D`Nq zg@_Bt9Z@FYg*KMOCz z;lhp3%zRB4MXj&_gK}+3XA-TzU=^S^(tp7ljCuq2ueckAW4>RlA)sfwhPs@VH}+kJ z562`5PBkawsqFHSih78K!So1$yP!U37eG8U$g*wZh1L}KFV>_2A?B>n0M=}Baq@)I z(D!ljjeHM%MbDPzIZKS$fRl=tL5ao#qfH2~sHS>L1u!xFaY{ zViv>QNNW;S28<>CQ<_AU8#373f`<{V?GRMJy~p`qoEao@XeLAHf`%WdnKQ_Tgv!Uk z!woB1a~wG>uE*lG6@3!YD-eegBP@P2vC9a6rRd+0C{(fHdY|{ZUWLZi>X=9{?rM$6 z;_21GHFW{u>q0Xt^Q)+j_h2A&aut>)xYww)D6D@o8&*QsYrLeCXpR>}dNmqvb+c!X zgOHFgzp9E=dTw^Md2leEsjojgSus>0ipAv%TzqN1)^Vn=r@6g7Br7WmqF4UvXGt~8 zQJfQHcsTL5hL*3CsEOq*&&d@xjqHG|57yUjuHG$Wp1f1mHR+VXC|#V|Gov&;ukKdy z^3^pvt75$_tK#wbc|q$V(#{05`L0zTZp!Bjq}io2+qIP2_ZR7%np^=BgZJbDm;jeAfhP0)n<) zsnA==2$UON@lUe^(!L>=eieOSc-T57;9!ClQ(`-!iMWZ;WV&Py=V-Z`=&3OKL<}j#fQquntWl`Bj~U%3 zNR!jLZD2-}M{&tbv-kZMUHFuDDUMi)oG&RwTD9_-FaI4XBJcrkX3@%Zd3nd2fnzrG zSUQ4;6ZeA6b8b!Ju`@cZ9RP*^iPkh=<=c56W`KkD+XfyeWMHPFa;?os&yJavagfE*fH>BR`s@T;%l5Jyr=jRD>)Va)M1e; zGhdDC_;uKSyx%70aa>&7^W}_d+0C1qrSGlJKz$=QJ;`f~4;7XHP0p@$F72W2O~VF2 zoLUNyb=utV(hR)%rz*>jUt=y&FZ%E|Jr*}gGVhPl$e!KM;Qz&!BclhpTXU@ln|c-d zkMPKcXrt`%r+1U#uiHg0&g&~Dpmr(xNaTpOPnq?ASipXhch#6<8Yms_HNVa)2jh413_ zpo0$$nKD~e4nA_gUcKPJ@~1L@NLb?RAASjzF+d2{Z~~M^+|yNvDDk{2bU+EHj%LGo zznDQ$^id?z$ce zZf@JNvnCrrm>yFs*G5KBtlrFF#7|puqy(0EJg_bmpmPr}8I_J-X>A3q zW+OJD=Y8ch*{SC5Lp1N|+K87Ap_GhYEeYyvk(BLq zNw4!~d$c}GcT#|QHK?5fUt^Uh)%jPkI*|!Dcp>JaJXoMF=Gq}I8QTLjNPrRJCN+C7fO*n?x{%!ML`kU0=HWP^82uZ=Ixa4yQCI%LD= zb|R-!^7>DwG8d3{d1xc$cXuD$USyr!#B6V}dR)+oJ&aEWm1cv0djf@w4h*)e5qs*S zMafR2KdDJc;6<>iH?pu>_z|VD#fv43=rL<4DDtEhSzD&}b zyoLsP2t`-aJN31@cOdUsm4pk?>a3Qm6=7e#nK}LzHLu}1*(0L{-o-*<29y>sSJ!u+=jSUlb`OVN{gmhjC0pwV(^4^X@lBp>aJ;C$PICV^qHxhD|3iJ$+ z1u-74a3#A$Wr>-vfjcnvAO)<$89DefY=?Q*LuB?}S}sk`mhAk7+b5-WHXOnu-PWAU z@#!-F+1hDAF(4!ux-cs2g+&@i-46IIx~h1l9PbtWq&*Lr;MCLC+6C2uM0 zG31q$l+4f1r)ra|s;uNMyml$3E$#jE%EXRBGUZOxnhN7mnU!f|Y|lcdYlqYbIr9>2 zTZN(83z{(SPg~mtRf{<*pnk;=r{r%%%cas*%q+?|J&(0APtDxDC^1vS#KNLrm-Mau zi`?0{;{NQ3I|Hv~UM1T_uU#6rn|Z841}A0So<$-N#59+Uf7ej{T_|yXV4)Acru9L> zCo^E7AkO%|K3q>r^mi#UD<1bAc)EzJiy6H}I*_~^%|b%~8F9I`{D zOi&O1NZnt{@rUl%{${i77tI@RxSC-lvd!zcmM#-})u`MGM+-NVcwC#(#MdCpO!%El zXAv(Y<27CDCmF$$3wJ%t+=K5wC3_bM-jiN4jjoeYI|C|n78Z*&+yjup`yU-o9rSBZ zGZi~ypnaK=ycNQWDrQz%jb36N>Xy%%XfDaqb3NTVb=9UW9_sKKY8@7T+St2DC?0w6 zpG_`c>SIXn%TLRuxrb~hs+*m{G+sB%qGtoSqOwWi`DNnAVAujfBDR*~!JnrLhxC2o zzlh3omq{_(IOlFNdM&nU2g(I+Xrk-1OR~Al;j)Jz^x*rFYtYOO>H+D0E&m@JDR9!W zEr>lR02eB(AhGlu5oc^GDlMnB#M7vE-=eKGFINt zV6K(O;`BL-LJpME;yJuV>gz-lJL*ZlM~>(a0Gw<9-)e_b#fG_{%{{^AMR-xgqu?WJ z3j-^{D(_$4Qrs)eyc@jn&fJdr!%e>gx+7Bf9?eh{>pWYNw8b~0Lx%;0yo@I?^Nj_9C2xy!K>f{ykT=I))h z+nguzk0RFv`S(4?O|=Ru#0o6pvlH-jeFIxmLSJzQ0MTv?_ozCxZ33!P=@h@Cv_k8v zH@8=xG`ER*#Oiz2k8>1GeL+p9Nw@C7oZT1usy6Oze_KOodd)?wDQ}0CsE1O5luYy= zoIYb$P?%vNp|}Lx<67P-?}gzr?~51i00+4_be#(W=lE+^$7P4MKQI-{MR@SlOJs>Z zCnx7(wpN5i!O0ajX}7A*8yBFqja^+OZ&-DL;-4~s$ zB0Ue|2fC(LQ|`E&s4&#NmR;C=o_n&-o(LpVWBoev<{~Nw($NHy(nfGw%&t zI*miYS82k4Pz+yu3DHmkyMN^-B)lz>`7^(UDox$Vh`{$HAT_UnI$L^06TInrBT`NzU=tY2q&TjBS$ot+*g10ErVn#8n(cJcq@$f zWa-Mqd!nndT|?0L;nOIwZ;xfq2?3HT^R178B(%r)Boakn*Mw!b3}sR+CO|5U=VvPI z$h|?zrO?|)@^8xZjow<_otU$bxLNx_YK>qrWnp@3&Z7iT4b?a9as3Eqz&I5U7M%={ zc89w4_9*~BT3jgE(s0J!MBE>kUZ`EA-3W?W2&=UZ{#+@`x)epA4Wi6zFt}HjI(RrF zx;C8u5@btAN&e&6l87-8JbOaY-KM*g)D9-+U(+0`RbM2bXLCb0)qtk5bnQg5Y{6*7 z2OxSUqNs$?Vx!>rpHVQIIujgxN%ni6$gjPkfuIFJ#l;JH4oPZP6B_JXKi8?#P#WMP zJ$wg*<&@Wzj3xIVh*cK*{;SDt-{zqa4nWlF^DH}l`JFKEMglly76i!el=+IeI zsQWfWZ1wx^i31>V8IecxT_iwuS-xIk94($cZsr48Ep3@w%cRPx>1h%89#sDjy#M2J z5OuD>cr*IBhrA9cuV%i2PHS?_7)Y$`8|bF+_{n23y55^m5(5ujL;K}Ac;yrANB*N2 z1OXDI0o(fPQwg9qYF3J#wKMoS!F??lw83GLTS8Fr``b^Dg5lJIXoR{UUi$oxIv$WH zHbglQCYqjY^Lr?Un1DO>P&WFO0BC4q(}lMu`_wbsOKMw38c9-{l>BqcU6_iv>(ryC z;4v+bmMdq>r%Xxsh6l-tK*WyuFzbxVyf`F zGk}!uRuQks%VAR(6BSz#cT>^r^Gm33q|jkEdB?p=Lh6xW4sSN7C(T4U_1g*2g0X(m z&JtbLhX?Ho^*X1EtOl$U2ZZNhi)OSgx_SOD@0X(Y3smA`V3g~M`@r& zPrd^+Is`xQziCmlA^~!cidgS~JWfHDKoH4|6zZj3^E+K{Vf#x+jVmWQPIY zC+?@m{Es3j)(mU9mUmg-N8XkP%q6mVV&swf0!pzI;G9aiem)jXm6s7E_%MumIjrZ3 z%F+LAt0%GQBeu-gAB?f`Jz)YeS3aufgwv3s<1j@!Sz&urbbbau2bmH8gS2pAKtvAB zTNY8?2+R#=q4$%{2P;L=US_*oGCOr~s{sEYFZ#SOE=5yMZ7@c@8IK(l+5QLZVF!e^ z!zy<|8CHq_uW z=4dl~9erdok-8mN&df|-6;u{{1gh&HAbxuMKLW9W2ts%;fhF3y$!J`jtm;|Ye1Nl1 zb|O13iztNc$F@@v0ZwStm>B%GJ2VB2^+XTKTl^tlzkjO0)C-@Yh+4difx6#Nv?3MFjS|eLCb@2JcLahk5 z3icH@&-v~f$O^3|UzM*|7nYumIc#+{rcCSBU*^={_DWb)*bjmd;6?e6Inv*q-G8Rhiu2ZJ_+*E$Pm;rReWis+v^t{=8j zK)xMV6CNVPZ1|VE??pkBun|v89`13xU^RURVY&XsK(j3~c-<}6ZM}XK%*NHIUeea(aWWBv?QvrwG2VB8m&73|#>@s4 zQ1JH#uf@!+ZF)lU7Psp%jZWscO$M2n+88*0k9+B{YQL+s+x?j}Qc}h>C+qZ7=}LRC z9DpB-f0hq;;)j_PdHs zn(!q2?>RW81c(lLmPj1(yi*X7U}R6&oSgsA>8U#y1l1Lc4f92R{A=8f`yH9#k| zz&deGY!WZ$qV#+ZK?zXu+K@|bTonF#^g@&Q`Xr-IcO^Xx=e z5`#GWh*2nA;stqP=#`VITzXMwaV~%GFNr@zUd9z&0hPMz&c4+l1Gk8{2+~f+EkF)H zu-$IUnS@^h=dJ5bWLGZ_4Zz--`*rhki6x`Qliv0V&;K@KXYZ;cd z1Sl!$;I!xB$RvJqmLkKkV!RLo7j7kefV!v^oZ?v#K+g^kbD5o2*PGzbyKqXV&~0|I zYDVk^w(I?Zv<|lCy&7Z*YA68d>!*nP& z0KcADed|q+*06{Tc2W)Yy?Vi}h%mW|Cn`V&;wWfOGYe}!>goGy-SPb_G#It2!g~UM zJ%S8ZC~`r_-yz`NGgIG596k zaTKM0938W-!P*2!vMQ6zT%roJ2_P_PUDv1yl9)S$<;d2w+&k|G#PlA_LF{QSH6B~; z7+B|`3{1@9mk?1vB_&gMI1g@$fP;nNqAsJ-a1M;4gXv;Sv;TJjOOQm8tv0)%Oo2E> z&l3IuCzXS?U-4Ti2eeU~_1R}6a8LsWKeEI-9mb*10sR>md`S~0+FvsD2y7J0?diij ze0b?+lzQeGaCM%@x6bEogwpTIxx35ovIrxB8z>%F@U$$dK;E6tkU3NMc>oi50ky+* zX+T_$0ddk@gjWy$a_GHbK!O*EG0+4)I%0~81dZFoWZ`}suIV0rZ|GWJhwKA0#LdyI z>LeM|9Y}nIIw{?-uv#;-(`^6K?m_k-TALtXMquyu56d$bO+q`elL~%=+Ly6iI(FQY z9}`Ja2WH6rNT&Rxg~d&Hb{9dMuuxc&G*KTS1DFsnM2+PO6BDqz+ThMgMl39UnV9|2 zIG_@)4>GgC3g-(kS#(NU5(7+1@)-|1cT`2iKpGFYf`X7MNMZN9d0W}ZmbgXBTR7k2 zx&=~S7ZQ1&IT@l95KsfA9;48{fd>RCpH8@UJvU~1w5(o+!Hpci3Jj+ObTI*?1mVF0 z!p|HQJ}PTR?%M{^w=8KzQG*WXVw~g~RTl_A7i-I=Z-c`$xN{1&(l9w*=>OgYFC@u% z4Fbd;P`C~T5dfG4tfpNaK}Wg(q4UGe9_Ia}@`At%@8pS@7nWL7<Z4$ z>bOxq>IO#JvU{txen(ok1)sP-S>edst$r`@=z$ zI0e|-)MtkdtPcX#*BniEe*zNtt9(Gp1pD;`zWYNZDUL2J{a#+d{E#!+)8fsr=x0%B zSM@I_8efrFB_=)Y$K%fqS?rOi>t>P=>VV?vZL`~&06r*z+RLk3Cj4=&$>6U*;kKLy zfpw<>=Hz^`23T`od57Zh%LhHRqoA~`ZgtB&{tm!71hWwvhTpyfS{M%0{n0kwlgas- zz4O(=onLi+Y!@0T1>W7l0)1f|w~I9chGaAevt_k+ScorReEJ-R%jS8r9PB+1uh#8c zOCSBce!a0TUl6NnYsud}e>UhF7>MB1({o&&=)8f6m%5|e{#2Z^A%r<1E{<7OS2qld z?wp>UhK3pS*(z5Uh7Q`FX6OXfu~$M>v1ctxT$%Tx)>=7t-%YMmZW-M;;GpXBmlhVM$ z3yS?A2|v6S1zyBR#Iyt+r_JPrn3a)zmmZ|$K6beN;jikQI z+Twuh_3r52;!q1}sU{~Z@Z$+h-o<)>gvTgU!(7YO?s}yrVE|0YH|Q-S9Y+I!0ehxO z8~59QrS2QBr{?G}K?>88U*TkW*~U>Em`C(%A~0(anXe{1`3z(OZX#p}5U~KwO5${q z4d?6VIO9tRp1noIN^L`z@d7Z0uzmZQ&>pA5U1x2trSl2lL%WR$jZZo}kA3I_p6YNC z*5CF8HA&s)-SU>da>j_0024s&-0lYeM2(OKhJ4YQK^nx+T zsD(pO8Pi3gAIEIsJ$Y`yG~-L*PnFok(g+fe$vp1Bj#KMoEz63 zFHoj7r!Xi&@qY8V`CFlq*v!iDbx!KVW8|7U008r(Vcu#9=20z#}eY-&ewUM-J zJ#Gl|2gaRhME98*^mLzq!~1%;QR@$($p^{`;A0S^Iuy)4GS7DQDT0Z2eGMVSXHMbB zMZ<;9vs;NEk~m*oy^)erjnvB=kBAk!{>+3CnH zsDA`la=Qo+_Hlq({kvd)>D~XiU|+XJOETb(022~0 zr(f*{Re3;&j!t`YVy_f?Y;^@-E6ZRdTeD@@QbBjF>c9TghJC*;}+y8in`RXDynkSC(k@wOT)lXeiCPfAw}SD-~_UH-7C6|ATRoa8yp@&}JZG+m1+OL!4-Iq%+0JeBeS z)38AQn!wQ_a_o4hA5$f(uY;T5`?!%*{6pS|5Q)K9kwbn3a}(Il$4h?tO6FD_`V^H=uJ@=D`2!ttNpl z9RV8lr%u&Mey*CDCgxQp0!tOwl&TlsY1mZB1pOJiEPS2|BNTWvxr@|{o?%C5?3gz& zP|SmZ(Fpjq6O=bn2V;DYotEgGUw%21Xy4y>$iVcAfi3av3llWlABi#Vcm+?ViuW%) z8Rz`|&#L$E`nX!W#4*eEr(vcIAx-}$+V#EvJM9{pjE|_M1^6tGZwtjWhz0P{^$ECc zKWbC}dn2KK(mYrR7=~AIpE(v|HG|9a?AjX&=g$s-n+P=B&HDqB9NogQr_&BV^hMhM?CH7C#{&5Sw}8Ubh|?~EsX$=E(CPW0_7Omv0}~1RcvW}=O03iE zr7mtqX@%uw9TRpu_eF&pgk&HNshSs?{mJ`;hX0~TaKO;u&!EBFYt_KHvG21quvsto zqdg6aDvsHgFI-)WnKt5g?A6GoJ@Z0qZ222Y7~|mb*}iK=f9*Z{fj*cF5Q0+78?lGE z=USz^@E{SSA_%#&OmOX9LHwiO1Bd>2jA%3zr~3=`feZu8yPBvDXY7gBckYcKSpM`Ut3?plZg6>@oaIHt>qjWXJNRP>#D&=|c+LUsx&060j?`<0L^-@2ICGuL9B%r1L-R5o%BRo?;vgutjt zGfU|tl+x9)jC8W`B61J?-BFO-oxP%_^g-oRrVG$h>?ETfSc{$e9fFS(1O*3qla}=p zU#{m81#AQJ2;970MPZ5XoU8P#vm#QOfz}H6*jybBzs=o4*pDKRl0kwse$05cxa?YX zF{F>5(su1Nl}!aD_M1AmR1^P)?6NANN8;(-trl}Af;f$G_h(c`JNsO1nzdDqi37L zt7kw-e7yW7SfK~l#$zaq(O+q88I$iQ(c~WN}^z3Uk zMCBY125qw@n5*VZm+mUiXiIYkn=Z!6p!Y4T`N04up$~Wn5=q4I9M+)Y>2mJX8$R0J zLV5R0{T5=PtrJYRu#y1A&eLEg>n`|39)>i9$PWQpQE!5|7rd7;dcK={ow=zUZ(W!S zb(`f{lXD+s15cJY=5Dwk9HymLW=(x>_scf|oG~Ky3AdZCeD?JK+$?+_0UYo_^GTm6 z4dbz_ceyH4nXefv7>^1g2>LC*s^}DWK(m^YUK3m4c$WaAYCU{L5}IYEE(Yh6dlmX> zgmds^6Eld+u6TVfv&Y2VT-TzEU{3m!qq}CB;ELQWE8YnNGm@>#l>bPtdZYJmz*rcu z_=%l$$5{zZ!!rnUnw;t-{!j2r5tsr5=S-fOl6=a2Yi@gQj<)=kP?UD1)J&G zogHVbobX*t-dmR(!#BF)8}$M+%vE#c?jY^PR*uw3`>i<1anFQO?Ud`&4q+8x-;7(mQ z|KKbZL@<%nn^S@MYulNel#ZY$^8SZ(>cr&~0d8k7s#}^3wVY0#_L*kpm!a7QN`T$` z&9FoBW8ca=W!$HZ=?y=)KVD0U5vHS%^)vhW+?1FP3h^#FSv>%k6S+Qidjk4xQbz>o zK@nWO6;~FXSSNO?+Mfj_v(QUOxUe8 zyWN8#xJTc3gE>!v?q4K*J4eHB^ZL^s5P@%7)cM0ojKVbHim5qR)QX641{QJNnybR0 zB5B-G#JcKbYM`{|KdKQX~8`~mMwv^mO;$4tAqrTNwaFIv_WGhT={V;|< zmxtv33)D6Q=t34~@adWEn+?PHzr2<7F9-hx~4T#Lx z(Xb}Dhj;#5<;T|Ffm4GQqy;6?vf6BM=fVE5tas`(C&o^ob-{SqF?hS6p$P9;si)>Y zd)<5x7C?krCv+%(nS$S&1gR%r6C8&QxsJUt?||hQ+`U#yWp7yysLh-nc3!7SK4n28 zzv}Kj$Y;V?0%3dfal6%@^DKYq6U7gd83Tlw5Gums$EX*?RNehOx|nY_^jwhHV_yXr zg8&t__nqLQAl_y^tzy`;G?P)6Eg4P;FjbDjrsluj_Lom#v42`JFo2r=qbWVR89LyS zvy1M_m5Y5co{l^mEtF8hWz$)HT@v6nfG>L7GSB>J?|vVWD5}a{6t$4x@=}w(+z9af zu4OBri0tDpZN0gD$-3~(T%nu6i^nhCL*Ie1*2obB!>!4^7!GIjOm*p7DQ9avQ7B+< zQxLh9-e;@Ol5Dx;W`69NsI^|Ud`{+>g3k)erGPiL6|{OeQ_W0+P);7O=`^wp~XU**FE@8hjBoDrg5G-6(+z~`c#)*H%#0-7oa6mRrI(p$8 zFxx3;lLmfvK~OF6c?17-f4>9>Eda~_EBGu2oXS0bBB8K&?s%xAk>9bw0o&^zA$5_H z5;|)%a@|iD>+x5r_wwWAgUdI2_EzG63%vpE+JgwQb?MS2Rs5u@)J2kCV=jMr{m>Hb z049mx&DW`fQld>G{8HPN8(mqJLhf5l+cY%z+`b6O(!#NhuWT5g6kN12T7M9;&l=1WOu@`Zb0y&AFUw)J| zTe{M8C4M{j9Zy=*d5Cn0d^RNUoeAd^BtT*hBzwSU6?XX7}^($94FmD9zVZ}A<{*1(i&hX{cu96 z<6|hqo1wB0$NLKAy$GxIfB!jQ@Apn}^?}PMgBJiwT?6_m3u1+Bd1iXECgocztQrvV z?6zVaKZs^)YQ=~RaN2Pr^F7bevx~Ryffk~FNH9Dcu~Gg1D9T~scRzc`yA_-=x-$<- zQvE(V%-fg4ICk0d8JS-@Kk0s+su4zcJFnw4N;;BK*wJ0u@YuWPjHKH9$w)qYlBOVN zX63ai%#89{R=``;`g-n%&@&e~v|OW@2XV=WKd;{J{NkRh``RLBOX1uh&#UIr<;4{b z2L=;A+O4e8zj|-I5N=0$j$8B@-`u0W|dP-i-{HZHU$j7IDasf0> zI-5xek3{ea&#ZqG@WLaaU_+o`-et0HD`|b6#@X#6x3<4$ZR5`7dhtc_rggM)n+WUf zxNB@bTkCUu{koj!+c>M2Q6y^HVAs&lFQ`fWbt(9K1o)%8lvMkP_PE42Ygo?St#X~! zsm)UTV*OPQ4b=p-z!}5H?t0e9Cny!e^0#A2<;`1nFK@#)tWiNjBcaVf2_xh!#VBo* zii|)4@2+~e3*YXoa+jA;{9cv?A6SEEJ81X#S%MObb{C%vrzdz3qt=%ao}lIp`IdI< zQH<+FKOWT?|Zynswr!OHi!a86H z!Wj$CKn~apZ8CZ6(0gJc1SuKLkE?+1|G@SHO^jiJH_l5VVhk&Xq+e=izl&|p`S_wv zds?eDKLeI=^JPMtGubT${0pB46K}E-@*wcoJ}Y1f4=wY)I|JKT+kJ~-*!_xHbFOK# zHH>6GyAj<+JIuPX+~JG*7FtQRw{EnqOSB{(@j#FdxPmZb;6u{Mb zf9yumLg`XH$F{HRnq_U(M$QiJaKcu@?#Q_YExfaP!>ENJAJm@gR!&W~4ZQK3%TeyX z?ojb41$vnY?Xi?OpFWh$gV1>LZIt8A3U%~t9|d3HJ5MHK--?R+Ej@QbziLU>IF%3+ z+53u74!SmRRh0p*cUp7eyE2zoLTb{0+H?Bl9acy^0?FelXqNc0J z_F9H^BGmDYTtWqI8Sh4u%M$HP8SPruRP73E7Ys*`C)mwCE3s#+mlmzsim0O9{k*qc zMY`ARTt%Wy+qPOb>7^U;mwP3mn9xSRx^BZ9gIU-z*xxNLZXl0DIxNF76l5=PCdl-c zrw@L?{ifXjK8*lx^giD>f|pN1y(hG2DZ6`OyJc@#mVS36Ve9@aR|mtbm<7|-z0EyC z-@RSbj-AU!*&b)P+ZH+5U;{_%`qEzG@!^EMra#^|K4Pv~}| z*At^l#q3g^b3EI%d++>*rxug=hO#Pedd!~vm!S2-GG#xrv`Y|@xq&RFcnXvJ-K!UTXj)t_%EdB+_DUpvfY|OkO zG2Cn!jh5)8is^NAI0ua0xYteTfMpbEDbQm?h{ZLSmD`5b8hXkNzv894n@3^!=MU}~ zFs0AlS<~A$?bQc6;LvlGbGCjwgvDRziGwdDZdO%~_Dj0|xa%=$>}r85Y?pG-3AcRD z4L(gS>Ppdo3wN=wWZ?%HTyK{8(ok>-d zT4Le=Bf*9T_)t3V}v<%eOXuf9Kgr zha1`}MbZf6$O4MZNw`M#yBo0qDT7&@t@aWuBQKwWeKDEnxE(s+n(mWe#n^NX9?o#r z-|({R&4(8SXxK6&HSfg9ewC63$i=lnX)NRqPZTj_iOVBUsqdJvXFu1v`rx6q_`% zdwMS?bY>AEyYW&Co-Acqy3Q_yWx>QHNru3u%GVbfsu;oN=L_bWmegv%Fby$TS#q@m znZp3u^bHJrgMxwe8>ZGkZ=cx!)MTJz=->z5Nepgaaakyn%_-TUgyqWN! zpmE_=+NE55z8Vi1@}NbBqme=8QG=g+#vLisx^<)4(oAGxeL>m7F?!LVQX(wqTY7<& zxHNBMTt~AVO$TjDds=sHISty3x%#Z?-_HhQh6}tk6CAk)gic^+jzyv5WnzOiV^NtK zV3NLPZY*t|TO}Bm*;*q999UprNG)y#4c$Rr2{^jP?-ls1rD4TsWvFDxVBi2bLL^52 zd^P$7n#Ht@#$0hlyHuqDV$91!iajfe+q;>7+TQhj`i;il)E;^R)qTX7G=z7pEV^6o zmKMn50=PZ&CqwsCh`G zXXDaPqP4G5ndtX-g{yn^xsZ@P<7{3$=+Kw3Fe{!yh%9p#yu)CV9*6Q##z(%eNSV;8 z;5j8eWjf$9M)UxTtA-=f$tigM<)A=WD1pR=z*5}tk+Ur;mjpV$@N{enQoxu=kn0V71 zmE@&=y+kY&1GNJ6r^)dp8ITp{I%VgMessU8ADHrrRs=@ArROw;x6_{xu6?#^eltFTMdM6u*~q;P}MGQut+1)oif!saWBBSX7ATu zJ4moDVg(BlduQ@A)#Qn~rBF@ZCaV<~{g<5_viK>GG3J0xF2haDameM3qwuz#O^E-^ zNxnt34;H216P$4wvn!+z?&o;KW!p%NzW&sG>RCw-Zpvrfk9LIlYxBrLl#=`f2d}nd ztQB5*`^TRAjv}wtl2@P%6Mkq^EO^@Uu*J_U`91Yt(`Oys2PNeTD^-Rj7B{tOH27KF z?`XQaeV-WaT*=i2-(%*8KlPpRf|O~fHY9X!SLMx6e|MZHiG#mL3=m!Y-t_={B^l%@ zhfo6xiykj|Cmf1P6)0+74qYV2P<}hE7kzo(bC(Y57ISp4B#lU!wXu2oX`x z#bL!byo#_9NZf#-caCA0+NC+wTeB-Ls~%ISJ43ydWP(@lKAe#2w-&jgYY5gda~isE zH{gJ65rA#^!N$T?VJH2+g#r`gx}=Kqhh%qR@rp72r=c6BL6QkE~vGCDO}Qs7p@J+ z#n2GZTMWA7n7eCVrdF1|ypvLa>_rd!hUo#)sKA4TOsQpHz2A6D&}@{BXVM(Y?5XI% zHCuZ**6%&~=(SH8t@T6z-i|WcU z(eS$`bLHDRENr+>eUG!`Ou!v^fuO%y2tQym*g%T{ZMy>N+wuEadXL3urust4Z7zpT zJvQ%g&3LO%lIPsqoUqj2xK~1eC=dnxlQ6R=hqz+I_8gL{$Z4Nb1evurQ0*5M#a)tvi$^@i}#BngQ;6*B= zhE|C^Rsub&OAYRICxJH}o0^6VFe8M+t*P1Vj>+C>=~*VY47sXbr{88CAb4Ug_mvC* zP>6HsUEo7fX*q&9c$fCm@RKr1gPqW8jlDJ!F?{&1f>tevH3`UkLgZ8hi>tTXBC-1* z?^eYs?70zK-Qb16D=fFT&&2i*|x;aRtmvcB=TvBOcN zUl}VtRiD%fAsxg`i2)NRqF%MtPF<&fUfscqxsUgNb#_1i|1&Il+Yc1z*y)Uo%pU2j z5lhygNYPR7wb#$j%2Rm?FJI{nc%J63II&(1>B1DX=nu&GQ!%U0Q&6DIh!6y`@UDqP zH>UE8coup$m(xnjMR#-fkbUv(U}ttuyZEY_4802q?pZRbLwCjQ;VMpWz~)ZwE9KV6 zQ5|t&>bD-g(t`{OZ4)&P`h~!%kpES{+zq=;H z8nW1H@Px_QyQE)#X636?vXwy`@qU|`<^+(X_uipm>{gq*-K75{)#ll zD6xS>*IxulLhW8bG9*Y7)37C`YH&;Cx>u8tR=f+HxLmE41-EYBK5{gue(DdWGPb?O zR)%D0quirX=kD1!zwvAsS4Yo->;uG&?g>`h9FwFG&0*0m8lShbCMqIVS=c=5PezYK z#7WC&;0p~bLH^p?zwP?QarK?is=Io5N3lBL(zx%BtPOGdwKkGaOBk7SxR388val`G z>^Z27yhr(vNy3()@MFzm-ui&c^xHpvAoqm3mtV#9j;dPd`K6E|QQK1I+H@W9@E2lD zzhsY@-0n6v);p@aS0XuuNui%~zBv~WMcebsrC8`^EHS$jRJAG)+OIXofo?C$_ z9iGQZAA`KHp1rZTD|Aw+Z@&NL1*v!uWNosJV??JMg8{n6uFf~AdplNvth%(+i*%;} zmoc6FsOxp5-}l1@Y)k6r^W7ZGGU4wiuE0r9wtcY_dZ&JOcbe%PF4rAIG+E+hA5EiY zc~!-V!H@c7Ux3FP+mOh+`WN*Qi$Saec6S=~l7J}f%PRnm^f(yE_9*C!%1leCMo^KK>3L)9vXHTgVt`WiQxVltvoOF>HTHNeHfV8U+S$iV#NL9E5*jB zoY%##dp-h$`@p;IJWvH}Qk1OA^GXv>d7cmO&c(xOnu$Ab@3aO+&{Gkl5)zN&A5UeT}_q)T&pdrbwf>hQsvyVaD+$waoi${Bi< zX^R(N9KiK7)CoGkzF6%$Y0SI`w3y?F`wY14ie?wi__C-Buga+XwD}M%gCEExX+k}9rhXH=W06P7iG z3s!dL?DF$T)WJFFdwMVx?EiXs7)5L()CY2nAPbVp``xbWj;KM78-CR_!OIErr0;-X zTr7O3>Bazi$dSLqGzfhG~`9b>n zY+wH`;3G4My>T5M0GPY|**GJe_>uI2((55yuG;ACvn*D0s4=g-h-2bzz-9?QJ^POt zI8r$B8w;CVPIbfZ2d5n&gFO5A`QUWI#=)}-cPgYe-6}nOpkPC|)Sg4~!oRrH2sS9t zJr4`dAVhAgrbFVkrWw{L6;ITJ+S;g#glK0GXN|klJ~vwO>1&2!f+Ud~?|Y;K*rz z5!Gje$QSi@yBYZotDA&Z5F(8n8g+P8Aj|8t(Utpee!avROzB~~K0finod&mYuR^2G z?r5`@xkL_I?yRv2DjBiyS2>@rEG|@hav;yn*_{%o87Q`|jTN|0sJ zQg+$No_*i9-~E|6bvoxvo$v2?Jb$H)0lkb?u{G0mV|4FgKkqPgG+?jN@yMQ9b&2etz8oP4@{9 z^}jQI*?4CV^tfuqJHF149~s)1Fu5h)=wmqk&QhalsVe*3#6@m%5=4^!u_ZivCrk|B z;2uHETp60a;kdF|>e+ps?W2o@^@BvpY9?#nq~E`h{@iHc7o1nlshuCz@$_<8*K?_9 z)36|-rZbrU|5t~YitxBiCTtN2348v~jXzC`FeUEkQ6eB|3^akMZCIac(D>Nxi{A)> zQm)498eeNAf6lwvIXQYG!c1Al((YUU8$nHdHsJ^c{I{_*&WZA|xK+$lHEdbnpsAp` zwF^e!-k|m4mb;)Ub)#y!B@B8ezb&(|P+-czm!EsC0lZy%wi9w#5^h z`h|LB)(V+^wdG*4Glg<=)?WPGr11Eg<7Fq*FHHb@2C{MsB>E@tID#i2&RAx0&6y!k z^m{z}B2TFLKy>q_#ivPvmTcBL(x|-h79xY1bBq~yGB;nCxKHImQkgPQL_ZR4?HCL;qa-NdpcK@i_6esQ(%i^oQPj3{M*EQWd^&w zNrkF|BQ!dqh8f@3sAxXT{CLu0V_RFI`p`Ja{y?ggd`s#p8J!;Ea48CcZ%5S#1DLBL zI`Cu?9+=={u;Lp*_PEdP6(lm3CNjU)kAy>m7SVnNF4$+O5T^37GF+?u%s{4*RBJz6 z?!{ps6@EJs!z=jw5i`~wSxsZZu}`#$NMXgL3pd|PdxPDY$jTTp=(rpR{&AI3@62!j zHxg+{NDl3pW0##AVIOgF)O26&oqz7$R|}kg4U}+Y89^5nK_wR`K$6mRF9}_MhKQdJBw^8RbS?BDq+eH`U z?5H6rM3wD{5zGcL&U>$b(#=h}!I_@C!O!9Rqr)kx86T%QGAcY9BR%NOX}b@J502&y zJYjO#KJU2NWi$wE&fw;z;OnR;&jf|2C$$DN>FMbotEwb6wns+|H#CG*xs|qy)Z$<2 zT*C|u6%4UEf9vh66S1FHp|HRE>4~Wy8H&F?2weJezkrGqessA`6_Dnhz_8t3 znXuEf=g8aN41G^MSW>@qwX)s0hkr|;UqjCPGP^PXy$5h3iTihC(SoHYC&QB%(ol< z#R+suz;aI*b97OBcf)^0Ol~(o4HJC78cW9D>Z8D{+(*EK#` zgcP{}A5xOiTVr)ZVL!o|Rpda=Q?Nxr`kGyAc$St0f;i~aI8n)}-!P6YsCPZEN>pc*`5>L?Y=0+oB1HC#&;QID$mQ|FXV9jyJat{qhLN*Z~L60PE9k~2xEArti#oTi@w6d3&r}<8Afr>@Dy{8C1DM)!D|43*G9SvKMv%ajMY4~hs;-G z6aWEU<>SN@dx4Son@QoJGcR3&WuGgb3#dT5L-3eoMau$4bYkRugiXJ1c+L}(X#1a_>*I0fa4_C zn|0sld9c#HBS58|2w0T-(1db*+Qk0KwJbpaGn|#ZO^UN*7M!Tf(wKqa)i9Me!F%+M zc7$z8Q~~QBt^FCfcoZY$4hJ(vk;|c7D{rRML!ga*sJ{9>o|LLUE>?Eq{NV`tb5Tra zua#Yg5@lDb8YTzPH~Qw-q;#i#!xTQT7XySR7ntR1@_%?=hubJ_0bcOobiF3c3 ztN3B2OPuc z>+e~|kH}hKll*piJ?wb=xD(DZY$m}bqUoe!6XU9;E~cR-lIqd#W8!qF0*)cukv6I2 z(gl~~uG_qB#tPh2XvQCH&_g$OPi7(UJX9YSNdXRG(8A)zF;Df3t+qtMIPu-4sRlm_ zeJF{k&+uZ^`!5gmU8PV1xLtRK+`)X_$>;I3$41$5XFuGGm|cq0Xz#nv)0azx>7o}Y+Gu2I3L)zU$QTPP}|hcohA zXoOsXu00oQV#~L3^=F66qwO~7bhx@GVf87rUz>nD2AtT_=>0X~={&~D1VMz~1k1t{ z+U3?<>TCvLAUC#l!{7iOg@YI;0BTJn5XMc>Za$rwOGO#qh;GT2ssS3D4u?;W`=pzW zgs-mGC`TWLFx%6;lv+3*oAM2COu>UrbAr3spOSRT+q!r%^&hPRiOn}ubMr+^f&aY- zV-=to4_%*b1smrzQnnIzaaDsw8^I9A7T zg6iNGeKyY#TeiR>`hq-qhxF-LPNm91;b;fc5fY58KA@ zeE%uV+_j_d$hCfFy7#ZGMJ zhKN8C9lO4obr{dC^Wrot4A|U!$4L9DPhFM(cg4{kdKE!R<`g`ccL6f250D0wr+dVT zrp8&9%8Vr>3+f6bt|q%iS8T`Ao`?`Mo=zGN2n5jfSt9xx5dQu>*+&0&lgkc5*29KU z{>Pwz^7*<=_H$xVVYu5m(<%RPFUHf~6%DI;3^G_-(h`T&&2Rfu+wI<(ZAw?9cD$GX z%$Ui8b~UchtCd6s@=5+XA#ghmr>u8?5#yRCY8X(rfDEpoe*maj7ZS!9`}^NJv1vsj z(ZRP^fVRt-*`e-$P%8bzT43MFf5!q%DL7Y^2=f+g4;Rd@6<-@fXY7|=`;4qSqs;Ph z3zLYgWJ#6i|Kr;Uh|Im@&!-vOoLC>+!Y%41Nlhf_$eT#Gkb?jig(009m0W(ettZyl zN%Fm+%;TeM03IEmkx8bL>e*oQ&-_!yBCXPRpnCY_w2af}KYqOO?N5Yf4gEIyvdpmu zjN3I(xC5EqjuRInw%?ey5fR_s*-8cLvs{cKr2O&CzdoRdaE|>O+7(z>?zbhz z5;B_SZgyDQrV2QEiLJ_wAF{Fra$FaOlCA5UO&>aKIfAjvAF z1D&{^c+DgpA(Y3-b~fOmn7EYQ47lzWK-v0=lQ>MpB#!gaYQtR96@5U-P%IIrkvb*{ zZ{S3V_P^3LH+ttty2fd@y6okhqKtvqV>L34LS6Vst&Be)S(ncr$+p2QL?c-5O(^?$ zpziSMBOBo|$$~=Y2^{<5BmQxI(2ASh;j$bT(XKg(*NwYa;_iC@epZbNS>-C_B{_j1 zi;gp8!|wOHhgp<78XB&0)2^E|s)GHQ*g#i~eYcoWbcwtC8_l-m2Y%SC*X}s84%*d|$6{-L^V#TEf)06Ql+jTk6s=hVY|0=HzJF}JsaHo58GJS~v)Q1xXZ%pO4?{odhp z1)V!Sc8`D|PTilw5$|7M22AaDzTHxeYe+x?y%JJ;JC>k5&D@|$u!tfI@DvbWJp4Bu z#>T*IaNO=cUW0j`IzB4UrR3I<=+2@Q)SMH;+Mj>=?7(Uk00f55WLin@VQRnRh_R&K z%5#h-k23Jbawgzq;wC=-cE{y+anA(?i*a^K=r{;g7H5+7*UUMu70qvqM|qAnMtOqq zLl2yt;gKRv$Jw6Cj1Vics#v#u@7>Ali(KF{dlJ6?Bf*2Ma8eos$N zrsmm-4Rullw&$5@XDb}fQ`J|U{4~Cl$-!g2w7nJi1JdkG)g>dt?wE z+WJnfYa_K+Wx+9P+C=bRy#|5wJ<{JLEh(o6f|8r0Rd2KI4V5VxH|6NXntDji!kMKe z31|jh7GkdY2dqzA*PJfvI2(okA)d9wj8OJ zxUIC0zkZahysLPB&)kCe`ke<9c(e}6kpSU|lc5Ue42zkyc(@_le0YR_;=^OA=u{Yi zU1rZ64@oF<3o_j)%EX8nO!y@{j)~$F*4@$@fviR);-P;)FD)COaR8!|@7Zs1Xh()y z_7H8JNlZp2xY}JcCf+M{1IGcxKi$_)L zr_y2uCGF?p(O42bM!2u!SEVlyM6ID+vAS^b%?t9US3P~@khLlYo-aN~Zat&%vT{jR zbw^SG#Gr898Mq2i8-aJ>W1_FBrLffa$pe_F9~$~o9w+`!T_t3-CW>#{*7x&JvQGnw z0Hbp0fjlN9)1@yz!}LF8_JIS=>Q5t4yleCMMGsphDP`rr$a#nG6KAVVoO^HIs}z-$ z4wGpMt$dgzA=Uykp!90rVgaW>w~1{y)CDuLT$((69*=HS;B-KLhQMVY_1l8$2Q8~${~C}@V5>Q>+DXiki!X z>el=^w;n38-p+&q_omN5sJbSTr3o)$^ut*i!29_G7&f$oSlPGOkoRYtyx{kRh&A$& zH7#S3tgn89|5li6ug?aA>o6M*3!Hca_6aUG1*(B}EwW^8E0R^rZcvp0Kt~nutg&!h z%DOOK3N)yK)#wOmRQn7D%l?q&%xH+imIFF)f!(8oNm z|2L}wFqvUKpO9i8izV*A$=R|ha`AlTlZ$WlGJulCwF>FlK z)AfDY5#x7}kf8IiM_PY}XFmzs>oDvQjexVH5UNvnLujD(CP4ZwWaRh3qqQUPXxt5) zSb=iyE3}Zvx8td-#|IUqriZ~78{>xwi7Piz(SH26>8MIyM?1zxTTJ;@ACT+>+&C>R(7SJ2)=gr3++h@ey)0`^^HRKQHd}f&j)o%f;1f;En-xt z(*>jmPhDyC3Ch>ToZv_^D-SLpJVm*yMa%XK?b@cfCwlLcWGi`q(|*knI_sq#M_Gs- z^*8R(Um@++Hr%Tw=-_PVAw@Frd(K7ZE6@HkqYCf!SXS57NbcGqMUz`+9NeJF;OlO> zkeyRMp7MZ`FljGBBxE=<3M;N(qjAdxxjoi)LUcL#v(f`clgGQ=kSGwZ=`GFYw=-U1 z%c?vTGN3%3SN~8f)F&9}Nr$bz1)t-=T=`)ietftcg;T<1E>yKs!&5-e1S4&6Awjff zVh5h~tOpdhFm@KO06rsTZysA7V2LwU2#_y9?MvkW95s5SDv9?UQo{DOm(JH}oqWFe zwFxws1RZDjCC3o(CKgm(SviT1udUYvqgZKPvw$1sX1lv{FqrCu^aQCQINTO_r8_MH z8~*=>$f^KSvia-OY+_lAVuhhybGLhovp*f z#troPl-oNhs9}N?<82&{$1YR@>8d^K`gF!gByFcF>N-O-p~3BR1F?7M+g?Q*4U5*a z?9A@(4&e;f;O`7qI2-DOKy|+9sIFi&f71vfuLX8(qkK3kxw*6nJ$(8q>E>qJ87yu{wR1TXR z$_BlzrCp$`3W6cD`^6cu#jj{r3iUv0g^}~2HE|*a%bCOkZi;HmV^HVje(1*86uw7GZ*#tN~1`LrFZDgmCg!{n~W`ULMuO z^$uE=B7ad6PURq4jw#uw&!3xAun{Ct6v2Ne#6d0!NDKOzC!<%_r4MeuYZL`|;^q~L z=?#knrBj#mzj?+34G0pf{e;IF%^$+q*h)Ju*0P=X(dV|P0 zWxlgdJdT*loG>>`Cqe~yID1kZZ9ja?g|dL#zz*~FAPS#aH$^)XYam(@SFSn^C_$oi zzIwwb%4KuG^f{dA)nbICdHEe!Td|vX=@kgI>kDVi;7enb0@w<6u5=)wwOhNNJY*j& zg6QEXe;2I+YTI|?z>bkqLlWxNc<3Cy*X8)JVSKihoaWNXy)P3d7-Qg|EZJ5*pzO-< z?jy^ra&la}pr|#E_$MfNNUzsx|CxwHez0PsTs&LgpBUkLWpQ{7mgE-(3~b?jJ5BRA zq}V;0H?y<|Ikc;ZV>0cDtToB$Qf>Z{A}J|Q5_quz^uBJ!R1Sh33|qd{zbdYR?dzHx zEZMTO>4`*SJz{M>!Rq(c^E$wb=Z=FP923H%9-9E+`{Y5BrD&)q*m9>=%@YqkOadf| zML@;nqulHpxF;cFJ=MplT&U{0jLz{iKV!cw$8}ASmZNAKyC9LfX?T#p*JYe0wZjov zYo2h#&TA$x7{R+JC^^&4D{l~+XdPG1p(P}aL2<6JOZ@ObxDOP7Vg%Kez|0AC<_E`- z!uM#RKrDaVugIFqPduTqbiE5B;eB<7z7;t2u}C$R>A|dVwtJ^KRX|%!0u*LH*(h8{ z#^ZkdxUkmPqAvUerHuKKc>NU*Ha{=h698VVOm`v?0u0hf!BM z8>;K@@Nj6o#7lW2UB2)q8zx81%*^Tx>o6OWSIfbAaOK?;*5MWkm#FB z8V~w~X~p}6uq;T*m?qrkygu|zpo;uC2xaA{*3T~!8!Hkl%7K*gz|L96g(^BTxA&T% zqZ(CbihQDaMlDzJBhz<_x}-Ajb&njX0pt$=s;6BBl4*(B-#(`$O=xB@(DC(6b5BfX zhXzYDuN*NMJXjD+xXmJ0I@SGcJr zTut-pND28Nxh5t3U-!eXz&7G_{rv&aw-V&`mddNwbjgy;CtGm9TaRIJvyXKWV z)kWQLU$874gPGij!B~-2t*6RIIo%*ejMCV1vWC4+8hDgsY@?VLvFLhhd6|Ps;F0^g z>m9@TD~==H^?h;xx-)tFD(`h81{$>idI!Ja=^ZQ|1__f9Qj|8EzK_5p0F?e{t67oF z*kzb~jd=h$Fo7a!c+j=*KDYgJu18#0T}}Y@ZSh)>8Z@KiTvWJx!sswy;BIK1*V=Bl z^Uku+u5{gYL2Y|`@kJSh=+3gzdPUS8lu&+A@3_}9@MNtYea;aK^cvh*8=z8%6eeSG zTE4M9th2|ty<7Bxg-Ng$e)J%h+AWntR?h2+w5&!t+tbfKpUJ!>!|w0Wr@>;jjWztu z=^$+@X2||krOF#hSE-CY3&FGO*DuQ-A3*u|PU)`0SV7xce>6wGbkfZ7n7ot*zkB!b z$5dG#4U9;ng~!?mrpRcp=^1Y)g)S{T);0~e+RK390-^(RxokpabCPPUxu>1t!@K&e zaOO*C=M-rfw$Lt>kxVQ8piCVzb;q+q!NkU-5-FC;$y?{+2NYrz0E9VeC~Fkgp67vo z#lW~q!CG{v4v`#~i@Y8Ss&~l(^uyzBkefS(dFzA+bT;K-Epp4+R@>ZaGA&1Yno;_7 z<6F8kTsJxEZ9%hF%Qd)*4rmEZ!p@Bd5+uPV@mN@HPY9suVgw_YAkte*^Qs~m^74PH z-yBm!tnizN&xUt=pAq&{9q29#ENK;%l)}+@Z|9H(YI&hE>tl>Dm4CK%RhL$V-{kVH zHj{3Noo3m_GDvWRv9Vf6J#pZsDwa_2&-QmBKDYwXn7qlR|RNWrw<2o^0G+ z{2k1hy>69T`d+D6zd5=!T!^tshyehNMu06kdx$FgxLTMH5P+Vzp@(l|QUag^K*Qf= z2xu@0jkL!MuUdAn4mn%V9DbR$VNHAD(iy%ml?T&v1eP5_25Xip$SV+gbvQMp^IP8H z(3{AU8npDI4*QkwR>DuFXfq{gBdnLE(2uZvfIAsyrGBKQ{`!D`3_1A8If9tm%t*%$&Sj&1zEwfgLv(usn~yK} zv{nY@sm+zFecaFFc&OiHZ)bj__W69X6qASO?n=hTxw4`Is3-K)?LI-1+N@{;t10Rr z(1CI~&MSm7WE8-;2{_{P%E-c6JLuje4^g|K_{B9nBE9SRv08zaJ+nqa6QD25MiZwtSULczcohSGP#+Q1o{TY8WOF-8_2 zNaeAx{I#Xuq4fiHo>15bXiB3H&Ls+{pPJ&iTnl4eg~==rNv}*MhDQ~AJbBZtIW&6z^v2AT$Ev>_9SLn>`kE%L*CySl7_=@e9;vF4V zCU4s9Y+A3+cfFe*s`AYap6nQKJnXza2(q^?be@39{)-8NKoJqA%^dCzg`lcmC8Hs% z!}`4H&!T?zwW%S!5Vdruj2E>Ytt3GER_*W2aUG+#QnJ(V0HwmKDQfW??YC0@DDe3) zcq*f=5Zh=WvC)wRt9*g@XD9~7Pht=}W3Z86E)=|?lYa8pi4~Z)(Apuios|bRJP~}7 ziZ3cW0<3KUpn!H&#As0YD;vtj5WY1gk(nk@%yZOwTMIR(WOKTCiO$)ja)~Q0@zV;L zqMb*QP+er%?G>^2J!-+e1(FAZ8+Y;C&C(2RKUr^h<5@Nbm!h6w-OOs4uTQx3yb%!n&^B+HboNK0}>K<@2-Z04ZY>z~}(L5&jJq%nv$t zZ}B(jdir1C%vnLPi=8=-V%5(Dyj!Fi^|i3s%L1jj{C+RcYy6fA=y3Z)I_>c-^yAka z6%MY&MbH?F-xfhH69&8pw|#r-L-WE+wnLz>%y}b3wLx zP^xw10_bpWIvkgJ#QQD3t`?c4&C$}$6bIK&fY!a#Wq@zOg(N7Y1K0>EljZ?a1TD=f zBwpmd<2(Uh;tg6GR@Vg-JtR`w5>skA2Zm!Ib+H-VY{ytP`gbyjoXxxBv|5$GG}j~6 z7kWZ;AjeZmeJJ~;?v054fqIssHC8hdSzAYMZ<$fAws(|~nh%s7H197y&7@P}K&xHm z%qZfx%3W-?5R-cRZxHo=CC_=J;eY!Gx7U?<&m;cbvV>^D%?v95+qf{3wMpp)SQh?S zo>Cn*u$cyH3iW^Yc)6CJ1|^88p#_$u-cS;3MR0d!I-Ig>*}z4DQ3 zXPw@C2N!>TBDBrx_A^XT8J`APhhwM9vv#|z_oqP7cRq(xqFKR$RVy5BNavU4;{&i4 zoN2?sj`db$u*c;s7?gMZ5BX{)iW|=CEh+?RT8P~&&w%!y@ztmsv*m)_GS246~Dxy5~Gi+cGySU%n5Cq)g#aU)%0%9@cj;tW$?- zOqMhQ@-{*LHohn+5P$W<3_8xd&Yip7wlISZPDz1Htmi_R2`dIM6+DUM??cOgx5kh^ zQ?tLZ23cHFlInXUeh`}grd}p5{K}_e26QgF_kfO0dHxydpyZrmfK9#6k~>}?CqYoH)#P!4B4=IlcOOC%uk^U4u}yOcnAnAzZ0 zf@}rwzV)XLyYc1x8u?*UTg#5RVV1*MrKl*0LBWloiBN<47De!ClYQ^Ld0|!DdMN3t2j^r#7I^%qg2c=g%E5eJiI%!2+o+~ z6I8)@t>NP?d2rjdHCaQvR88M9zymja*;!yn-vjcLjzU3Roi-kP@941f_TC)QaoOuu9Q)t(Qm`? z=SiLtI{sbB9a3Fe`dLjxK-%tMrA=NHr4!rgG&gwN=ujR#^q0S>EF}@hRg7#t7Yiubl3O+)8uOmEt>wO<+_iv147jMaJpzZ0; zve6ArZgO4&e-`=aU=tT9BA<}ts}Ng>NDmhVzuU<6DCA2PPp!CnFjL0i4rr~Z;rgwm0e6iOBS7N4 zJ-R8EUAk93hFuyOz#w+(g~1UInyHCl<(D~g)Rri$uQ0j!rtRkI;1IZxE%K_3yt#v2 zoP+blsEq%)y#YgP4>sL<#0!;DUb82f;z=tX^95-DFkCU*yiVhZX&9d}CvTXF8km@8 zT|YOs&97F-{_R$_4J!^JTO*vt0ZT)U{ zEQ^YE+nKT=Pt2EjFAsq=_(N>(y{WwLFMD~PKtGcFPo#eUf;;L zya*|z_qHY)i%D|k2D6eiWk7obr>siGVVAl)Yry#~tR5W-bwKy3Teijw8Mt`JEJZRF z<+YZINkH}8-!a^Q3IRqdWqk>7Ab=Lc%BvCzg&wu`kGlDORyJG$$t??bU0$r$4flDr z_4yJX356Wm9mL;&i~OyVC9Y*jzqLhAtBV?Ao4TABZuStK%83Fg9@(Z?)w?+v5KAnQL%0NTwQZ!z^{uFXb@xFOKajfvj?8;* z#|coR@5O4?YV(LI84y7pdOf2R%!-y_%| zU|Q*!`%3oo8a7=8LJU6SX+3kFJO190a8oW>fK6mpzDa+En$m#a^>~YB8CLf#-xKOt z$^&!lr)IJaXI=s6syom;w7UM2)DM&fEz4pIiE_2g(3>e4F4X;{PAB?{?Uh7I9acDt zop%Hs*QeF(7KS9$OP8n2OUXpH=Rrxk{@nix6Y+Gv=l}M>2$HKv16w?4Qv)ys2*?@0 zsJv<&C!E$n?ZCnH2$}RYnC`{q3UvehhwhYZ#B(1>yx1X-&t9_&023DvUlCoNQc`Ck z@IprzQagNy`Qdl^to$tcL4WMK%;dO4^rs#c1ESnmG5wUGQ=U;8x0ArHr<(--8yS{& zg4_U3Z77$3ECz)rP#wxEuM;je!BZD0mxl7TgaIIzP+GgcKZXO)Tb0xmbK*F{t|*6^ zceBK4c*rCZp1S1X;vc}VW~LDEt_e(O(K2o*VLOcjXYG*)mI>-?0!`I?69p*s4gk@G zGvSTgp}yHVHJ0kjEb75X89no$o*5Uq5Wy+4i(pt#qs>y-jEn!syFRn;T*aSu+HU!W zJ}IPP*^0xlU@Rj=X}8EE0f4U!hud=T0Pd%tO48y2TV#hem?B^GdZxaE3rY1Zxp&d$ z_T~9R(<|B9rJ#e00BS{f28c<_fN~iYbG4>UU)U>Ov(IYs_QOwfAYQV3%GHA>yyW2n zC;}Q`1bm?2CxtRXR<1@Bn!`E?iWblio)P#Xe1P*d1ull0CQy8MAoi-iKQ6S4bFhjV zW9L-n1C0+el~J~6htympewU8RLDK};SXH{-t8|YQDLe~-wd9t!y+sdJGxqq5xVTc9vli zYHk45Dz*X^996)I&5+%*5~(K)Wd}iPcAa-2W{Z|K$;Wqd!$N3{iT^f>NeuK<3VTBI zKWEKAaUL|O2Q+tr^oH$nq%(=McGSohsPB0Mk@fF2z})sDuz=QT3fzZ;HjyRgM2dr< zf!_58m-5S`q=l{fXUHtejB3)Az??nlLMdvMpP%uwy|K*WB~=vulrV_pF+R-qGF|N3}-cWcg* zT_MulUu2VGdm+Px7V!8ywGv3Gftd%~&U!Fu$n0CuT#2=9Mtb)K=1xpNg&BPTVrbUbRoA8oBCDfKBl1^J|4 z7TD!6#ltx0%QmWs`M9P2}e3(E%&AX5tKDg6~MfF zrjSwSjC;k~R`KcSl{ag!I8yeraC-=jiE+pZ45j(pTAlYwk;wR8Bx+cya;Rap)LY8A%-IO_TQ6Y!Ks#`cMWYENhwfj0b$O1sQ*eJ7>V zIrknH_7rY|Nvu~%YLX)8hckePM>oI6NZoUSqOmr4hb(sa?LY_A(Dd%G}}}l4iFcx4d@j zofDqGSO1XRYl0fk3}y~Sj#t1;vZ%m%j4iI0qz5IUpaCTgjOM@SP4&M3tL-I50!G5> z>NgORm{SFq(Q;bkm-QSTac&IYOQ?cUznX0gmB(M%!oydIPovGjoV%xSU|l?^oSKvI zJunDHnN~mhHGf^>_6{gECh$?n-*fSVsN%ycXKhC?F;k9#R8`uDc4KJE;u`FP@UZmX zyv4D_!NduDD*x`yC)S3NfS%9Z8FTVda0e6-uXm2XlKQ`{jbS1nR;$%LBaDC!K!Nnb z`8f)IY0D39un8)%z#&P0(xXMctcQH~4MWu-JBw=Xt zS{^si;CYO^4vxc^3@hLo?9^=1(Wx~TjL5@Cfw>M!y1=AhijYVM)wFM1ZV0d@sVU^s zPcf2GpE@4|n7X!aZmrBG%A1$(<7kF{#PapaTs%V?Zkqs8ze@x{QhTY5d0NCPu8Wj~ zsvRmB{)H>cuveadPI2_FECt%98(-@ZG@lC)xb;YCcFFzzOw7>5R5l+ypj3T+vwNlr zaHDNhZ^v0aKG+GPJwBrrmH|ysY;nv{!;>f~pTPCrfL3iGrevfjT!$(kSf`a7(D|_= zj0?c*I95f(B0dU&o6JsKC!u34IxFFo##0<;c66}9EQ zirB)EdkX%x397YUv|x14({J*y1N$eOs6f0QPtEd;B<@_ns1#sAwcKhZ1y>7sV}Y07 z4`j|dTgAcEBz=h~Sg4ZD+e6U0H7Vk96=bDrPWx$WUHhJh{9dT~ruD|bB6E!{^TVIh zPaIsQncBlOT+`^#dE=PE+q%|7Sw$MwX$ec9T}Olqh+pMSA{gq5k>Vtyfdqrf0v~^a z2O3ZhkV7)zNcx(TR-^w8Qa}+YsJL{PmLnWYDMbqPgM@sy{B}|UlOBaRfRiN`8onSe zZn``TIh6uuv^}F0CTCtQsTRWm`2vd}2Yiet%y(6(74iY=qtJ{FO#UjpMhr^N>MOfm zQ)EE%?Gd0{5d~S7X(PBq~7Yq0>^J1EyEy}HE+NW%hjl@46eZ8b4pkJZE|#Yl2$w2w$)V)f$*6i705S05^l2h~A4TS#o_CD_FLg5@OS#o%h~?Kh;$8 z+vnS@3>0ohWC@8M5wQlNq)Ex*wp+!?nVHGRBAPTvOCF1pF|M#0PqN)k`&Rkkaz$&eggDne6|hU3M0X`T^w zEC+c)1_Mi8?+eXd>^w8Bv2NK32hWaU=w>#=^IUL0xHMv`Yuu8X#n4g#Mo@_+h$|98 z_2#92%(Yrn6%azb_WNp6qCk0Y{8~WV=-V?yaZ67FY+fPk4crl~2-hPv_p|B#wOZUe zaDvNOz19e&wdoOT6@_V^SudnkWSyo$I(5bNNvr8KCtm$X?R}Mn=9%QF z@Ru%Qyzkla3L%>3M{?$?3~VyU^U=MTp@ z-H}$!IiJxu$Vx zmr+Dx7}#IT9}ak&5XE-p%$b(nUX!tB&z`lkwXyDOtvrA7WS>%K*Tb=z7ztm4#gSTG zyZPHmX=x$J$(%_kDbH{7a9p}{iH6+c(^PwV`KU9q>K zFzHX%D*yD+P7Ei3EsTgS*ELj#re1CY^1_dR&DXu1`SifqvE2g*dH9jMDJRP}0)~z` zmu0y3%WPc+qU6OM1!o|}t2!`3LwW3DE4B7X&r(p;pB@Sx45T14zi-gh^TK|KE72~A zBA}NZZtOZi5wO~$VKnP~l#%!laloTlVH@bOc03Y_@}Ne+7PMhWR{OmMXEAm%u7^zsv&3n!Nk`K&N4S?mccKri$u%teO6)0dV&84( zYW3i5j;%?s_=&_E-gQx8mABNj?}`Obhfs?qye5=C$?;Dg-F#TFBCR+<8Q}Md>GWf< zWDgB`<_ULScR4n%(UHtA<0ZZJlF}0-hoq={N3|A=X8DLJ8+0GR9>Ej?qg0L@|HT&C z(_mlM;spUsKu$owQBq!^?T1G0YA?D@v#>UfU8fFD;6)Jz1-KIn(hr^9aF~4>aIw5? z*QTEK3Tva-Nnj~Px(qrQe(`i#aJ?~PdBC|l^BYL*xld69ye-Rb#5$6tt5Fu1t=&>Ts?CyD^e z{OZ0minmOJDIdLufC4AYvt9euS$y@u*{=d2RBCiy>b{oqOBHOb6^rWm4a?tzD_GW= zbmYY!lf>B(R5m{jN;VD)_?)dkdBn)mdz9g{i67X~p~+UaXZXYi2-WpL+(`$~BML4E zw@}k2Q9nz)({{d<#hw9F?)taMIh{F)Dov}UEQ>CdEAvTTF`8u)Vg|2j^*sLCrC&TX z09-^5*<7&Z?b1^WJG7MaOs1594F@URWDY(KIz9fyz*aJf;dsLE1x)<@e8J(_50V1iz5!uLQX5x3qP& z`m`-ge?)QSwCVMlhV2?5qDspLR(cPl5AGNq+z4sxbpcyhp%*w0iDlg98$mNXwGjs) z;T-y4v>!}i#;=QAhq1ybbH`P{!+yMhH0!uL=32mr1$r^qXrHttN8-h%wtySpGJ448 zD46ZFG2)fOH{*w}&pB-ibULl4S#t<2X=Zv8tDTfruA4k{Pp(9UMsI-J!^4Z*2#hRkU2>JzfogQ$Xvte6~mb? z<7>c1nmq$yBUmE8`H(UdVJS_G%7zn?e!u6xU;7OQ$n|a3EEUK&7;_@pJJ-Mzau4v%X>3hf!UGisN zYc;$8%0^IG5RwT{F&@JSBj$|4iD(i~_B>fB7Z3evDH{XH+`;JQ(g#^sPvtEkiC9_0 zDy|w^eIl-HfXN*F)ZLiXS-Y*?e+kFuo)96YBUNhzdkH;@w)AbMBwvGmZgJ+HFc6Fgg&{H3#r+mgobak)` zQDx{VaLE~1&Je$$#^Tsm)}AXvW?fGEyv}C_5YYI_urh?&5fW5R9Mwhs;^hl*rsxoW zaq!Eh0b(mLdC!>eG+?%=x;R~2WuMm*bl%%UHE|JproR*@&VLjVltU{R>C^!)4^cq+eFJJ1uF`a6u zL3;R?CT*J~O?swVWmb1RF4?@K)uSerzO7h+crTbEXLbSEQn_8bD*eR7OF-ya>U^-+ zcxWpNi+h!TjG#JSX~5GN39K?mI11j7{>2X;!TF7=z~!FqbBS_b7Fvp}sqbL1#Skct z7-Ie>%LZ;iwh?BrtR!&9!9ePc!3jo7BS$q#x_arrlHM^`?E@b*%0RI(M#dA6y(G`M zEUc+q-VmtEaFYHNg5-{C+yx1f)y5F&M?`T!!~xSiStmtQIaq&j`gM7m|xfQs1Y@A0%Dn2fMtbz0j1Xn*q*(?9T`0dAul5rG4 z%ttqy8g_PGluuc~Ym$JW#^yACS73u%(oP73JpP5yNO~5xpO1>%PZ%Goosf(|B_}6; zI{ZzrqvQF~8{)^cF8jrqlgO225(Qaret6?9)(!w_kV`xG^CT`lSMt$^0ObzlKjf^R zbtS~dSEuqn)Jjjgv=wHW^5{+hXCfwzuO0%kj$;fT0_>ejPaWkqY_l};2yU`7TtF#4A*nZN)M5a+@)^QsKJ9Wo-Y}AcP<(m8wZY!-ML_H zViFu5&o(iT5q0t6#g`|HS6^SwzT15Am9Tv_1zfoEqGQR%Gl^4?h3e1wO|K4ReQD;`{>}x+ zch}H31^1T`U`}o3&lUA(z_+t27fQ6dlr}q0* zRAh;8CHihTFHr`Zq{sUAY_cq&Ur044PO81=C=YSzSKLGG8af_{9Mml^ogVX}XNsX@ zsXwk~FW-A6?=q^zT~Yh_^yt&!TjNW5v(r4a4bwHYmu;U&g%4LQ+8UUcXWkJ|6tsB5 z@RaE4$u!;JUhgXX>N7O{DJM?dVK^On;Z)#;`DobjdU>6W(+^FGKDnq?3W<9B%8*gv z;=7<%1T92i*I%eo@0Sj&v2gtgJN;j8vqo4`w>B2K^8TF2FbfE(d%j-Rto9sr%?cN~ z1N6_}nD137jAXW}DLo`$I6v2-}((K8#k1`sHInUg$1`3nyv z0zqmO7&B2NraDaUB5tQY5d04e?ICksAfoQd2NutPTaKCzk;Xlvk*B2KpJKd#dP)#` z=5bub>SbOrPBx;-e%+y9G6+V^TO2bpW8?s<4S>udb(H)OnqMqT0*CdPiQ;M`Co+Y= z!ZskN{6E+<>9t0f4E-@yLmp{^bLbF)mICopnNk&R+y$jy^9px=u~kHnv8s++Dd(sw z6W-a94^8V>dR_E+KC|I|p40zF*;|H1*{$!xgn%e52uO#3v~+`lgrp!KEz;fH1|i*D zf*_4_qYel&14wsB3?0%r@ZJOZ>}T)&@ZZ1pgU2x+M$hg_@5JX#?`L<=34lC`?JT2)>BzKe?s0T%klygcd0*-;+N4GrUG zD^?BR=Q_20`wiCc6)PbvJE2wMkdAp(BD@s}tf#D$iRP@#+b?2x?PA?@SYPGXDDQ-FIXz<4!zsYD)nw zY)yLnNXDfC*!urryObckMs2%DMF$2QHYFgTpDC(ymtB`hEl=SQQb#iXHwF#8DmsRJ=pnOk?Z@uCIHcC3m*VM9y|gbWfS@j!q~0*QB(Ws%MPQs zD0s^e2sT9|VefzBmk4a|n1?ucKmoIUynZcHzFMof@5lhJqgzQU&#TJ$_7qZHAzYEj z#wa5+0TRXttO?akMpW4E{(XzTmdl$bu%ZSXxVR-Dms=~wCV8dpu~8ZwY1tZ{{ECSm z_hVogeY!CInmN9W{q5jp#U@h`m`mPQs3A+7o^M@`gHg#Y9(JzUOtqE zpFh>Z$S8-0msjq>7oMA){dFN9G4Q!VGNO)a7T15~uQ{H2^~By^;Gt{O7kl7$I9NU| zzUFTv+^(ki15OmtR?kD+J{`4=uE;pU?B%e?nlNCkBfLk1O)n``G0pHfT309l_zj|- z3$7afJ@AQ7p9!D0CvA3=Wn4Ao4XCOzF=+1*+@jM&^NV zpuqw39NJX8#_RM|+N(uzeu6H$O-eNuO`1+Z^1kbryA3Z_B@PZB>f?GdRLAYQ6Oc)HX~4 zQfh)w>H7pg;<*=X&coa%D}}%`-wZ<-@g&kx@Yb#&*1Z&0gQn+}SY*J)EC9D+MPZ>z z=0}8o10qp~hc#a&Xv3%L=1K)t6rYS-4XEwG9eUwiI#7pU8k->3ql%H!DE!9&lah5i z`t7Dh(;qbw}FFyfI07p+Q zy~9?}#z)TJWB(>~>&q1=I{SJcSS7v$K)_&U8d=tf81SC)GF;|t&}F3;-gc1E*izin2$T)l zkWa#&(?So9e-h&-Ei0WD1T%g(MhQ#A?@tC@61{dm$7la~kc#V#icD4JMif=*zsR48 zNWgm2;#}nNDgK{iKW&VLs8kNXbQ6xf9sOr+gM>o#=qeONxFjmLAEj|UZi3vf;FdUA z;RHmZq_D`aU-*b(DzXGbWU(UiuE@CWxS*b8s67{(tX|%OzIvy$STajr04)3n6B+xr*rkg2>yYEn6;zHHUW`qfc!pvwlr?rS;Ci&~`WY;jPq@gYJ9GB*(P_-`$MzkmE<_GfE*+m?@m#4Yx+x*k=X zi5qEVetkwOze282zg|-dM*j-Ta{rKG%I>4zt(U*A53p)kgjch?14VLhG#(Y^d=C^E zL7w7yXM1PD*4Pv9Uj4#K5`phb406 zpRfeCeqBs;e|C(6SIf(8&-Ad7rnQ&`sM~-f@0VJEan0H@M_x)5vZq*4-?&BI18UMd zR^%KO7VQca-YFI?i*CEEz8EQ`5>DJ>F0mN@4N1+*ls7FybU*N#lz09ob#nRv z4Ptmoyv~i&rnxL!65p<`&+l6m1yQb!^HCz)ZDrg;%0tQARj#8r^WgA5dCX&1>E|LegRpjg_K6$F@8S?e6Y2ogN)!QdCszZ#59N zFgM5ZQFHe>u-2-x&sIq0dJ>35O8ftdomKreJL|&I`6#LJBnLmFV~)VqCf;3y!H{B@ znSf7HySk<(#imOnr73!U-~KI4IrGoTAlk?OoB+PK{YfN3d|I_3cZW0hz;}oJHC4|M zo5HowB7l^t{@aTBr*Fsr%sEuP3p5P>ZJPf)Hh}AD-ulG=)*gmSMRC{H%zc56Q(%S= zSjodntSS{6`?+PRV&AjF<5*aMl~J}s5V0CPwWPomSpNuEvv&SO-}XfY6Ofu`uqK5R ziXDw@5QVyJivjZgZpaWx#Nb5LgKuJo3#&*zq7@#X6Rre7rp=*0d)N9+?U{JyLDKRe_cs-JH*zDOXB7ux0X!cZBW+$m~xuSjofbnP^wq`0Hyl5 z%#^!gt^ ze@Xy-0?l<$Byi6S2guC_Jg9AAYHa{s;Rh<`NG5I1HBjodjInX<&|5#5Zihy-SRC!H;o~tzT@&47X zy+c9$jzNFa#9N?Q-xDKv;+`<@Uc|6StgnRwcwUIg{SiJoyZ)cQvzHB%@S@AKw(app zYNUYvE49cW6?ty?3sW}e+ZI;jE1f0>fBJL^(!0csCYjtKPnS49x2P|OGa{y7G&Kr- z9qRZ!b)Fwk+qPUYHfougQFDu|)`uNl#Y4Y(ATkdSbY1@|=*ER5Sl>K8m@HC=tWm&W zDFX3T(&6FB85_IjCJ0l|$_1$~*Q2&ik4DWR$pyo{bY?Vm-`wUvZ38D8PMsf{z9!sa zypI?uxtKQ->YpU{AG2`Aa5;s5<3`Xw;hzfW$R)0MI`vji{F)!ta3z?^_9Jugla?6Xx1d**e!Euz zY)ew~APEUNG1X|w6+hXQWNO$vr$Z;w10Mk2dwJRfJP?})t%Se90$@cx-#5S^_>cL- z8RKe;60IOPEIYcb@t1D~J+2g8N6R@tiMaq+mzC~IMR?DVJ>&{GQHV8tx^Di{t~Lv> z5gxDvHlyZXn7Ip7%jNHHa7##)@F5rvpy3JkObN-N5O8+XPPCA7i^VL`S*G76*9`Pm#w8vI2J8)Sl5{IKESxNJ6XiF1%g41_0?~q`TyRtD z0jx0Zi~!O42ZD6D&Nz1%Z->J@5h+Kc4p$IxYyM+tf?PdW78u{r)TeZEhI7CGP?|lk zoqkim-KrA_Q=au+ysQ9iw7C=@cuLXXhqS*Uzzn*dApxj2um6{N1CY63pTJ;1{n~L! zBD8&Jo`^h`i3)0HZm#)qWEbt=X+(=y?1J!e%4;(J6v7(>Affw05|by`#RvNKH9?Ab zGM+ce*Z28IH`@WmG+g&3MM#kIefKZ>YCmRAQQpB6-VZ7GJ^xp=r=OJC*j<3P`Spky zprwvcQ7k2J(^DhM$)?6cZ&mo=#C~Jre6?z%cr5%J@yG{9n?^F%?4aj(m#8Z0sWUK} zx>F}?#f}2U?C;(DOB(%K5qo$ecQ zHU>?AO9Z>*w9nQn_1@jJUioN$ z522<*P~;9}%{XfJpX-PYvoC3IJ8>}yl$3p1w7fOZ130Ms>2npqoo@Bs++t@9Uyu<% zM;1195Jz&&qmL1)x;zs2Li_nDr7irf2K?5ZUamS0Fvc1B+a;gnQGA6jCvp4q?(ESu z$%pB8SpZUZv2|nwK#{11T`3k5oZ7&Kb5$)bF`fcF`}QZ+#)1-OPWCTq^JCwAx9}01 zT-<<4AT+|`YKU=2MRmI)8zbzWU2&hVuJSMp(8#6;rcQ`&xmvzRrUXj8v7KEk_S`y> zgN|fq*wg{vjWbvCI>=3M^!B4m_a+l5b^JD|)PrlH@+a99%Yh;TFNT?2=%l7=`&@<%08{ZrEpj1D+laQy3sMXWN(~OxJy8Pu6j^V_s2imzhsyi8#j4Kg z7c#Fmj`s1t8!`Kl@a{G-B<_Klac2~bs*Vney1KfmrY7UpmnxQ)mh^bDqsxI3 zV_{>%JCWqU<3YE!HVb$jj{OMBW!z|cMN2BDDmA7Grk?$`twll*fpg^nokl{S<7|=s_(UyzWfk>sQvJ$Ix5FmobO%<9(~q4EvSy$R2T5=y(oU0u=1DPg+YRAc|R zp6p9`L08P5={f=E457IsBF!zSvuu3!aZ7rkgr8auphqR|P1AWQ_ORZ>jb|0~;2 z69-7q(hO%-r|)AWh%LsZp?vQy78fX?N{?6IEj5@^+pj6993HBxYm`Zcx9BI^$X?jY zB*Z9GKhpAsv!@z#aOC!=Yr8T0*$B6;pnr@Vdhiba%)Y8p^q%9G88G%QQI7sq@N!Ek z<`_FBxr62bI7a-X*XI`_y6~SG)JunHe2WrrLUU#LiLXrLm+cjn#HXZKVrr<%Wigu< zuYY#e1)3~xL53D%6;Jr=rdd<@_)|tufUWQ#y*}BcbVvx7gxcq1P;P5nUzMUT;br+# zz|%q>UFZbVQ@~u_&8B1QG;eBQZ5?CIYFe{`gA%2QEv|;tdoa-I=eMGx$%qg?^rpV6 zcc4ae%m*3*y&b@vVqtBWpqR>^>PHq*^7w6@vkt+-!>%tUo&{@DI-WzrvYnRl$Tsp| zp$?;m7U5F#)~l2BWc+q~hidnS#)=NUIsA@s{oiDU4z;ah=8GjRnwi%^>Pj>nh>k+f z@W~6~?x|Jb^l-epNrRkB&wlk9lA_54TIX`{aotGAdIaIioi2DCvE~Cbn8}e#D)~dM z@3`+u&h3v?n#_KC$7KQ`B;_&=K7RB;eEQ0^^8Wl6_PfU`RLmzvPXM2JjJWP=(`I%Z zzmt^xk9tcdk`k>yHPNIzG2Se8IP%=(H8|SG$UT>hxb&HKCtkSg`pE(a1yi0U<_tgg zuT&vB$|~bLVcteAZ!luBFJmDRfHc=0gwgTG{+V@pr#xeDRcAy7xJc|NI`*f;Sx^w9 zt}n;rsC<^o{AKgrh8iR95O^!_M3m=MOMbzxKKeC2Zw4ygv(K!0HM+wk1{`bR6Vydw zdNwTs@kLB81@WJ+lRUIe8a$+;1|*Q!ZKICPU)ho-Dn8A71x2xza4~YMr@90L*fPfQ zb?SUJmT@kbC5iD=KCDp9;<)OyVet5VCh%{q%Wc@P#7VQt7*^PdfsR8${igOF{r>(w zm*tT?=|nHJWDrjD$vHgy%X2j~s~WZ1@b^@Zl7f0Iz@S85BnE$;P@+pyq~XV$X*0@X zHCz(=gU_0?^#LThWLV4Zwhga+78a?D*3Sw$b@}b@pmeoy-F9W(#xHS{lbr_%;zUGwQ9^}jU zkr#b=*pU6GCR5G|b8aXZMDp2aB;74W&u#o!4F&gi0h;Gh5VQV>3ztkE1}at^cDv@o zjHcrQ;fkI4NEj$mj1N~V9-50A6n^jF<9pKKadM3t3B|k1eX0iH)ONFv*Q#BoJe_9D z(`S6`T3B^HtBUDWN^7h2L^&4l!+wWd936l`T5m)-HjSqz7Fbg-@I}PUaZB7P`P}~F ztC8AjSfmmKD`~_X1R7>u+6amHD-#Xf*u!|$>g!|tt!4t!63Ym!f*-^>7=4#jv{4M2 zjP!<>VrvpcNG)s}PRHhFcs(*j+z4^}Qp?jl(>r1bR-?`5Qt@f|^On9B`0GF(xW-62 zHDYzWIM%zzYDg;x$FA{Qn0mI+-DF87C7VjPj) zn?cN31ryCAv(UoI20e{3ExNMS?YyR6{VY41uaCcz-U*3|JW}g@V#0V88~%}3^{s+i z!a7LBQ5ZdrH!f!CpdAa%1RHs1Wzy1FpN!05$&UmmBzv5zAZ6TP$=b3Agz_Y?4NrKRJ(UOWqvrn;J!|Q^Mhi`DfWOmJLnWcI< z&oLab(FT=HY=EWp$zQ+ULE(|>P`}(OKi;=Klq=p)J(VN*M02SJ?cxUZMs`t6|3D)?dS*Ei_NO>yo`5<{RomAez&;o-pKazNpcT~TPE zGsF}Og)EG0G8eu+CW#cTQJ~f%G{Ij^Y9s-ibyR9DIKyY{IH*|J>cm-+2fsSPATb+x?U$p>H7GNKMgLkR(rbE%iLJ!jC z&Qbi&G%9y-!r386E7o6FFR9j%Q{cd#P8R?-c)GloC~6XOaK#_vh`lVtgb(+4lnL8$ zKl^l)m0sf}{wqpk=jcv_@iAA3j8Tg1)crQg!XlHzbl12ts$=!z9hyx3*~M|gjS*v) zVZSEJc#*Tw`JGWU<<#u7L5f~IhLW5Hk>`6!zJt#yrrvJnS^C}<_0;d~dvQD~<9OZT{+}j~^=#PMBtBs(KsJWxJe;%HHZ2W>~5k2*} zMsOvS%cGzE&h(~Rkc(-MhniZY$noT^H_(+7xyiOqUVu@^1hY6q;m`n;a(Dny8#sT_~v%g6$| zys@fB`lz6g%l3l|C_=!00OJLPl{|5Er z5f)y)l^T6Og?-YEwy**#?CbpT-fPfi3X|?h^DLitjyplPSRrLZ!17s;gn7Z+L?ovbyn9-TB6Lc_>7wlG70Xt*^wYi zNq(7nZ$4)8Aob0_Nl(p)&-rO9BP=D=Hu@tp@uRP&r_3mUI77sQhLXSm3HP9;P08@s z6rKTC=)Sd+%8rG+Hj7L4GUv-kvE#T&*Td!t=P^g_1(G|LlLAD+$v*3`Ny(H5o-Bvv z$3T{$Er%0|ZpRIDlt-B8v1Mrg;UHdobaPizk1hLUr|}}%8=8XBf`pXSdzF#CjQ#tj zrO@hXQ-ewy^$y-WM>F^^qH`xB_Sk6BHN!J1o1mpJ^qnEkmA8#zv@ zhm}>4hhpNsGSx|+iI3hziBr01R}u{wziphFlGvEH=J0jyVZa3-dY9(aT)P>u4}#3vi#1ff2P3_~)U)MHzjlfoS9nJ{V@#mkkrPi- zs|NgzdTDl=)s&-iA_cn6>f9QplU)XF;w7D+cQIvR#JgPjtv-U{Rw6gXQ&Nl>-yMsS zDrH(#9EtCY-f<{E8(Ny?Iof7=c3w>U4O-j~*?4;TD7+91saFJe z8z|TqQrh>abqZ1X3GhO+BgYd9QKr_LjASAc4AS6|V`e-0A0#pASDp{!OyG*2ytvDz z#Z1L&VEPT}BEabLRG~F(P3;%_ZW^~F@GUlf@K9lpB`JNIATAVod z%?_s3Y0tHN4^K6Qbx?tK;;A6-amC~IGi^A(#k{8xT8WKrwzM>P>C=o^XgBI<#oadi zWZSTTL&xV%b932428)V;(Hl;3e4^QEIo=pM(O$Tlr zIjJ?Am!^AbjTzLfqQQKxaI|tL>rqAbaE?N=j9qi}-qwgR%01sr>a*pC!=*YLwUaK1 zlb$1@77xLdWFe@(D<5+dASlnE$c_lpGnbPtsxNQ>oqx#sKAP*Px3YfMIA8<=T1&r% zK^a#BHQ?;#vJvSc{FQWB*;e<*==21tRH5<)6oIk?s;PJd!}&YmNUtbE6QroAQ|{qK z`&Shr5!~!d@b3(ikl?#fl>1QSerjRN8q1c}@!8RFef4`}@rzk4$MtA~hkS>+>EM%` zXKecjHn3#i`^2<6nI&|_fR?IE-z3nXe-^1Y)G#6zZ)K=aqlQ#awI-GGQQoHL6P!^$ zaZ)b#awiO1PA037lGq~K2CYpm_dpM!>D!ubT~%=J@u>6&!FdE1gFzJSm?f2)Ij=hg zIGU)-e)ve|{Ix_9CTe17m+!tM1X9 zaSlB@?P(0pQhgBXI_(GlL`gf5%STT>eUrX!D)oMDWV%0ocuQuBvT5CA<{ zpxy**DoL~|tpr!Y325IKIgs9ql=IQeWzUGZCr9!bgoPT5(l_>!Z~Ut@Cb^KN-yMbP%DH;cs>NPo7qlGjd?y3-zZOS_#w6vWN@x4jYSr-o9(zEF?KtSe8EIYDTLNn5!ggVTI!hmT$jnRw6VTb`36i#_q7rA`jl)Ig*12Nv#0IoJt0xOi z4Kdb5Ab&V)a;9^n#qljZ!#acf+a%#Fw)*`|FJpiWRaopUrhN#S`pjx5!EKtiSy`L5 z+q`wpOqSh2uIx~T%8!;9zQ%k!Q~4vAPdE6s)FBy{JB#2(DkEeh@~qI|Y{AiQ`Cylr zJx0E)USqM#3tDB=jJqRRtG;xCjJog>6(=gT)geuRq_)HyN11(bm8*Vl)e6?fn8_1C z@LRBxP)4{N07=0;)N###N#J&yqlSPWXWB5pW`OgFxH5qbamF@wyi|Ul8*9>B9GuTj3jRsCyLn{Eh zJUjL#6|*+)u_}78&t1o0{gY{}SqaYgL!Ahv;80mu-F3<#sA?j=5@K~+1wT8tBZj2$&%`mi;1WID zOURH+F1q>Q(eAw#F~>H}koLKnbscRT*mizKfTUuWyZ6Wzv@rP$hrxK z+O~*J`odoqr!nX=0KFHl00b_5-m{@3cZEv{ZoW{4eOfb`Fn?YX(*9BT>CtlG&f2dR zR&UfRm62p&`cRs$s;TfZHjTL)X8WCA!+w@G|`&ga(`LpcD z5_va2bO;<28ALnz9o)HNY{PG{)Vlae*J-yE)*?e0oxoOt=0a_iznv9;3QOBcr!f%S zlULO-%5?kClcWrYVXY_g$9XwoiNfD)CWa&IH>%FZHE|NvA}#{KkNUXA6osS)y4@xp zXinKzetMIu%ZN>QG4(2Pl_XMaXhlvJh zyOM%?w}Td(vp;3=9hTHClqKqFL?z{e?63>V_(y(`b&zk6c(>S2c^=QtaKz;sq*;vH zd_IgA!l@Dsrx4Xvx)=`G`S^nhz|US2LXYAiZz=m7O}wjk**8~lcMwvYuEu_qi72m< z7^K$cXp{4E8Xe$I#EE+iB(70nxhz(0iJi_HS8j>F4i0}|L$>NHqS>wsi)=G~D;A$_ z^x|%9N4J~sS|6c{KPx7h$rC0fwzs)%Up}Sh=SW9Ht+BUs8Z(Z%KvoBig{1nXaK-2^ z>Q5=PDqYjfDC))^Of+h8)UVqS11wkx;xVbh#B?aO7p&_$h0WkCGNWWHnmBAyo9-H8 zaJFnc=8{k0^d48IrV1j8E6VEEywLwT(5%9$7UDB^mfq()SPmU6**cqA_$>J9hp*UfF0an>>QgBmCW0ywNp9_GF@C& z(OEi&l|8A?6Jkkq`0!j4`gK5z(Jb9J#ah@@p@$E?T{z?jDyaqgZPv?JTCKlht|cG+ zef&-(*|xBEU}g)`DWN7<%pEKb3$;314N$xpks1dd+bi4c|UIiGgR zZSnJ)SY002aa!z-l`n14m`*xOSXuARuN)1Db)BHAfenL*&QKHx8FlvZvO^V`~g-E>=tH7xPk0LUBN_9r#y*jjA5AH1C+O z8P{TP$Y&dwOmAA?Jx=vpezT92(WutEAqLzMJ!c06faFsEv=WV29^2Sj$6=U?FfZvH z;m+SR{4ICwNnN#u?RtRe4@c_ftKTPk;RQ4^XXD1Ao5f@TgFZq>yWw3&cu>P=^uB2> zg3%m9m2cjfiZ(BoI%TQygWr$8Uj7gk*)tPYk^1~Vqdt`!(SZ`(21N*6E|ve`4d-7U zqCeMJ6Nf-F?m6nC&jqPN7vpYL@Q}pho$WL_0 zx&@EVEoKq`(QR;Wtj8l;(p&VTl~M6L&{Wi2)Iy8r49+Mnpms3rq>Q%*=6j z!>v=De_GcyeHfzb#5jnVZ3wtL6Dib6cAYqyS^dtJ+A|u(`^7WvU>20(fD0JYsLe85 zEtO`Uw$t0l__eM(9$SN?M{L0NzOT0#MaJK0^$i;w!7V2$r|tXHtd>m~xaEgf-}8y@-r7t;Nlquf3VB_}9s5kpj8?BP-IXx9$03gUtyNjVv3D8&pgg`T)lA+{RjO5F|6mZ4Q-=59X zxAJS-*55etcJ^Z3TE%vfX9ZvxH13@VfkS;_Bzlr>uIX0vwTCQ=6c|>>bN1EXb zv$g0&Kh$v+rr8#Eh~wv#@W$j};wNYnO@k6YF}+%?~k9v%Tj zmup2V*YpJoCbX1JNM9XLJM+_-!@4Bdz3G_dhr9mnx~}h~OpIA+XcE(dKfbK^%uIdz zG0ia(Nu0(ZeDNbw;nvb1<3RrU=b$rqWNRYO&Kb>6ZmqOHIsyPMLudl)_LH;TSTcuK z>m>C{i)6V%#^POSiZ&%82jbmPb=VgA0#0DNiwI`Nn z`}{rnja#?{C*XQJoTMpee)L8BuKo4>QD>_Useiw<$ZB0dq zk!|b7({SCqli#rB3qL*Nwhf(f8ynMj-tXiKv?54V6`o-DkbwD-r6};Lt9QPX@RRYs zoyx^EFCejB85)6fX__ThG_u7m`kpPEnn9NGeU!eH%ql5oyPPl2D=%QQ9(_c#w$_Y< zA8uJUH@tP7@Gv`W7Zi^Zb)9NI!1HWQ@GaM6Z#ZH0IH@Q0CY>&c+Ns37lB2U&0N2{l zeg9E|C$+xsURO$t%a{s0u^cVq=*>2i_H%{2_)#y`h_VqmlEWo&i;OcA3piY*cl_{M z{bh?jixIz9ApBup(mG{`Y)NCE5d`H2Ms`*g?mJ^O^{kHf)F~GZ=XSnqGL$DKD^rcT zO1>Lk;MO4YdieBdSLP!95wCSLAJ#@%^YV`+FwH<7tP>d%B9mDh<2PmhM&w{{pu$AH zs3^jONMaD%el>)#lRrZ2>B-)T(P>;$MLp2^g!NmMvOTr7w_G2e6*F~yljif?4PbI& ziB5HS>Y1g{MRrYcwvC4^uceaxZ{(IL+mSZ!^Ht2d-ckJ3pbtg#w7o*_g8NONK*||Q z2+`A<3wawxBJ`YHWb3d3-WEa-`rW$C-@o|m$cQ^^1OuvB*I=r2wm!@l^tLeiLf5U@ zTHM!L3E3Fjj~iBZxZaig9XuM1*rTs}YZcP`Pt+HW+;IDGvW* zRay!L*Tn_KeJe^?Wr|xxpFhjlHtldFHyjdjKesT84UF*H+h)G(rflbDM&~V>8ld5^ zfew`~YP|VnaDrXOt;WilW~J*7vxA&js#X96VnxTcJk$di%JUv12ett=x5@j7x<286*#LUFw^}H;SS0hO8Tpix zgzK;vN4U{pp26dIe*~6&A;z#v%+Zq^-ai&nEh2Q*?|%+oPp1#?=i%dp0K3$|r+cT= z*b>94u9A$SRRM^3882jdk~mzA;wWfYvwnu>Iob#)-NK7Z;HAH{xWj9#F-sP&J|;X) zEk=%}vkFp%t{-ykWJEQ-d+;r=Z|q@3_x)#lEMjM!STu*dMv*lBM;^}v_%63e9NT3b ziz;p1YY|pR!=eN{b7(>eD6!JG5V%c+EQ(1oGC?on0*^QyuUqF0IVg9*26rN^SOiv- ztkvk*i_mr>1`a8|Ec5TdVh_wsV(Gaq(mfwP6I@+0wQW)Y7xdEHXgJ>p_+u@-#hCSj zc#Av)(kA-sx5eRmPJMhz<(tT@pi|p?@;w!{D7?kytEYNRYtt>-L&Rj0mR^J~Ywgtej|A1z<|6AOkn9GZA10MBA=ZTwm0ahsYM(UX(g zX@7$gcLK=bws`P|hQ16ReN{e1fSvTE9y|m?pzUCe(2K4)yl|1cowl% z77a8?t3}gq`Go=?9$H$jl#zKHW6mC-Mp68x!2hlSjBK$`S#xMiK*DwM(fJmJ88}YP zYjju=n)4xYSc~HMYTREqo9C*v_^}6Rr3F0@a3f~8Lhl+FTwXC?_k{e)=jM42c|(AL zJQ5mbFSY99TQP=Pb23z?E_L^Lkg*&SlTOv0It<0{9-PjfChf=WbGbUdvFYaBGTLnN zaNIOFXOrBTTp`|-sOJvS&X|Ao$QvEG_Htg0Cu6*;zc zJGQbX&gXEP#Z0CNN5^^8p`&pV%Z=5)8(mv`LpHN!rUg(?;g4(X32^5B%3@E;yf}|E zC99>_Pf2!M*KDPo8J>)t1=&p90?+pFK`0KVIQXYr8$U@T{8-T7bkN!K?n!ShN%*K) zroYpz#tEYy}& zes<=U_&l&qY7#s-Gyf_lsnO?c1s`hGD9Ggxz~MS;KxJsrRPA?{ulPGv8{s;cgDk5xmLZ;xW!7KyVGCi(jJ^ zz2~}kpU5N|dpkWukLH7Ohd=C{0jjnePrAsgQe-((OEPEOmPKllmnXm8R|kz{W6jKmFtCS(3xj z2G^@2&Y+0370U$VS%@{vyR-Jwv}jv*qr@ww{lOk%BNlAlj4ZTMoM>C_WD)J*r7h6 zO+xXcf=|!HU;2aHkB9f>jhBCZtx?JZ?1c!47s_$&omBVV?&jsaUw>?HcBh+F{1~jUq-7IX0}9E|0CGWG6f)G65l` zBA~JcQeRI$AR9p5N|l}L4V2mBKd0DAT{xtg!>y zh+pbP(r*){Xf%L53{YgB5xl*P39`X;2QuzX>uun`*3Lb|?ZE%KF=>wzTbJPmub&g9emO61=zKVvPYCQ4NYLa6^m8-m=jpTh6zd!fkxs z#g^R@NLE(YwLmheIFxPa0hqoVVJ`fg@@37Nqo3`*eFts^2Gj@zCDB>;*xQCnfhulY zbdlmTt?*s6QmMdKtA(e9NL&tQ0zgptv3hOR==A(LL}*5>2EOk^_;*c4HVG-_)<5k2 zh}Cl)?|a77-+KzQ=%7R+dK^ER_Z-_XsWtcj8`%~EaoXub+A)<-+5{k8*;H^&dmf6; zCw-FQmlDZ{8c(qlT=p+;{0?S>y3Tiu>GPM&13eQ+Ci_`_Q{~Hm<$cqCQ-p3)o5Uik z(gp=qqTQe1iYYm)`f;{AOiKL@J4yZ11E%Ax*gF@wW)whURayJFbe=n36w4B~(@*%lpZxI@${|GpD6#-e=BzVZl8NaKlmH`s71V zF3Hl?eLk&5TfIa9i7KNLgNvio^~$9TTE5x6hVBoCiAfPBKnJ&7q&d^7Kf-{>6QLq| zTb($QikN3@lxwX<)Nf@6!Or~jC+{Qo^_eStw-c83b?Cu0$!tETFex4}8hcOV*cYz} zHc}R!7s{04=JdS?&4dC7K4mt`5zVcLW$s;~K?2|nQ$<*a)=Za9x2tUL(F37K*RuCy zqU)XsDNYb9BZ!g@B72zxsZlg||I6i8n$budzk#XmfEU4cns z6-j;VIFxR~ydoYlS66ic7y0$W$w@x8JQM+rb5|_C7;}4u)hwR(%p+mlOgM4ceRVQ-BhqwOdU zU1AfnKFFF4B(Ui$M4VD`_V`;8gA|CMbcG<|a7s4u(^;*&9@p$aPf?!W^Ep2~Bcabz zI0m`J5l8*RUB6_7_B2KCw|LftNp68tc&!!^gf|l=wi=b4k1C(0%oU^|d~nZOoce2x zJAs_ou9Aj^0>DdSVtZirog|qkD@LwIFG&u_&wW6r>PU zB@!#tGUaI2jf-&09@@$OJ|@(_g1o^sO>-|+4}_2DlJnG;q&TLws0dbBm=TTIKRC(S z3SUt+J{gH1=$2o$?xza8hvl;b$Az?R)u|2JjP{zK1mleH*sS`#a^tPqbv=gQ!gkh5 z69Jp3zBXNS8YoeVrf(+Y+(mGEt>Yi4Q7(%+!3vd;gSml zO20+Y`n47eV{nBD5${>NzJX20K;9-tzTcWuvVg5_4=a(&@g)319nub zWG40&wGmlOG?m|lf}s8Cd5!Yo+I}zf2?Doo`u?I>Ne@E$Fk&;<8SVJ<{1l9EVCh7X zTLeD6^*p4i$~rnyvm`;#uEoMpd{8T=o<5#)#bpTRBKr*nnXADmWR^iie9PN#5wltJi&(awts$!ef9e|6Z8M1p)sS!tA#E^f(WLr%0+GUy& zeA2sXl*r2(&HNZBT(x=HgP3y{)XKINr?q;3!SZZ{zcsFm!lAAtHY*OLbLRX=Sa87~ z)$tZ}X0f(ShN<6rL4LQrVtt0W@-6CpN62;u6tiQs+`y$CElPMZ4(Lgj%HP22enm1+ z9)7mmwFQ2ZsfOH^w=0Ghr94vn5ac`Mu!UOlEnctanOL*eVZy@+YdQdk+URnsBjXc+ zG`x?Z!Dqc;Hyg9Ro>_$-Q)mevmwefIw7-uqv{q~_fOn29GPxzaVfXSBNk7@=(e?u{ zn&tD0D%v!>)nU1|{aRhs|Btt~jH|NizD7ZiPy`eaX%GY?6$GR~T3S>Z6zQ%_H!9uT zN=S!vqaxDXxuu&e4SNIotPR}v`#;Znp5J@Um-9)!2-mf)wdR_0%rVB?9N>qE7zW8t zdFf5K4(F!$e%d}bkIJ??=;7^B?Q2w4=}kM;#M1hwYlIT)>7s!cJb$iPnk{gRolA83 z#oZGl=ezf`wu{Yl%85~0;bK$Ml&AZDKr$Kw0BQ*u=&$Vab1lX`RY2y(wtAf*4K|7*?W?}7&W~t3;eKeah_1M ze4_v8-Ql*$j>7DF$~p@B>JB!Rs@!1FD8_42vepY`&l=(+*Rg}RFItul#Q}^#3;q?b zY);**owDMz>NXh)vsZ9cP;=jpoId#OXNROi68D_{Q=E#@@L}dEZY}4*WZr0`#&w28Z?OV;GOzxPx10ls`J%jT# zKc4l?kue7x`P2#R6 zBQ3s{*CQq;6*XA6Mj3!#`xohoN+Ry92 z&mLOHxf=fWht{pAEk=35M&Z<3Jy{~#3M--tq5B(SYEt;*3{w_Dod$_`vt}$qo-+Zw z=A*^YpqdcfcK;(5?H_siQ6ni2us=Cnj%KpGkmr=0#gG9-0!mK>|H?tYsK5X26C3#x zi`=~OrtPPBU}!=ou^qGp8A}>2#GYCpa2Hltjo49at6ZC?!+bc5r=+Aml9JFWN^ zC(=F7)kEY}`}9?S%!@$lLKt)v@bSpY0TM&Uyy=E*tNxDPGi24XCvvv$y<&Il;-CQxwQV5HZ$m zUdD#g6)xqh>J{onP4FZ^#Gt%JgF#-wyUf#K>#u0L;UZ^1vGMqeDz;41Qva7Qq9rMI3wm0K*XIc=*YlwF84LO}h_i z_uJ%zH+qIdN!xlf6#;m-Fv&);%ix153L+d^gfH7pT)4Stb~1A6pIR4Ym|5NJX=v=t zpYPIwHvpQK1^n+5Ll7lwHJygk7~w(;g#dtJVFgN7&DGV8sJy} zJa-V$de_>MAR7jtbl`+-IW0AQyt&xpG#eXA&P!S?nI}oucb)KdVAiK5&D|f^yS=B^ zcMt1f9jC3ZE@N`xXql{i2)lok6zNq}nusDgkca{s>rD#c2_V0|f1?Z!hv7Nhd#?+_ z*kyL+2)dS*XAgszKG)+BV*5*KdX;^j;CoE5pWO>pEK?Ai(5mG=QAbQz>LuXffP5B* z+V5%&sxjvul)a~Cu9wP)3ZdJk8j}9;x#2ZsOkaL}VM)4;ppQHLDZ3dpuux9_zosT;_W$>PStx_1jrzkl;!g*<6} z>w1G8{mvcNW>N15%;(z*GKicA8`*2TAL;jJ<#KJG;}YVq-6bCie)Rh--tU?zt%iIg z)a_0AXeU{ZOj4v&J@mwVbl0S}J7q7GYjE3TnXm6OUhj!jpe8#oR;vihuFz;1Y>5nT zuo$O-v1&8*b$b9re4EeZtmpc&(7j8-&P~je-_(d}7*3WFsCyiEH0waiS(dW!7x_d} zIQdhmR@5>Z#&Ig-XiM>ax2o!b zt*=F-LcVAEm9<5U;=1k!uU-D=g||n8q=w~95v#0n9y*!gm7QTO8AuV4*9mjbJ*=r= zeSsP6PBAfcy1#s0`uNZ#d%WXgf8n*|=)#KOYn+*LM_BHPG$F`i?j%EM?5eIL?3DEXyd_W0iI)cFDqR+MfV^GiBo zwCsdg=iQS@A4FG7AW^6}os+ZPk_^NiSf$H|i_60gHW+&_u-d)RF~HK0*@{=!6F-kD zQm3`wb`!Nq=aLqNJDc_phEVanR2103Bo(m=vCZ4pu9Haqq%N;N!9!qW75DZ0?BUDj z$!zR$!tikezwTP3=ZVQ0Beyvhw)BfF%WzYjE+E%AL+*)mfFe<4Q>DUoEzGm_d~`|z zq0Gi9_Nt$h*QjFOd~;0v?>DMHL$2!%ZzE0O2|1nnX-r9Z6&XT+LW)6w6T!@zy4#6I zpzfH^5NuW}^QKVyq5Za(s8!t=zDAY%#}6%)f15DBw5ecUrYxc)m*qnrSPlwuZhMnO zV{eO){r2)pR75t`7hN`!2}e$&#g zSue}p3(^kgzP+YF5!znxEw^xsZb)wpk?JIeJU@F;Yl)%t1{A{8RgKi+@)|o~pr-M!A7MDVM=HbEpfb~M+a*4$ zywi^L+Q_ly7t+$wBa(ze%dhft)!$cYP(IOI5yQaljG*;XNUl!hQE*JIEVmHx&;kT} zn|K=oI$w1^!E|;#T9n*C+o`K4>eWtNg8u#S*_ZmQorr~oU(w2!zr5D?>szPU2tx>-C`K*~J=$>Lo%G@c;>+;pElLfzzArt{f0=qW|9y`jmNr($(%LHwa517U z#gBHEmhC%uYvdi1fUEB@yC~AI@b_^zBeg0DNETd^xf;_2r|?M({9q9)FI%IiLjynV zF0)bBH4W(473O&;uceio0_X#sKd`s0?TBx%kzbE+Tl_s`6+dD=>afCgJTbvNHFv6a z{`_#p*m_FLG<92si-Nsj_l3*f0WUdY!8s;xJW75EMSg&3!287wL{+0nML8-^F`suD zu68oCq?TYBf2!;7*CRM?XBP^Ee|2{(5$Yb6{#fGQ&e$a!+pO&w+flmObxnqynsU7Q zLqs5vW;gfwsj!=z_M5jf9Q=%Ty}*`4VJK?}V6b4IY%@74^gL|^&#@D8?X0R# zI2~R8k@_2wZ#vbufhc2xK}7SU$)ydXvx2Bs(fvrpuFc*Lc-_M}HzoYLp+S<1GbR%J z@ShHk`BFa@HR^btEKqm8!}e%(1ZF>vwVRt>TQwr6YUiq&JM+2CCCYJ6bzh;K%#`%T z^=&oA7ola)4p4)VP1^x%zQ4`X(Y+sTq67E%lakD^nD+z%udwhblwYCMu9Z}GQFGJ+ z--LA>8Y0c#T}Mr43MbuZZb@hs0_Z@d9oLZ#aLsKvLIg}HqtVFto*S9mrIF*Mb$Hfo z98^c~O2W-+$A34zt0clp==_5Tja$DC&B@tMJ9i6imooRW@%PlZnd97R=4saV|7eI=>bBK6*`9bdWHiI9Hd$~p zIG{kDz>9f&39V!O`Hc4qZKnbb;`34)xMXKhM~>Fd@%u^ov%6!MA5awm^}m_{<_&@XxvP!5dJbrF+7>7IVd$jDE)4B&hK> z@Gvi!_Uxmm-5#AXSXv)>O(e>G+*4b2WK|w~gNEPP1Hi$FKQ^2*jxmka5Bm#0EQA5^ z9G)4K0f)DPLwk|a3|n*kUwy9FD5xA~Y?LgK=3*h2Vtk`x@(ks2s+aDWxJO5u+)`h> z4!c)9%u{P640>=2z61H|)t#$i_XChfz;U;j@-OrWpF;9TpSw>GmpRVV)e(ty>ji1F zwS`UfMGPtNX&CjSJerdke}g&EKkBU=hema3xqB0GB9bTgL zRqJ5NJ5jv)w@+xuVsAa{bzexuQ?1t>nw_N;dA8@kxBa)p;JY_^)wKYlkJEfBFo2;{ zw|B-CJiL<^hOwBj=fzUL#o48c7AR#nmm!G))4MX2gdgIPTmz7Zhuws zHS?BFw=(?Zn;ZZ1fkm&H*7rUA+s$Qgq~rMLfp>s=%_3dxpTVw0Y64-4DRvI#dTOspOqSw_9*^sYa5C4_(x zDei;!D@YyvWRf3K|qyPqo3 z?SJ33(1RXKhEjW9EfW1?$pxtv`{P}ztLbi*?Ct_A?NR-9!+RC%b~l(8O>^_A{>j+; zaJftc=f?Q44BhZ-!0jR%8D`&2x^6R&HZdK>oBC_GI{1h13U7 zp)K;Vw&MD`w|w=Q?6Rl##{?Fm%!>_~D_yr1hsiH6FlLm|d?VW65ZG@ZCh86>ZN&;4 zo!~`SeQd95aB=llmBK--n(j%5Xy#?F!wmD4assJRdt6?OME(<^H`m!zkw0_hA{r=y zJ9lLoWq636jvJ}nf}g`}+d8Y2_*I5URZIp%@J4R!gkw`j$raa0#j)w>5~EABhHh{) zs()3Q$~X6gUJF)KIQYp@(%uRL#`Y`CCcET8Y-D&df^WUux4vU6X1x!O8=#X;$~n8q z#*cIUhc3L6z{^GPHRqj*Z7qdFRuPd61jy;vs>JqOh3+++ft>?7=v?+I#1I~5==*h8 z!1^^lc&YfJzp-bI$r3mA0(|P68T21w6L*VruM-mQzZxGh-3sH|R3_y&D9sxBC=xO9 znH9&Tg^l@!a?n{c~kx%ceCpnCB@defcz0WZ~nA z8+PT>dH>S4YlEnDW#JN8B!3wq6FQp~tUX>YSjtw@D<|fv^Q!MP0JO5^P2@>mR~{15 zAb(#(@u^#8e}>BgCE>G>lyg7o*OCUx=mr}_RZ;e!TR&k`kSEE-qU&-7IXQrFM8qSZ z=G?ooDR7nk>TpUf&2!r>g^c0{_1Ry(sZe>G;+_lvR)@e%Xn9XBj;q$l!JViG(B|9T z&4hVMYyJ)`PsNg>Q~_kCpcIeUlQNSX|2v_%Aw5@~_NxMy*9&OmdLm!t}`QQnydZw2+uRJrE<4zTBzWfL?XXSS&=z??2As&L1b=AJ>1F{q%0Y7>KQjO zKiQfP;b4ZZQm^mny32aHoFf5+Z^iFA$|mB)|2gRJFcuxA_JB@A+OyWcpVEo+dIEi&dE zI*}Ovb6SnNf$cxlV>``|G~wiJVM9k+kvJ_Nng21JYqiTN~ab_ztkTp|IUl z>y!smO6gYQDaD2)?5=wFg6sE9S?_$_dhIq6$Z77MxLdE6%0|Zvovvbo`_~1jAfbFK zodAYW=;xKQy~dw93A&kLKzxO*RlM%0-mpkV=Z&()$e4dSbAx7`A&FD63DHwbK`5{i ze7N(FngbBG8Wp#Xr=XIpV*ljHdRHNB3GG`5wqwZ*-TVY_u^L0XwB9l^F9CDsI{&WD zrX3897fs=cqVi+-Y#SEaWo;^gvs&_iU$FQ=`=Y_iPdS4|jiYkrU~5S~*!5(607o{l zd#)ZR8@EOL@LPh*u}85nTD@}%eD&8Ev?M|{{)Aq)Rx^C-j98oMgplZ&U|B;xSM?9} zkJG5>I{9h4w1k2j0U-HFJcdCjkl)pYeG~OTiV(wUli3cjHY$!S%dO&Y9Yv9`L+)Q> z2GG83Hj$HbWE^NyO;u@hnLR@gc<#upL7Tn!a`qt9_2#i|gJw5xGJB^u9ri5+rz2B} zB3ij(%@Nq= zj)uOO#Z*`H5s&Sif315Q_$uM--4@D2o8ClEVi@}}#wB|PQ6=XLaXv|mip-1I?{YXB zRq1Jkr&=r2be-(Hd-`B2fLe%M%v>vZ+Oub*Y~gTf0?T^i&tF6^NitqU8*)I+(COvFO?h)FTKUE8)7d*6LPCbV2N?FjS;y)aJ>3~apI<*h~MVykzV} z+PA_t^N&nFKbWi)ISX_@3Dzqm5&ij2qt>+&RW(b!UEkbvc<5SdSxz4L>vm}uz@Ox& z^>3am15z-FFoJYplZnYYpnaE^WwB1nDw?IFnKr^Vsf$4s?_xb@l{EJ={b5MLN}r>`m)rwTM|nR%T|)h&O~eHtu$de@AtbXA}iI zmLvJmw@s`ZEiEmO66-zUo%Qr*Bj=bb$mmWO$XtwB2#+qIDsA33uSgFv0bKrNer<=P z5zfzf9l=9xc6f%l#i_V#3lAv1w=EYZr+6P5iba$59zNd1B{G0to{3N(=qH`6=L z(~<6AV4OMtNenR~nnnI9Nux_>jolCK81B&Oyho%-f2Rsza8uT}L3P%B!}8=$%fqx% zNB#g?Z}}7^C>$QR+%$UrN*%4EeRm1GGFbTntD4*Bzi?TnE`#FO<-o;noT8nSe_&~~ zOl>xE%tLg|;I9TX;}el^c@uMcF3K={)$TiB#|{`HrN&RBnmOo68-vp`^}Tz!wjyJQ zPmGcR#2$+NGq`p9_G)@Z`|2yr-(LqoI?h(N-L~*3?r-GXNXT3%>n;D#4RJ~uQ+6e> zYs?g<$6n`JVS=|Q{9ARrkp7A6#;u*P8O4On_aGA5XYnE(>;f<*+96dQ z9S3E^8Nt0SNi{t>r0b?_8@l-h&S-^R8))rPxE+5%;Z0Lwwh*mc@;}DS=S3w$7?4}) z*0jc{wso6N+{QAjqQm3Zx##_LgFVp`%Ve;}4py*@(`fzOPecPHZw9}BS0^22vRK#1 zZlAX`4t{&*$1;#V==oLkt5$zWKL%uhj=uU;k$ zM7UgAVh^a|9k9@UwICwJuWTuJXg|;Timg?I$QT;D;(sVUNJ{rXy)Q^@{>Fe_(E%gX z9n)JM4JDt^Q%gT6W4;@A>m8Dk@xZ-SV8M3bL;sH>poO_>@nTF|iqvI4FlX_IM325H!JM`I&AU5kJkeLD%RT9q>pJVY(KlCh&!hW1~a9nxZ_VZXyV`j@U{NnnhH!4Sw{4S^3`}WziWU+tKytE4sH4f%ZL&*a(%nP&3#!&C`u#t${ zroCB)piXTWdPJIX&$Kfu`w3lg0PRP{fwemV4g+csHax{y-R?(re8)72?o)#3er5GA&Mx_`Y<7Z+ z6m)soDP7xPgnM@`DqOqr_06@N15+fWu>~VkWMPWa9+<5#F5hHN3~?`W|LDHwX51lg zlLY<%zWUikwb`=_*@WnHQWTJsvZ&G+K-)(O@~ZX1PdKtGi$;z23c zDc@tbQW4O$z`5e%)d|wIH?euc6}OlbyK9%ENV)Yt6rp`7jvXUmjR1JoM?oQ{v^Anu zcy(1tt;=T$r{g)h)7$voR0k8ZcRSf}>Wi_g zPBU_2wQQ*ek`VA$n8O8kVxICmedVC*cm<>nqbzy>)r3=&=e)@5{ILDJ$83h&YHVvU zS`Y=0z7%n|ko;HnDPQ&dl-beG6cqfck_Sym6V~iL;f*rx4T0nGR7Q%7lT?)<>a`f> z7xr&-w{HX*xjB4#UPhaxs`Jsnl8LowE~4Vo^JS0J?M_p>e!Gp?sly>LWy%lDJ7vL! zzxUVfRJuISOCP={G;OA~?AoBq$2`tOKRYKx;9n?qD*Y_5KOcm@CblZBJu#|1T%MBG zd2#!_0~ssC^Yo8EPIAZxee@AaOG~?n1KxH}vY2BHHtJS1zLyH+w2JXNL`L!X>v%YH z=JFj0(TE_oT$&U+UYn7F)3Yj05ILlDQin&*KkTUa#IU3{l{($NcunbcIwj2{$5(41 zh2rMLjCo&8aX-O>&GOxxF7|lddkvOa$ZZkb(CA!Q{PSIOyLrHzGSBrW>(79RAWq%+ z&ag%71g-Km_W)m@Uz_}vhzN*ghSLd8s#b=}IGPWWQugXa+I^8LtjrA0-e_|Ca~WV> zBGodjz_s-@*$EbY{Xr6+dN#FiPH|Wj=sCL?U>*44Gbg4i^i6}auh$g zxbzj87Yi@jtMRz%s%m-d102zjo>7s%9pcgMbmhtn5+@Z0pEE?qM9_=tV@5bXqbHZh zqPxqP5%AD;-!N^d%A@6zA|FCpfgWb!@E!gb+E9ycGT*huU4+1{`h3{ zygIgdx0yC2Zb59^PXRS;qH?UG5pWC1Uvx+HiyeOv_iyiLqhbMo1&t}y}Ol_^-aTArT` zvDs9Ax?hNl+yUN;%sS5?(j|F{+>k@-z6*igUpC1G=Iox|TSQ^)vTc-XD9A$6X=Lr! zt8qjZxNtxMMX8k8Hfpoew_TZ&;y91XY1wJu#uyoZ3u@s$FIwxyy<+RFO87>=T#G_e zPb9XN!Y7m83SZC&iKC-sT{iQO?crXKFo)kncZ#JRG}39@xRBGLVdhI;XbrL|@Cfnn zvJVgIKw$vp?+Epd>j)Q^j~lwxc%V!;W%77+l)LtRSu2;|9iUjWf8@X4k+93D>4bJA z-Rr`uGxACamk-p(9RBz>QRpbb-V08&x8F3A@w4TpWbk6phE;?=wU;(OKmS7t$LNN+ zu;yiF3drBFMMfyXdf<;)z2u%qTIx~5>B%k&u;|g_aN%b+{|mQAY(9kY9o$WP?Afq2 zgwVu>FrzMwb_@cMbyG!O{Br`}Ns5o?j9>bY86Gv4e5;(YaMTsJZ%*y*0g6HihjRfF zwNphSTedte^^@o4cC#g+5${(%_4{D;oY$O$)i17-TStu^IzqK+;3tm_l*_f9XRQsF zL$)l)3n7DfwZ#qRQYdUy+rNCn6F{BJ(6i+{snbBlJJS4! z6?(28e-b%_rrPwq?!c2+FBId~`ry)4bOdEeNV%sBX`k+&@4+uY2fN_Mi8#B}7Vc_573)N7AF zJkuUalIVGaPTRvg2FGR4Tx&hj&n_W5;CqNtb?hJF5mv8z>t!M#U{o4n;pgR; zk6$q1_J8Nj;IN~o;;*2v@Js@s8k5nFMn3Kp4tdkZV*>IC2X&AQ?OXRq%xg)rZhc$? zPf3$Q<&~1#(qg4T5|pOb8jhf1_JFf&)EJQ!(uyzs0-KYL;3VlO0fqY0n+69-+{?g6 z#UjnOcD?C`mP1cT)hG1Mv> z<|J|jQR(U20hedi>D;`W;lOzP3!hi?d%*o&*6 zVurGNaYO(01mo97u@(tAFn_lZjF0K{(Vrm~dKkaUDN4a9#LC#gkVxgy7qBFtcv|am z2Fbl>=I21|UR@p&G|Y7$?;F>KzRE|6oWoNxPRQYI--J)j*Eg&?fj=Zb>-JJ|1-@+h z?8*Or)^~K)J!>~g3XS{rUP`XLESm%^$FCtt%LbJ!{gi}}5Z_9XAo17*xjfgBKg!v| zs@8De{hH`5NPjx1IU42D+~Is&2WqvJZ${T;#$n~@z*2+%IfGKtt9^BT^)nPCtJKR^ zJ^#b3%O&GlEAX~MdDu%G_pe37fd*EgdgY`IQ;y|&Hz}80%;{Q#UKfvQ&b_U}$#7wf z!#vZ%qhkZ}0I>wg@Y6-wBq?VN(^e2E80@x2__)1ybq4 zfhL>$GU$cX`~KZ~!GU&#@z0>F?RC-Cw(tp?1?r<)vd$@K@Yx*fY-)Por7S3+!!KBJ z!oMR$2;Hyo>-4KN?YCwmqUor^8nhdp8UwZmF3(#$7(;$GgfIF;kBgWp+In{ZS7W59}@s|I`X zwre7AIf#9BnJq;AUC z%~JHUYg0o21fMNdt%{I%9}MjK(sFKY_3p?Dojx1tOx(DU5&rE#Gp#csKGus9qA$kK%QKC@Q|~u}XWg zIHbhVDXQ{`c~N1;E-Deeels0W7&30*JYb#!>3`JjiD(n)D7bk~=II^V=Ut9hm}Uve z>%I{b4PZ{+i>UXHY-7>qU(J@t76(yRXMv$N6y%CzA!<|R7=);6xa5>4Q{{>fg;T@u z*)I?N6agem3HXqTt)K3C$oCIeUGPlcCz)Vc<$^rdS$v&n5SylPkH0eS?=3Gxz2MQQ`$CEh z%iUb>Oy91w`fYuzf*jzMn7Caqex4+fq2I;Phe%~TKh6xo>OugA2yD-HB7FOwIl6{B zqO(Eij$`Ypog46FR*EYQXl6Os@D!epx zZ?yZ%4o@6TZn(^^a-!S~_Y$Tr;ZT1I zx688}-OKf^910<|`|gPVn8ViFuMm69xDxG$)iZ#S0cq)pij&VHOBehA~x zWktGJ72g&EJsQPornhzsh3JkRBPoG4>Pgu6kB2O`c+BHA%iF9t-vKRI&Nd|JYc}8p zuvpcofC{hKVs&%h08T^cOeL7BalQd86%$(Uxix4MAIR{hMtuYgGo{lNQWw`*L6?=8 zkAsu545p9|i&cDCSdqm%1w>*P%T7?kxXr3AR>0Ef6?B0;T|L`jOmB}JgVI;ecvRa4 z#nk5xy#tkU6jzgNnS6Rx?Ct6ssahNGZO|75ha$Pujri>go&8CQ^AW$r=4MDz2cNCx zeoB#L(e7{G>cx|I-N<-dj}aVgpks#7;;A3>P!#_4vCd5@-4Nr zZ?x41vF9sb!sqEZOTKa(K%;%ypO;iMRY`D*;;|_H@y{j(RVhUI5M+eM!NDRzP1j3j zxz}p|Cy>8Y6JVva#)W4$D>Ym!c^q;rgdq8wrDqNkE{H@`UXG)p&vXE@Ui*Og0Lidw`cA|Bxfst)0 z^h6TKs_`#|>c2bJnGhqiM8EO}84_v8dS51qH~iG9=--YtRYrm6?yE5rcP_zAG#`HY z$H2e%WSdthwr{gG2Uo(0ta}7~qN<9=#c2Ss`&-L8`G*07pim3SoVue(ckL+;Py6R( zA5Qo3Iq=ohxts+I2kA}C%){~!npjT{P?g#w_H!y$rE$JSHLMGG z%n>Ly?~%x9skSSSB)D#oNk``1CxUpn!}hJa)Q*t)1A8pysaWejfLWS|u6rV@R#St) z@T49Yg1eKHF`d=lb4k?H%F0pjQyTAG{4S6eEj1F%#(#vdMS-@&*1Gg3)0{*jHhNN1 zi%nzSKiIsyT__6G_>=bropD5l`dyd+I)VnEDSEuA=5Su2tX=IaSPaZb=RRODTrYTj zYY?%VD~=zBDts+?3Y~KsA;K(v5{Q9%SR*4f+nL0hLiI;QTU%xC{Hkh)_LVHHn?27A z+CLj{wT=K++tAf-t;e>5B5vXQl^U(AsmmK>=GGC6X)aPvPg5_&4>bMpS4p9R_hR3V zSFWWzNV@RWaKM@i<) z!p~}(V^WPQV9uV6JgB-o(M)~@*J?dkkGHYvy>w{QP?Ki{?vkV3s5*j0)e?S|6#(I(RXRp!AM)HV6`NXXl4rJC2J z>K-PYaax@N{#2}p?ecIiHKLtZpny8hCbKwxq+m=&TDl&Tp*kLqC^*C7)W!Yq$et;G z!l!%yZ1H$2Aw%gErX2>I0TA99&v?PVC0?;hn|Wvi^;Z)OuE}0xmJ(c|-KR;*M+X$* z;y#c9ZPSubA3IL|T{gO6PGR?g&(Uq!8CycY8wL7WG42gxXe&s=44f0vUTcQ0sZgP! zG);48?%hkiI@&dEy3(WV`i>oGN?NY!nNdNHZ6lg#!HyDrnZuFOr6KNhi^J-)UMDx! zRK>N{c)dvAF{rWJ^w*b9D%)@5qqn98e)^2L65m=vra^MiS6SA32gN<6srcO!oW+)^ zygdM`i`uZNZN=8T*^zZy1SJqd zi9!VQ z{?}D9L2%hx9d^T#>6b=gfV${U6RRuE7=HAo%5E2G=kL^q+qwUnnFo90H^82_sqyXp zDd`O3GI9_yO^;UkV{E>k=j5@A1kpb(*}7RgE%B(qdftBopYr>gqZSk~4F%X+RQHT# zs7lw~axD4ae}H|QRJpMT61SxiCu9$furA)KjVBCM7467W`>I4&`P4hD_Zqih4M%C( zjy(|I;g57=Qy&kcL4l(`uqkK|9*0~g{g3J6xeexk*U!7pn&Oyp=GOVQMpEs!WBt$1 ztWB-0j|4GV?yP>5Ph!$LpC?(`bAz$HmkNJs>Dakq1AVX(rE|tWx1lw)2rj?~z9Xb6 zl^5KU?l`yf*FVGNze`Em643r~4PZn({#5s&h(e%ihvJ4Luv@Ydk#>_q>G0tUgE*K(F2dO z=B9N!SmROWWTpAiFCxXq#Wrkv%f8o_=W;<#Ojcd4*v1D~bS`TrD-x76#hijc_n)O? zu$@8@UM+IibpKtA+!;8Ce|`92#w+q}k?0&Ap}#zV`SLdfPN6b{Mow`ro?+!>K{nS# z@K3bES-ewz;KnCVE?Zuq%%9?flOuj~6I)UDgwgm(md1(PJbHOQy!d zd2PMOr3p^A6-rBt{vskE97kryFNFi(7dUAp-Gsi_$u}E6Zl9wAEfu4#bA*Rt6|j&Y z9Tt=sHqB_5;eY<(x;`#7+#kNqZQkvm>jZU6@Af)b+8lm4_mW^OzhNW|#h&5u+SyBw zF3M4SGURcO8}b$FNV3C|NmoZe)RUS1m0C4s2A(B{_qiqc*s>y(7w(Lu8Q z5(dhsddqD5=syCY6q>7}KjvbVRJI*a4KYvXBE_XQ7I1oo(?uv2&=5v-fUfh(;jG-@ zge7lcO)KUC*1!*K4y@85A2XphT5k0X&Bx+MlgZ9}sA_=Au;J72#R?*& z2s(UWnNN6B`Yg+<+%u)`{HE=Z7vb#th z1%+i}u%(F|$c%CPi2LA5Bia^&hvQC{6RUq1hqhg6&L zX#0eJVla`ziCj`!)B`XR1tA%;6=nTBH7V>Z|68Q`SX&VI*R33-c$MVv$Ik;4#Ulhv zU|)M``R?&odMip-7_7ZqsOtZ*&O7TWH(I0xJE@TGRQwofSv~op>EM9KaI{z8h3b2e zh0W4+OC2JJTm!HvzHm`c?GxUZKx-Tk8I~+R`Fp$U;0@r9>o`SHRwc8eaDCq-Mf5K} zC9N8#ZARW}@zEN+@OPdLcmbMk5B7AORnlA?i*F~47V9y7Gbzm1>9D&MI_YjdU_l7^ z(4Ep$1Vjj(@+1S}_ct6DV<1QwnU`xZxOQWQZ1A_=Ov@EB4}0B$v$&Iwe2!H{x_Ub< zfiT6ZXkU+!H9c_dUirOb0139TnfsmqpFB#NHCz*H_wS6~Yl@;drZ2_xkByQ$hS?Z9 zdbx}{_kj_6ExsQX{!{yV8))TT455`3LIG zkwtkZ9>ufoH5?8D_D%^QAx?(9X9V}fq^^^R+}o0#>;(<|t`9p42EVJO?#KVSx6szr z|3y4o@4bj=0Hi0i=&T*{%Zh@E#d?Zt^4eg9NZ#jR&<-xtu6;S{=Jl-saLIPpldU^} zvo#pORm|D86J12aNIdjvnv~nlo>asrQ?mvQ1c8`d8_58jsY>^v6&8t|I)R0{fLe5C{$(q408JpBr z)Q~wO%=i>%X3$-gJpW(lf$C=c>HE9hX zX3&0;Uo2rUKgN^I5$HiCIsl zFQ$9>BLxOqbq7X-&;Z8QI5}@_XBkebPuTuGEE|bolPnk8Z`c^yc&ZKSxLMMm|CMGa zul=+*yh^L@Np9`Les(0?r!Ew+=T{uA6w|oHDq(c;7uw`c<}%_k^}muN9++Y#Ly9&G zN7lJ_fssI&%>Y6Qr{6P+loK@Cd~OdGo=?v#i}i3i{3V>Q7*`$&chIbjq?2mzr=46j zTHqWsEekP;?vUZ9TRu01nZ!djv7CE<#*92{Oq-Ea4!;6h@H4Qt_?l zmek_fScwXm{w*ELFsUB5$!!M)x~(nC-?5+l4!-v6)VS#D@zF;ICRI!%8OfH!>T)$I zGArk0if&|Mtd87Bsa9txkRLtEIN($jCzX}}IZOb^VLE}$TPR^h8d(pMsX6R1{)2@= zS^AK2sg2^PI^WF#IxwS$q<-s7awU|P`+#>|BR-jlA=2bm&D;Ii0iGPyM-!!AR44f- zA5tkLG#N}%z@CoDxAbg$=~*h%|3{*%cezw{T%ti*V!P!6DXg@7Q$0SZqxKZH-$>R` z5?pV5h~konMHC^_?GXluzty|1nI#S(gavC;k~J9Xtn=TZpA*H4c4VNv`+ z@m(xeXzUH_QHPf&q%;RIZB0G!VNZZ_eC_F^XY`)DAyNWKzWFf>`-_#%0cT^ofsgv; ztN#9TgPMH02P{pH`d8p|(KYL^cxqQuX>guczTs6loLyJ2JoKY$yKs^~$BB7yQJ2uK zf{8opqCHX@Y%{;52=Z$&vWAljhj)u98745MVyo?gP#E{4N~M?oZCLdI%9Fwkcuvy% zHkmV6Z4WJM4=yd`vcPQl9!dA*nW1Y!@=ry(M@sr@z*!}ECz2#Xjp_?A&v9pTKjH;q@JXXa)v|coG`BuEA2&@)!AvUPAl`yoghrusam@B`L|4UJzE!^gR;|f(lx3 z-``+biYs9YpomtHrDa1wBffu^00HR#afCR{AKuHB*k=L2NZRUCyJ^M^*K@-TfI4Nj z?65!*P|KEQOR~KGACK|Rugkr0`Tv{1(CeYssX!f8ovb4lT*JhMK~G++|N5JNL^gAE z{ETeF;OAZLM^0oH9tcT}UW!+-_Cii@!P&U{3qH)36e-*~Ff6)wX=Jr2wdI!+c7T@u z$A{An@qME;(-vW7TbIZ=0eaYfo6t*hncmp;qICq^XY`GoR(`89%p6(X_@}N?FYHzS zmd)cg!BH8s>)Gya&^oSh#&nKoyE4+`+zdof2XXX-hyUAnSkFt5cwQwK!%3!GK7hP& zKn(@cgP{(n-4-++%Q5x@HE9*JwR-<;YyHP-5@KGWm@}j_d|0=-5+bepZ8Uj*hv%~p z^W6zj{p!E=E8Ecd)g(yPno^JY)WE+XOY3 z+6_Hx6eP@U2~z_e%?{+qi`K)&AEvP{b4Q*Rzu`RM&wm>ojdSht%MJpo`b_>8HgE4j zOpYIl4IkD}8F{N$zlBiVkJWt3Jaf^dCTw!g#rQ9ea*CSi3UYUI)JK$hD~+V&oI7*! zKK2;nqkvlb!N8dv z<6!9WL+8QaSgbKxRwu!W8avW5YzNXX~V#c zg@3bSw6xxRwvI%D;*0XwT_fFQ&G;Vv;u+VYyQVYOB@P94@2;(`MjReG?oCckGJ~{< zGq+YlQ&Uqym)$aBz)%FD`9eI&~e9mQFKE1NTT2eujG z!2fW^`T3o|8hRq!wav|^hTZ068&ac33xPEnN+uHqKPlv(V`YKP+JmI18%yHuqwjT`;yiIE1>~I%9MtRo zan%0Zea)BP@vC(-+l}wJ6HFdHcm(eyYzEzStK;*K&-g`8n@qI@a!*?C3$u3-zIses0^j|2VZ@(Jn9Xm%i-7>v}@%XtqfNBOw0H{5-gR(G>iD z6ZP`IHDmN{lb=6nenbh!=%*2?UBm&W01p53D_KmiGdRPCx;}!OyeEF(1 zfFP8_#hiObf_vR?=E?7$QTB)rtmyRQW%@iL@fdW*Riznk;yXJhFi-}W^tpO6!#Dp0 zkpAzXMfb8Poi_~P-IsR7?6=8hNh zWd274z4}pf=IXlE#gb91cId?HfLb};iu*M$h7~a6u^x*>EFaoggkkW*_j=g|a2fso zeV@{+zU4b`too+}`;Yzfx%z)Cg_GW%nL{%f(K`*Iw@Xc*jKZjs1`30%TxiYtCJcnM zTCAj{X6up*Z?ULmPL{mU+T{|OHgMlh;4Boio`mJ90^Yj#HOxFRb5=gmUHba`>5P|#RW-sdKt;fR=f=Rls*eRz$O>clmi1Rf3}rLp1? zEKD9Rc8{Q7c#{yMi-rmKp1!-+C4~O-*IjMgKa7|04#_2IM}Id*o?ytuH+C+PE?YfH zR~H}#A32i*CeCN)J?fYVq;Z(F@E)x(Ehi<_vl`i zxxh0%kOD=Khp&UvRhoH(4_7qt-x25fJ@7VDsRm+$+3_VZ{Yliu_+u5KiNxCcRP>*B z)pf+S095E@Vf!zl{vU7QtaOn$8N3JkH$}xKmWG-0evQ4ey2nJ&v`uqb>NOT74a&-V zf48?Mv@Fj=6Rve3O74VRIspA|IkL~6%a3Sl=UoFaQEz-`L!?gJj`IzVRFTW>;QwOn zE#sozy8hvrp?m0tp;IZ9hM`khK}1jlK|nwRq!~cz2I&p~C8VTNL_j)3kdp51e0~F- z>pIst$8-N*Jg*q|=$^gTUhBJZul3#V?fHx=@IBA;H}EfKNGBZ&T|E@|3kCp+rI-5Z z&^nSR%sFf^S#Z?5#vIG==Mms(drOyWsUbEv(j8NKiTT?V(C(wG|4|F@Yc(VZ*98qj z)?=UcE`}QLxO!svDVfh7rc-t`f9M`uYimp4Ya5|EqNGz>xx26MGN(P2KR#^ZE2rU=8Lsr@y_JORj&5t+kX_jfp8%)vNND#Ois zP}2W8V76CwDnM?<5h6G#zYu3g;fHG7B}zJRCXVfzyd97yPdjJ}CvC^QheS^Zp5%(f z@|PR?1KTt%0B&}J!VObx9Z&%)^!Q>q1MP2@c!hMfNGsL{v_0y!lv0(;%>Yy22Q&oc+m`9~ zJ}h6r#=}i39%Q^m;7FvkbUFmb`j36|zmPL1`O6vrn!|;zV>m(ziFT9S&J;E(Lx7i@ zCq;BLR58S#LS$rq4AcqT=DXW!21U-iF}0`uzaV&t8YubwMz(U-ma6gPX#LW)Ar*eO z1x7f>2Pk}bJuwhlCd~@caw9e*l=fGbjz1l>4goZ{Ab|J-9i!f#49dv+lUhvQVA2g+ zlenkB3G}P4wi$o&3Y7yTi^LBL*(GW~yzzgHp8s;}E;?W|NnpZK%n-A?WsxNQjmq4l zat5y)%_(CZldEhs}fogzKlSuQmTCKf;5kQFDXs zLFfbVD}zj`9fY;I=q}$vL6)z1L?j?pEdYnCHMaF{bf12>C-^PFIG$d@jSRWwv`ocx z_@7St=Z}9p{+NgW8#~;|0lVa^9X*g}a9gU7O$I;}?SuWpBUfHDGKk__oj=nbjP}15 z3WptD@m&ce>}Dc;9^z95_m|fWNlCu;)Kp1~weDlBqU~MXUpx%JOUc8mpp8O#EZLr45k1k*i%!p(BP|RYe&g4 z5!MIF%kCJcwS+0fdAO@TILUj@#DS&`(g=u{`lamfu~r0vd0RHk;*wN@yg0T z<)R5c2ggoAxIO2!t!_1?9Bra7Hs%O_75<3u?rt^h)>_sYYZG_L6#vA%S24F)JBxSS zuO<3_VM*q=j!T-VJb5X30_jgXZq;IbvfL4dVP%WS% z2=9XSu%e}7N$xtKt5F8SDkGEkzDFnuGreNo%FTgX3m0%Zs0rLJSU+LT@*m6l59bHT zR&XO^*9^dee2VV>19hV@bRsrwW7e$tpx4!kN{^lrb`M8Y7(awA9wy-?9r6Eu<&zS+DxwG6?2aOaw3Ro4S5ftWMDOJPKa$U z@X3@XBl!Y22S0kB$t#BQ!@1e807uFUVZY!3n0WSFf3y_(3;%#1URT(nWe3<$^jXoV z8kQouzY&NFW7Zy(A8@ggj(@LikSYdX0TThZTTmPO)Cb;HAAWwZBu18MJY2Pii92Hx zYoKClcm>d{Y|bZo>yejZHOx@xG7GZn%fmDJH!}U&E!))%6p5$4VKiVTnuqKa;D*uU zy;U51J@i7*AuAdnb|U(mrM#agTrDz69DcdcdWt`y`+vI4%a2|Y@T-s-2TF_D`iEof zDTTEcR8!G?DGhag6WUK?d?LV+L-ba()ceOWJ*weUZkNHtJgJJA=-&}gIyjRwlq**we;LR}3yFs2q+)ylYt_kNEvRsC99d5LlY zV=BZ&(PGZVr*9E6D^kK@qFQ!v!y)PWC$!Fi_V)IQKuxAI;3mESYMm&7R>Rk>UCW@y zezDLMg@!}cwmMR3+Tu?LjMQfT36#4fBrp>N0DZT7WHU8)Jh;gOB#%>K`YtRf#|u}! zh1Hvhq~Fpq^Z07QoUEoI#N01Q6rm$>tzAc}KQEd&N=Yex_!W>L3V7+L3{<{3i7^Do z8bk)XvUhY$;Ge%a;=r_WvL!o~&sJu||0pzj!*WDc?D`TeL=msvQs|!r9e5R?23!RO z*dz*w@_g0rOVa%I9P&=}u)xhE%m{dgC&3E}e1-Jn!ryrg*h>FS#Ugdt0mL789PC9~ zS5*8di}Qa)K?*4fdsJhCYB|ivKwVzghhN>k{>F24URPqp%MV{gpG`(zO$J!_qppXp z(Cd~3A^2%oGoU5x;A#rC5Jf<${I%(gMgI}RK&UP#08`Kd4L}6o*h1XFTWP_#1UZTU z)+j#OSue{g$Rp^hX<>IMfyTTrQ#ZY|Z3qFw44u@(9_vZYJX%HxW0~_yURYytMaT;hosZl>xbKr-Uhy)Zo{#0-dZR{aN z8lB+P7@c1O9RAqRuTTgGl7ftu0_9tdh{G(9RTA#k1d{i`x)msgYViyyFO}{$sJ;w& zX-@gB^wpf6r3JX!`T|rdmwk6^ z09DUxI5sI?DK5u_#|pm_3c{LycdjNt{B2eE$d%ReZW>gqLO{@QayQoAE=}lJ&&DXjRvVazM8mYD zQB+A5|AqbKScDU5pMNkrk*9Zo!sP6O3%O%}%Rr(%~Ib8B67 zsUB&vU?`rUl<9}_==}s?`GT~L3R(z%<5PgVjIWN#>3|?7){=*xAMBnyfM;(K2gFUo_hhaFZFCIvWCSesUKEdGBr2mxWn zF|scE-GMJ{|9cpU59h)}UdB~+fR zi@3zIX(e?3afDl5XuzpjYD2?;pd7Luw4Nds$ZCLG2)Hl&Nm01dZ1a|yMXn>gr1OL6 z30HK7>&_*wq`wXNrqG1?{Ik(+g-ja~fKBRWLj_+dXhcW+d=wg$i8)(z&>Bo2pkZY4 z9O&Kwcivl$TZ$l!spbj4f0m7&s*FTqi{3St&m4D772gg#pzk2P9%0+2iV@rO^4+HS z>ObVP;2fy=Qd?U4=~nK@P?HSEc_LE8W$XP9um{^0<#YF$RgLFMprtxWx(9}#+vG^V znmb6TuQ6kM6IpP-wc{yY3$(CvcHd# zoFsf(vxMz$>LZN=B0_P#RibXXv`w15DnBA}W#J<4Gs>%J4djuBAKW4O;xi!`zDR%q ze)utldHF-)ra)Y%5=&I1Kj4|DD)Prin=mru=_nIzRb~kQ9SrhUXJExEMlV$d47w3(Lx;NJzE#T(U#2JGG<`Fzoi2cV zw;-_KFMC@@PJ#29zCuClxryr3YakJ@zLi-zxu`01>|tQmq?D6gS~^ghvvM4zXx|l+ zxv_GwcPG|$qfYl*A<(co#nv0BmQUcf0VeRA9q&Z}RgWD&yVfkAi8%1W;Ii=1q&Nzt zC6OKXBsbJ>otF?7z1Hyb+=S7e5u3ZXcJWnCNxLs@B8Y4^o0m^~C z%u!P;F7%6PSiW^X^_3N-e3p?`3lzcC{WsF_hs<3@o|Z4DVn!jTP)XGAg|n2|ci3MJ zRDRmtdMHH6yv&T!!h`6ps3g9O(RJ7P6CD9+Ws&&rXFdj8U9F4Z4})Zv&eLHsI8vbT z+3YK#Z?`XO6CQJ7{s?Nrct8m|3Jo@#?PcF%y{mlaY1ktMP_+W_E;VwUEN#g9Hln%gZoOkvxhgM z$=!fQ=LC2MNYQftyd?laR!Tf(OK)UF(t+gvpaTP9*B3W@R9Ghom*_ezObsu0&+&p8 zC?P@OG4fvAhDr2ltje=b_g54;TCSj~_{NR4B!$!n*M&OHwd7=D<`-mtGh#rfZft2j zKk0eyv(kH3Hy^xs?)?y1HvrT;0NzH?%Q%tuuix9Gmn zXlQid{VQ{p6{TejVao zBDDADqm43rV?~dd5#>Zs`36yhsx_H*K%-961XNwojFBUrE?td<&VTxE#09EmRAI^8 ziq+m2)G87TqJzz4}B%br@tLvrc6efOMg%PcZJ$|5t&-M zLLC@=I9TiF0zOh2uxp?-e~0T!*+{#76_$ttTH|M@LC_j~Msx!mx|Q+JJyv*Fo*;BQ z@wqBM1wkLMcq;%iUa*wQCJGN(&XIM!J~@~9Z`^-{2As&pgnppNE>JsV>c@GPjh~B~ z_c;L8ZyGYo0>(#h{m2xZVR|3*y%2)}{`#QyvaF_zpjh%Z>n$U#MtX=p^`%ZZ+z&SQ zux7#b12hUTXpD+^Ri8W@H~bjQAGt5O`lMW@->kz4^z21qCyPq;v1OV7lG#t}1cy!7#x9g!KJVjM{dVJbPG%xfXk|cKk|?tIV9y`ASFH6J z9m~>!Cvlk5^pd}{bBz)B_?HfAcU?HqKZphL(7B#h;;05x=AewptFZ$k9V=m?DoHd< z+15O^K|sFcuu6N(*avyQVjRuCYlAD2{R*fnDgi^7S zYbpLW?6&;N@eTvZ*se!z>?{Rj@@7ZBsWzAu2S zuU0kYfCS))$(z3hZT(%@T{aK`PKm$`H^%nnq_?h!9Wvcvi97YFd}E$}<`T03*nN~! zx?YWw=rM9EyqCSsuvE>m7=ObKo5*%50H59jv;Zds&<0;fRA;P%+(TFun6QRSOO5LT zu#6v-Bs^rdVDJeUd>&Kb0RPns{B;RP?T&;A2Sjwj(jy19hG-O?u=}D%@HWs0GYa#C zPi|$)CDxobRGWi;BhyKMx;ThyB0hFaJYJFPAGk>mL??C#|Da z5Z$hC?k!(`hOU^W{YtpJ0RJPB-ST{_l}UdSq|x95@B%P}EP%=#lmo}2yTi6rQ$SYg zDftU7BPZdRyVhLdfNKa?Y&7g90ORYNb~SfyP-SIKR#vGk)$U)4Zn-(kL?{?fmzYDrYyi~Z;Gfy z)3}S`dZ30J(y+S@WO{#d5534-EdjU%n0j7;a+=Lab2KMdM+`_qxm{dk=cP0#D0~Ku zi>TVo>wHapQ4r^IS#`5usQQlzy>}Q(NE~fq_-Ih!gb;bbCHUcxc>1@xgUZ6}bG7AB z1T;2DDfrMY(|dZ2OP_`cT8zu<%$$)Ju;-+!aWx$Be_;i=^UIQ80xl|p2FNn@_x8g2%m-`fgFbhL)#sS*Ug*nLtW@u1 zZY&wQFPiXED&-6i;Ro!7$uhrD13IHe+jFwCn`mjx-(*6&ZWzbZ2bnR~+OeVI{4=xsiG{p4f0NZU<*%D< z0WuqpLY|*U;3JpgtoIZ8tBn4kDUMOERMs@0PZYlREI0PLR1GO$Psox&FuSZM6EwlO zR!5Cyey^W6>5b4j7F|fX>Vj$IKFV^*OT)aFp<_NZUyXxmM1yK-cm>seTxFOO%hw!krM9;O$hW znsym%h&KWT!^DJ;!Qsoi*Ff(Kw4hK_RIn8Mj~^dk5NU*-H%L_!H39fY23OTy27EifahKV@}JEXWHuqcod%6!P7e| zz7m&iHV1Ek2qDZOwQjR?eM` z%e)Dw_(>E`p#jc-%&?2eA@>>3yv}BH?O#tUOLcW!QfU5sAzWYiU=~Y37r>?uIhcb` zaz8@k#Ncg{lVK1PNc->?KL`p++wgGM>8V@$WKWozn;Sa}2H&Y@cmkVQsQ0Kp^^{Cn z*-$t?KR>w6yh!OxEmUG*8x5(k2i0LlP2?h{p|ll#|w z^ZFy8uz5Ckib2gLip^t)YO9Bh*5r^W%$_GBCfz@UMD0c{h%fIB!SYrSHyr2K<6-dK0PwfhhKAlWbF0&9X8?PJuKiWI7JSV?o13`ce)i7h6!9! zvuv?9pMCvnF$s-$q8N-2y6>BkIqngs*<%k?tdp& z)(1P>U%Ziklpk%aZ4Wa78z2UwY0}r&f0GbS`1WMuZX?Adi}-6A?w^|AQWXWqU?6V2_>Z2-!Le|Kh^O*L|C1 z(V4s%@AstvV9=;}cJhHsCkrBr8J@eIzJ2zO$Nb}ua1+?20EKTlAzm3}DqnLaq!xqk z3TG@jA!KDhLwD1Vgs8`kvcvlkSy3VA?CiWXIa%+{q^_=R=V+HPn5Rw5`nq(XyRYDW zMXq85W3= z{}L)1n&!iYuzU9jKbIG$BO2k`1e-aH2+;PyK@RBr$8Uw~kmme{kwEy?3nNLR^9K6v zy@MIcp9@06J;SjOA3(~3V}soME>Zc9vtD8Je}97xU12CQkr=#lj-Srs7)DfW^_@hW zSqG6j7e~U)IQ(hG&(HrUP3$F?VrA;p{~xGq2XHB$Bsjwf5l5CBYHojjeBn)~8PP?0 z4BZDPNEKmgit-N2pS?}vvm7cXcXS7qj3@Q>YHvX$@8VCw*SIlIK$>Nb5*4ifqdES1 z_m^o#K_pa*I<&tF>9SdlG z(no(=)hiAGFlr18UM3E=clDCc4eDLU4Y5{GcafH9VE1F;RJ9j_d(b^0MAs7(IivNf zT)oV{QqkYy3MY-x381ejd3P)Np9gPowR+IA&}v-!oH=H&xYMNpCMu@xJ*=g+OkX^4 z%|mKh<<>g4yni4AA2#^$G7jJJquD2y>~q+|ctzI5DZr4S@B6Ve)Q_Smi*mXN;!7`) z&Wq#SuNcL~jdF&TUo!!tt02j7UTP!eZ2K>_<#j;7j6vSLOH- zAqCe)M!%oFf_kiMY^Zw)k^8+N{ajqiWIDqbjt#kBL4`n2klLD_^6{tcZ!uQ8!HE|{ ztlRm2EQiF3R~T2Sz}yvaI!0xY`9o9ZVY>39k~3%2GVvfJ%`Kq&=&rL?4eHMzywiz8 zl-#{djaP-Z$@E^>Btg1yi=4Jk%j1w%B2h~0t0XQ=3Kl{L z`+a&_u41C+Q_dG$Jv3ZYYIMz3nCve=*Zuh(`e!vdb40Ra-pJF6NKU>MmYCC%GLV_0 zF5`~AFfuX{p-^ofEfhihNMdW}ASWocsP`p3ed&0Gfg;pj8MRr!I+9sUHC~K=^APgL zX{Y;v%ii(L4d+=XYUgW2KO2fqcnIdiz0w!~wyofe-u09DbNu>S^-R2*UlK@G1llCq z2w5=GZg-COWy~_nssPsyyZcK*N$Ft98+@%WiWB$f-R9f^4L%t(#X$)~F%gBzh2f=* z73nNff8#C#qKl(k@khpujmf2%oSxme72SzfjNy4apG4oH zcpvP%G%`|NoK>~9F5I0z6Foj3SGYC8SNCY{{L^cYA}$SPuW@a|#ip0f{bO(1-1A(+ zV#v)x-Pok$>8P+Go@d9F7zGm_ICO4_7REQW2aJ}RtFZ)$*a;^k) z=MhfO0D`5N85NIQd8|-2(bkZYROSo59ECcp)cLK#`yY3yhnO(0@bgco8FN0x^?vG&|q zzgCnw-4c*_6ly!|Zw5DINNH-CNGSpor|PX+-)dRi56;Ei&*s~|PYZKLR7~Y>$5ax`EM2<9FcQ0Ku?3 z)h8(r9!y!mUy3IRFcP-82=jY+4ZR5YaJJ~_V0~%QGFwD^I(?6NnV7LDc^Qzlwlxo9 zRchR85Lu_%){OQ#jWD~yNA76WJIVkzWLM(v)AVlb?c~@@*7toKwppC`gkk@)pVVb{ z7R$!IxH9w_#;xs<(kJQ1H!U=(Z#-Qb`Y~K))g|JxxyXls4=}Q^Cv2!Z@Bu~no!y$Ld8c!MEZMwGMUeOZ0IC%(!~*M6@@sNs=P@%b+(IYp!bfrTV}N; z;&QT{-0tIttVX}{h5M)OJz((a=WwzylhW52Rq7v4d9v&VbO%OjrUv3y4=8@Z`sVkl8n zRm5WhjFOMv@|7e#&SiTHYt!Bj+w{Nw8Y`@XG5e|tKlq1Lyui-)#utwYf^Kd`5VIY* zc1eX(R_hv2x8S6(Y5VR*GdvKnQ%{PU{t~9JMN7N1Uw2}^+oE)QcvM- z5AdwQlQ3MJvWHTI$~W(J5s~CFDAYB$4)}$oI;l=pzwncAKgtM1$fT;piwVEEb|*5j zl5PH^^?dn#4%^!3OGJv8E15M-xg=uCyI!l z#EO!$s7wQF{dp!>d<_NXsSQ2-@!=NJxaXliKCP(QNf^!Cs;$nemR*z{KRo5(kI!=V zY;LzTHF*iL%dC|du+2V-$foe-JHl{UYN2fqZr+>*5JQFUXRs zY`1czsEA>2ys@Asxh!z#;rehL*UdcLShsr51(?~)N7}{pvy+QF1f_1Cu&8!{#hqNG z@#^Tdt~b4fZ|UMh#T%Ua!}}h`p=uw4~(9KW2yEkTgu0zhjQYR;Aq$Dyi2&X8%tO2EI zy2z-ZAHaU|jr31t9xJyB=XuFqp?O8Mb-lK@TPT|yL~iE%8MRxjwtKK%Vsm$QKV!IP zvj0dW*8HS!Ty=hmm#p)aFJG&fv7n&EWCrCOVBT<6;9P6Lj~ma{#j2g=-hCb=o($n! zot6vK2*{8-bYG2xF?nYGrRDBj#rr%%R)^il z5ReK2)2`?Ut!za#pcz&6J3MLuLg)m_aA$O%&*&{nWtJe)nWH-eyY$Clv9aW5Hl2DR zj>~23Jp|r)Zh2aRACGLrp5zC!ypZy}1`9tuJ*$K)0l<-j>A2k~JnA`%>AtgzU25~# zYQF1D(DA`yD>D(Xc^gn+pS3k$)?avr(f0L+XsyQf5e1HU=G(nxtxqE5Rn2!P-wWD| zzttpW?Hl(aU^3u)+~3vihw7yW2l!S63Y!LJ*gqJ@Ccg7vRMAOt8n7cdH3nT?A^o;? zQr2oZnEnMh(skFtlNDBi_DaJ?TF$j9c`+pHOuCvW!(b9nXt6gXSB?$Ff-MHB791fN zg{s%q;Ix$n?3GkGWss_A3PJ<7i5GsCuEmoD*=LO_EMgv** zN`*gUQ-_-_RVtG{CE6LM6Q z@Y>WRJdKD#abnV4Pl7X4{Ma6(Yda+|S?{V|LqL~hYWb}{v0|eccRJ%jrVwMutBd;P z?5ew5J;pG)vsZoB8;ZMkPiYS&>mQ%2kKeFq=P$J4mDK))_YeADsf5d`Cm zcmpS2&ixFgAg7A@&=flot!H!ZhoHzs4~u@iqquJQBJ1X>9K1IcaUF9VT19HKde}rg zA+D8 zm_x|EGu3`!;tVq+X!B!n5@2bt{)(9X1QW@-i41y=!~wyMNrfk7*hhu_r0l038a1v{ z9J_D=zPkfT--zFYEkXaa_`b7-OJAL3$J^~`umXsm;KAs}w+166&wUd^%JJ$vPGYVb z)yNM60%gXNQaXrGX?_JLF#V210k&AZ$B$>pp^d%;_Z?4fuT)r@iO+8;vdb!Le&8#* zlg+=k`rWHP>$c3sM18g->+N)=FY30fu{7RrD+N#!J*pe}{;JxXa_nr{fKkms@2Tn6 zd;D^Op!^1;ukw9D-FRn*L8b2TN{p(8(3hYdsc%nr<_9Kx{DgST+i);|$N8yuIq#!8 zZ`*G!Vo`}YP&H^fgqf=#U??LU3CpJ#SWr0#mwGpifw))j$z~f32Pre!_G|`8dt^j% zo%ouhTio03_#B2FXjR2jgovr~?a$Ij+a{JdTl?_6&w$=uJe7=HdcPpZir1_DHHKU1 z$Zlcwh3({GCfBe{T9@ZpmzJOJyBlzGlFr!J>X+tUw)H>W`Y6V_KJM9g2WXK1FJ!(w z-A#(yKyqCc(0PQ+&N(PfqB-Z0K6O ziGAUSu}UT4C;GmirUMhjMBnOin=ceh3|KGl>W2ru2ANjCnqHSLj^L0#J!q0KhJnDBx@K~{=rRDMVyY+^oXA>d-Hf=kHfAgJUC7@QGLu(`T8ps`AQLlQFK1U0@4DC_HLBz;&Mi)AMUBfFk_iXFnqn;S1s}4C*;|YE8BRbgJ1dU zI&pE1aPRMnydG|B%B%Cr*`7zQEp*>E&ux5XH!kJ%9N`~zUBt9eW#SZvzR7)#ZVs@& z#BGaPV=cbN`*p5K56qXQo>Gz+&4@9>=G`y3R`-Mn46l5sH%m z&oCP+(rpf|52oF{tvof`a&{i36!r8+p*YFZM$rMS-vJ7Lxi+(U>xR6EC6nICW;<~m zxL{Ekap>Y2HRKr$l9Ft7!;o>&R-MwM)qQKCwA@!ri!s+{Bd;E>EV{qSWJ8?&Y%sK- za(`c^KnrboARFgI7N+OAo~lx2zelC^q{&5%f%e11r{f~I8DE{B!~LhVdMBqZ<~VQ1 zNkkP+*3(X(<(-`gVaz&}`tIdvj*reh+-Quvo6}?Tu;@nqn};5u#Rz7~aM%#%-K^uA zmLDcjFhgq&*0oox#TR#Cd0go6lx{T~tjW+V4cOh;`t&92>8Fot_PeuB)hfNdMDu@x zY2K~3e@d5=r}cJfZ{e$Mk$GRoP}x#ZL!3R4>Ld#+=Ps9H}^cNrATgJoK$iH*OkKYB-PjT)VENK<-EMW zyQ@byhKY$Ti$pNJ1A|((*LU+L;T6`CeOv*bTQ{Dsvgf%Pvl4!N!}<9Y$}zK;`L|~y z%B5u2`+scU62dAyb%d|!)koefn1tIjFXegPQc7jTbJ*yO>4;`049snI#{wH$5JdER zX1;O-jmJ=AW4(IU_$(-~=U(*v_%x#+=Ql4ALds5YQWuCFz9@m3`O)h&GRjmP2CZsX z1f0RoZM@WcY4q7@Zvx3Db*;zJ*DFz(R%F#DjeGnX3Ds7>b_&f$V+W-Q__CYlUe77PpPV^l*maMLc<@*rd@rkaDs3j_^<=N*{$h!zp14O`hX~Z_-J0W^Q=1%LI6~V>Bya8^% zJ8w;})~OwzPsbU6y@fxzWMdnwLBhyafmhYshE<$nS#OFL_;+31QQPufER5VoGkK_I zcym!VxANWnOsb(g!w`4kVk4)+MLs+l;XrF|cprn^E+H(ATyLVY)i2Yeh%z!Oc57)Dt7{fv-C)^;*lPz zZS27T-DaOm4tKd{CzcIwhjzJ0c`t5UAKYEDoh<5ZHJE&v`}vv(G0e9WhAt;7kIpPY zAuTTS?kP{#jjT6wRH4t`eSBbllE5;b`Qf;f0(XK%esy0W7H;cK$IftNvDhF z+K7tGmta$4O<~~S;;q%5M=~5X-88E~FV0gv_4sj`%u#3d3RJdAjZ{c-x}WK5e^jMx z|MA6dF8v9QzBKuDqxogia*R=2EQwFYdzq09By+cFsZI1TPFsv zQNrXeB>ef1xgyb*8>JiHmR>AWEq{)CUYlU1vx#H5-T{VWS(@!TRyi-=C)XSj%n(T9 zxuUdvx>ac8mtVjrkC=+rpnT;4ZZ8nrnCSD){`z!p{#q*+d{8Vu@CBnZg1tqKm4ING zUX|jJnEToO8?qt;nVpzx7wwWtv7jaqNKeS}wYD`hRP^4|sGPn@*Ms8O_ym*!+ZiiiD7|I z-86}Z9C~&d*1r0<`n}yGkO6|Wz?ezK>dv8^m3FvAyJcJ(ZhqSnHO*8 zR)%f#2J}P+`lHownsKScZ}T$vPt>~hJPB#UIC#S$gFeBV`OND_ERWG5U^m-u{FG_E zUOO3f?vsW{e9x7r-AtmB`9*E4yT_<*x)HUk?G*Uh^EkibljA-h9(MTR z*q`APm;Y9t7W0CYp`@qeIz1*MLvPUr{0B1-&jAW#?n_BvoNbNQeCRUSd%%_Q*g!e< z*0l1y3>|wZ>2|0XmLf4E`Z1FloUxlMYi&InAt9cv7U0ZES8;H#F|oV6 zN4=gHXGS7STK*U^jD9Q5vv;zuXj;T#v|}fcT-^1@+XpX|L68`_IgsRa9)vBtILu`3 zWw|xBwNHES^zqwjApXW-+7Zp&=Zzu#0qvO|!Y_Ra*2-P-q13cpwLOYu?tE=!=Bxfn zZ@ZrYB62RD%#XK6hV>=HtRuR~=ZGOV;9yZw9XGK?+Od@-U7)89bLwHf^pEqSpjel> zSl$I-$W+qg<3@rFo+KP4`L-hy#VFsBC*6uzNy+WszJ)H}Dr!Fef!d=m>h!3vB`LL2 z9Ltprrg5j??(O69L!Pmho3ysGT83#(dc zA6N*cZw*A|L&Png*f|EOJAQQJ)(m*b2`NvN@VgLy9CJ=Lw!g^s%;O|v^yWo~)tc4f z{`kr6!r)VyU|p5K1Q$tOdcTd%SDH)|f|5p(RFaQbsl9)UG>I8pcsUT50KVO_ywZ47 zI1u((`(6_UfK}&C*2f&`(eAF#uk~+O%XU%SoI2Q9sn{^zdUvPHYER#q-W_9p{KiG0 z66+aXWQl2ar~A~;88)4UJJf|`TKK18?xSU8w#O#*LFpWkbCQt`%;Z={tK$1!rSAc6{aO90pftEoSh%fua##M zSWFXBg#~4OK*aX8aP5a33pgw7!;|}!#-z`_SBDHgmuE@Wv8T!J;uZYt@WE`xKKS-{ zAko8kMrS+DyB}cgNBbe`5|urcP^TB{3;62;K?&d9zu#gFfTLl4JIr-l8ZEr$ircJU zGSQ!1=02Q4p$Uex^_eA4i)s!WJ0Js_&O9Kkx$k)cg9R69sqcO|U_nKx?~Axiqi!g{ z6QuPmH<~`(aG!v{iihH0wtF~w@!rPG4{R=#oSM0<-e7;}5Z(QST!bH|i zp;f;z;E!QUgTmGl&gAh{2B#ahzo@6`om-607Z?Ouo0noKR(iJpQP{EacXxh*`hfVN z(@d@91-MQY_0}jR+1>YgUf8qkT}{S{G>GWNmr?_VHG+!a74~!Uc>Bav?gw-j2IT}v zK`_Iwts&o&_!}h=KC4{co;{p#d=@3Y*7GwZ;@)t&slfF$zgstUv&i%H+WAQ<31~1! zZ@kkEq8fbnnff8>fP84G;==h6n+}xzAc9ZEW%+vvOB4;JES-p>H$I(cE7fK%T6c^v zF^g(=xZusU-R60WNPHlqWhO8VL{BL?Ep*mt4?KQTg$5yNDc|X^f;@tp?^^8_6CL60>4ZyL5CyPq|Z2sG@kJuQQ!ha z7)sP#sRT<`XQuC`f!7VIRe_+`EMBU_1?5A0F$>)y<>za(m3V+_{IxaSS24cqsTN;V ztf1L-pohIH?CTx?{w}4wM7!xEboVVU#rB*9KT6eXcZ1Hc#x+a z+a1jkg;At(-LSrM*kyaQYJ%Hv_h7prX8NSO_~WB?g5XCw#vJY{vM*1T-^=!QKntd+5Uc8E80m zaM~SWZ->RWO`FZ*^)7I^if&P#SEj;Jcijn18LuPA(&Wos&J(V%XHf1q3@I_M;LU@B zOq&$LG4!?saobr;SGYNbBq={JKR=)!e^xaQs}u9ytF>m zBM_i^hm_i_(4jo}rZZ`MRQdflt|&Go<3O=SeJFV>QDTwq9`;bQ?cCjuU}r3l&kMvb zfkvx;&O2D=a6pGgFB%$fYnna8XINvQ|^A^9>~|3QMP0{R^j@PZd+6UDWnd zZO^M6cb??tdyc;p^+r4c1huNfZ0053abw`#3de(lGQjC~?ug+a#!%U!J@hE!`bMXR zvO3|32u&6KYDoR4z=(0QVb49q2Poqo05WmxmTy(vu(N{5xQ&`-Q?m&=`+(?bI(WQh zivpcc!u=ewzM}VqLL|B);$m+@g(H%6ZZ?({2+}2eRuRT5x{a8keB?h~Z~Ee$-n{Pl zc=8E>dG|C+*+OFz@33KI!&A@Dx6^XiI|XUu$~py2uY&gVF}{b;-ipS$Uc0b0@!+GQ zc-YnlP8d8mJ)z2Oq-4zIx>18#DIAd`{5}EHwAZ)Mig!Vut(=l1R#*FUZNAHDb>f{8 zG3Whvs70SUZzIx*kYU*;!L%}ZDhLu0m*pF05-zw0&)j=xQ_e#`Lf`6da_UYyn4CIo zQKkZny)#x+gWvqU*%fa_F;5H_N{)}JDPF1GgGmglEes*{)4%mpwZPM|*;2nvvg=-1 z9DpZ<6*ayK@k5~%{yGTr8101FEJn2Lfz*qWTRBO7TWQUDs}^@WDmh34POmCN*v zmQ=U4b8wi0(RlB5R0;W^ut`ZJ4F#Z`EQcg6?!LxJztvtzKV}pAu^`tYBtbQ!F%5Xl zZ%VVOpwZJSk0o^+Kl8;dhSU2FFXo!K3A+tN^ljFT?wWE-mE<2~{c&mMD)@VgeTe~h zs2kBJl+NF|3Frq?OH8_@jU+thyY^>t)H#d_4U(1?ryln7U~u1j8}aS*IV!vLc-1E_ zi*R{gd@Jj4O?2l#syCdr`zeeIQ+_Lq0==UNKEasFj0oq8CMM236-^0pNgqBARSivd zq6j=@$o31?+a}I6S;`!Brhz3y37IIevU zA8PFmWmQdVO7wg{{0&-?r-QXTR!wT!85iMJTNnRbnvCXufjn80JmWtOCs7z}9RxtlT z5Eo7OP?cMkNJ5Q0B?d(qfdRcIOc)G7b;lVJNj}*%^|{CLiFo)kMLeGeu4#K*fYs{S z(o~bo?w1xNt5UmWrHd<-#zc^j^@l7BoGMU7);wpOru}`}xr^-mUNA z&imXww7&Qc(Z*lw9;hx2XrCTZ4QQ7MQm-{bzkQ3@o9|PX`j8~{GW47Em}|yP--sgVqRjJnOu$KNWrM{~x(fDJxFgPclKEhR*S~yOSA(E0(;c;{_#J71@XG9n_Q7j z`0+3EEH-}FjnOVdc}NGQreC1;t>eEBD894cHP*0Lqs*wBUy<8M)_WH5jBu1{VYwboS2ZRwpsRv@fx{4O@8Hj_bPFZ}5P{l_)mP{kLfFryal_0~WLw|tU@ z<@n?gu;R145y0=j8{^cZo-Yv5Vlj+CHyIE&_Crn!cIVKYdH}wfmtT!9hmx%1E2u=} zj8%}H3Fzt9Js|o4VNy~cOgzFS+n3TnoHm@sL2-F989k?IC7?-RVjqq{SSnYy`=LAv zJNX#%(uIcr(jidwgDoNJ()RuCuBgj8t&*R9gKG%o(WaySyKJ)Y01ry0Ms3{;dr{d$ zka}*{6N)$H1x+pH%BEY!&v&u`BWdk$1y{{yh!>J=H4zXvPSqVY+zFPtiJS&p<8G}& zU&Ftk$udK=nUk5nA}F5Zn^X`94E_re ztKv(v2rvMTvMIZYLA%z`|HBs+lNUS3Lvlkv)yeSN6joV`6?#+P#=BwnMeiFtR0kp! zHfc;p_KP#Qv^gj2-=ps zd3q?Ll!!C|j1#<``z1Q}O{V{LXVdk85^5|pt{X1etdYiTJA$te+sv<}j1}J-D6{oj zct@JRsAu2X90{k&K0ae9#cXym2Klv~;E?tl>a2Fxw4za6=(ApH$)MVtaAE)+V!3@f zf8mxwf+LDZHPV?F`~_@*DR3EX6Ie@`nb$zGU6?2Kn+ zq|a6i!<50zP$LK=phKjxho^yFegNigwzkw;MQ2DY(d4*KFsff2H7Zqy#5K>mJD&B0 zuk3tlWUw>1{radN?F;c#L4RJ*YtL6^K0iM#2G@O`jJXjvQ&7zi$O+mi7MvP=(=Yax zS|mL}NdDOF)|-SvhddsPZc89V!C$WXZ%5FhE#Z3>m|Xs`duP~6`uHql^flsMG3j6$ zA7-p{luqh!Oa^}tI@R$xk+V(Lz&cH6ExbJF#bEO_wG^T{IU+ro%A*Uf`B-Wk*OLDp zXLL4i$dZsS0i?mwZ1tzjV(?JPt6rVB4n&>%)Hx%_7bM~9NP2Q5$=ln>kl_!&ZmL(W z@ayZn=<{5zST7p=bY7pAnm%HFf1~aTzojrOEcE8l|B2A0ttS#_Y4q*AEyA)@cozQW$_omh7SQyCX6J8lKb{z)>&{kW;$&Opp!z9_W zP)7J%wDH<2R@$GT^TCkZcJF7&ay#|FV>IEQ8>+<$-0iV)uB0}fOY*woNhJq!6=eo! z0ChM9J$i0D`U&~Xt2N)SE%Y@iGuf?lP6X7g1eEq)XJgH-kHQ=ut0H5vMK3iblF;#R zamlBfQHh39Vwb>kG8yZSp|@C@DyAsMa_CNvFIWT*$tCsSa7dR0hn@`ZZ(%WUvo!5z zzvr??QH&CDjas66_I%uYUHOce)FNUb4 zxc5^adi0}QOeOpGtJVU4Q|!4`UlB$r&U#5zm2AGSLZ!OFcr{xxMyQ>Y(fA+DpP#i< zpsTaVi|&V#-^L8l>Zm~Oi?(8_r<^7xF)nI4%T-!R$#1*838r))cz96$E3aj?Ki^gB zN~VA6k{HPSi|`X{W4+3GC&r=~dlC8x;?rk&it z*S3tf&Mt17byW01>JgtGXJ01wJ)G_*sME^Ol%rf8NP4+!Z}7_&UJkiYSk3gQJcz6P zk{i`Q2=q(m*JAw-Vf4x{W}`QHSnDgivj_ZBw2rMXMAScKTkH`@~A zEB$?=x7a)Eiv9lP_gqRvo%{LwgZU=pB=(P*^p(@@C_=CM{`H$~(ecVb5l|oD#97xD z%=Ba@(>7#S+locm6*&l1LkX-j#pqe5u`JKA28En9y`5dU+y&lk|LMEFsd*g^aQ0@M^hM_nYz3q8K6YPOnqjfH=HI3iQhUDv5o;Z~u6LY8tEdkT6 zw!cgY1ZGn2E41V8+zv;2u-_~1Eqy3oHR{@#UN~oOC$6@`#MlTJ3Xbb;c#CijYy&4c z*69Y7l(N|w2_RPeJJIBh=9n#f6aD3S?F#4-Zy9 z{}U9}pg6X4FXFJfHVG?dZ4plOX&QpgF^$1T^le7RMKs`tuHwSSKgi zdJGN1 zcJ8r@+3?Uw=jJ!nU%_cLo@G{6_QjgUuwpqW)Ezxd-V5>vkz+BRp5oWa+S+T2#TT}# zEo3ugI?+F5)k8!a_MI2oYz4-D_66SGh@s7Jwd1byG4F)x-{W%Q6hjB${2Z4D)04mf zyziPGd)P0p1p6F^UZ;w&^j#k1_E2)xE<^H1ta6Lso|HFTXRPgEP`mtrJM4A2QbxU$_;F4YIzYSi#p+zRZ^Sa(_Xjeki0F_@1l z-$=}nC#n0hXB`%m0W@LfKvi>^28Jjzm#A}YbvTwK+J0cGTg$ex&Ot7aD?Ib#TzqZ6 zhU7t2dWLW76o8GG!SbHM!I=~V|IOUOg+!rJCIUy0yG=DMr@Ul`@U(URciRa0oNDUo z#`wR<*#85oPdack-pb2C2T|m%KL03+7IML-M+>|}qlGk+>c|7k*hVzEaz&n8)X_&X z2dBQmn4OkC(vC$|MN#Y_fhw%TM-vBBynk`WoRMxj=PGGewjk%|X+HMpe1#X*Q;vO0 zRlh8)7MHzRjDO+0H!Xm%dzlRvAR?xHJ5S-*J@V7z0q335K}50fyC5Wh0SXFbPx%#$A8IZ8mlCB%~pdh^gppLDfD zohn&CNnph(CMy9$XvnFM_z%7|YS=+mt(c2?9PBO2==%F$Mi39@I}Cr5aUy!@C$>#K z1?FBZhyTg1ut*Y?9y6+efx(*HPz>%oLmIUSR!=NunA`FaFvYamPv_GU%Ez;9_>hUj zLT1*;_%=Sc@G$gID2D&!MkMi-zcL;4)Xr)S*4m^IGF8~S{@3|UF@m%&iii!7t>cO=TSbhB2B~2xPtIZ8pQ$~}+Fb3uNPwzQzU;jMI zFRNdMA~%}HjSU{VG1Iy395}8tbpI&G%zaF0-Bh|wwH;FMQ@Vg%hfKIei)W^>i2tqP zVzntMA#D+5oJs(&a*I~1Gz`jYr+pz2GSyzNLZHxG2=JkrYGyL_lM?CR+3+{*Kl8@k z&3(kH>{G%!KMTkPNbyvl9n;?*enyz)YC=ZL#^#4=gX;T?37mT=fcr~{*7Px2pnlyB z2+s10VA>rs@F%tuw(gJ5kepL5&EIQ2;H9J?2467RY4FHqo$gPo$;cVkv%d@~v@)kn zNT%dFhkQRY^UCdYUAE~49HYj`Fo{DI(>V*Ne>^G=y%=mf_@-H9vJB9qqAHt3o|;$wd5ty`DeW8WO^tk>Uf4|+$u?`2bJjO9$%7~ zEI_v}YH2&NQI`5*EwUC`>LGsq*<~TUPH)m8d;p{h*;$)Q80-#WtFLTBwMK54rm|pw zHgF3FxY_)QB*Z-R+jJAbXDT&{GCex?7Mb@4pT9!0gj4So$f;&;xEae}ZKyCnUv#}Z z?9SAwae9^vL`1`ldP!f}VW{?#^?(T%E_hXIes7pr8PBfp?qIHtC&9_ewDcYbRpdNQ zR_8?O9dYG1IvTn>kl5*4bDhDDW{wrvnw*9PG|*{x$N#^5gfq>Jq1tki9Z>y)w9Z4v zVzaT{$Y8!m9H+Pg#sUE{C;x5Kkv{LlqlUYJD;K&^2%0*8=a~P{cwSkmMk%6ez)KfI z+<5@d-jfC2#yzED*Oq_nrcZg>iDTi@C|U~=@8~Z=G3R2o5QImHrzaSpiAH=ar;FVow`tgHMAxh2fv$@pul4JJVf4Fpeq)%rVa&pi$tXhrj2?f zdB0gh%eY-fy4Qd$Hwrf3zAIThjmvcU{^POKt`9;DocP|nXUePLSq&-#w&_`C?Odk& zrx6J)XR}EkrF3cMS6WTIvD3EKpi56r89N;Mo#Q3HlPNWW@_c^0*UVF9!D*rWLTKM; zIxfEQ@VwQEPby#U?tYZS)#5KPCWF!j7vC4K^9*;zSR|&f9iXLgAxffy;?}cz`kq|NbvB+eEsx2(q699H} zvLmh_YUmxQo+64hs}&>+^coX9U9}#@e--h|Qi?tpk5Z;kzG5uv<^&je@CZc=Aw?<4 zS1M<6effbt!H=;5iBe-hNTb%I=e7G4LnVswn~EzM)F~)6k!v{jO7q50M`BqboLSQ~ z6X=#Oy?&7yYlMfUiAR%)w9@7)$3w!gv1ZPk&zDVS+a*w<|0PJnu+671iGqI;fQhA8 z%W9VhLQmliLF(*?ZKSB2@ElQZ9Ku1x4iZTTfs|lnO% z!ZMnYpwlCW9VE!K#LGn!4+6gq{3Omg@|-%jFI@*)C|eQC`yTlRokW$ z+7Y4u!~gzz%7{F1Sz{mG{op#;20? z7i%qO@pJl@aW;I0$Mk7@Kcs*pFXJoPC46PJ*D@P3_hW7q1qQ}UpAFl~s=C9Ri~K0P zy@h>_1ikrTBhysF$=XK7M|mF|FD7VS%&lbKQ`F;80Fi4o#gEA`}T=_@}^bi5Y-jOibQQUO@xT#;fEQMcZE%_P{cFE)eA znQDQN8k%8JCN7ghNkD+crFCMo2HJ0K`z;K-8qW5I=U&%Z-Ligcvf!wYUb8e+Km>Q* zO92^AX2hmfbdV6)30d+u7KTaw##zF*2R~G&3_&Ma1Bxal=S_Z@3`(FlXEA2jL*T2} z75&O$NU+0l-hX`jEs=PWOL3#}g!=E0wqOkT($yp<>}O(nUD#fS5M0m=V4w*ao1EQ{ zS*1>01~$g@s}s>7A#i?kpF=umXx->UkTFa`{I=YbeQ_n?ym@l=1D%4elMx_4zDV&ue3=XU0vN z5E@B*IK!90|Er7Whc?C^0K0J}YOJOr7v6c>JI_Rs3mHrlua-<4KzH@0LTCi_U8UM@kEyQ^~HfI(&3R# zO(mrU%ztDt;x@MV)7cjX( zVsP=WyroydhXBZ1p2e|9Sc%@`CknfFnA_vyo$;bVHH0IhxWG;6+qb9F6D!4Z%u(3% zft3IO&9e}6fTbBlx>PC2iXelJ#-m60hzxT8I}x30Bzr5RCvNvBzF-@Iv0gBN3#)Xr za0WmypC6j_?5)1fF=Lq$D zZeSpDM6-wr**WU=cvb?^m(*CuXsEJ#@1Y!5dT%CXkp4(Je0-_VlKN4|O?aG%MoH!d z8z?#=HF+?A-*VOq4*NbyWxq{=U+~2jV%|@P!|uka-0!!)op6wT2g_HPT>M_jc=c%OF8X=n1!EePc|Kh@zh4h0W3i)N ztJjx4)igzta=rDn?^iV7aK0Jq*}zZh`b8S(pzlO@&No8gqMm-xG4(;{hrqJE%^QKQ zB@^v<#cxjWPWe`Cx&)<)f~_l;bZxJS%m z%cs?5>UVkjd!#9IdzZG;xd3buHZau1X5D>te6dd$PSBli+Z4gHzHgf@lL2A8j-wAe zsro_~#yV3r9-+7{G|`W-J1hqrCe0AR+##n3b=#xp73Ul*Drk;$hj)YtC>wq2W&2QN-FhWj$|dm@ z*aZJHV^L}OS)uoza5e8d<(2x5Kf`e-_EG-~3t&r_;@RTi!vF6gSBWY=Fg*AIn@#DO zULQsJ`aVB(9nEP_>llHch6-9X7xsOZjI9$N=pg~-3@+!#`7Z$k5SfV)I$z-ElXUV$CnFZOHe5^X z0w$SmHn=6Z`E)J8=#Hl?>Ut!2NzE^H@@A4Ak>17P@87>gk8-h7KqAXGLn&OevnSSp zIHlMs;;{h&R|eOTuwtraNsPlBR?MCbc|e3$?xX&unMZgHR6NN+CyOxkW2*0i;kCf; z!@saiU$|ynI50bz77Bkk*P=lZ)cMBlB6mCnOCoXIG|2mCi>15;v|peS5MU+kE<#^| zHvo!&VJ!l|Tc{={DY@@Fgf7VU;v`5&ehp~;+X^`B*xC0kQ;FfitiS^l)1{JQV`G{2 z*Y|r8jfkkI{3&Sc0is<+#Mcdp^}+UA4&7p_f5kwmF-IJf&;uz~bQu2Y}J z^LgT5Yqqm`yY#$F6NoTd2QYd(Gk{;o;r8Lx_tZB5pbD_+*mS!$Ny)oBQPdF57|Hy{ zI`7xN{V&2J=rp^T&vE-OSYfGK=C^ckinh;>)_8VmcWen%GRxE(y>9g2ucyB7F15hD z)RZ?^lpSy8{hknx5`vV+e#|)p$D4KD$niV$&z2SY?8j3c-R=$%k&6K$jG7?}9E23- zLZzw7r$^7R;QkJvpJ}~$^^ztSxFe&aGs*SqXt8^+8kqxErK=iOM=%H z%U%k+K`umBNi$myGr{{PA4-={mzUCWU$xqg|O>RfSp(EbX*Dv+a@Zg`mYkW2GYO+aU zRWteU;qG92a3%$ywy+bxGkwU_p8AV*=IETFJo5GGgiOHJ`J2>^mJ%UkPoQ`n8NIwk zLJiqzZO=*dbAVmm&!4e}`U18T-u1h(dtT za()w^Wqs)pA()K2#bYe9eE{XyGi5z1ij~^)yKz)^EV==SyieC|TsY_FDYL7e+*)r` z0A^h@bjUgiJv!6SiW~g|O;2!S^z>m zl^lVbi1MBYg2~D!-|yP%Qp>0(;+`FYgm55j4s-8l==BN1@Land{q+TCzzi$kpNRY9 z9;NYExA=0Kzd)p?B`osce?#-%1jFT@RO#)-dq)|Er>^Nrzk%J60|b+5Ir-p^%r?7d z{~5q7QZcVmQQ&p#kaqI3A0^PM%8>Dxt@ZM8XXUya|49zd%-rNLhLyl84BLPazreio z8UUP9x=xQ0YdNaRC%+g>;QAhjjr|l2;T~FcQei|#MAVt%FOWl70YMjSZsyM@muEOm z`ulJRbSEa|_{oNij#q~79_B}APXnVd%#uPc_D9oi93)ySyF2+Peg<=gzyHHS3q~99 zuzaOGKt2|V24^SZaWN8o57T7nbyoP6gEl`IzJ3FEl>YHA zIdtI>2O)r=fPCh(@0!lrZ?n4B=k*Oz!p$cn6emZOMhVh${!)!E3=%*|eM}ICLL_EX z6~95MJ?sU3AO;*)T2~4MiHLOGtm^}EywGOR=-L`@A4d7FGJTPfk5!_mEtLw#*X`?+ zGTezF09Xt{8L0d-TaHaJC?X{E?_j4R{Ac!ev+~8(SX@vQ$H!uj!|eJkEWp1%nWI>; zEM1fsq7)R(SN*{C-ss_Xfd;B#k|-H)FT8=TpnS1Nxd}M}qB5VBMv~yLsOg<67SEkT z-^Qh`hhX>Z;JKw97Xmk!+Y^#Jp}LsCDE+8I_<+6zWh$wj5KkrKdYj*bE>r$j$IP!_ zDm=Ou2`TL+AS&k-Lg1lsIdbRhzqGf>`|OU7<6~Pq(sxB_#D$Qm$u=#Q*&w+>3dCzt zxkVX@@KP0}6i0p@@x$ZutX8=R30~vO9G;*vy;Og7BACq!P4rG_6men5xH5W?EHVP3 zwJbavl7nsTC+%!E-|+-wCGljwCJz^6>=kHMwLbuX372w!Rr{~WXFw3iqL43>D5~?W zyjEIGjYjfj8DbLAbfm*=Tm5yl)s&@&=x{DfGGL1?=cG+f0RWx88ft?FI|a{toaxD=ynY&K?f=~PKdgm%8we9*xDRC#pKqVT+&-AJOf z>v?_OwQ3Ft=89xD>g6|DWQZO)MUgAP z+OF|ON25-L{cyg-no!jfCi?23Y}S5J(Iw>-c0vQ@;R3*a8)0t{KUtyNR#b6z|3v?4X7i{s28cVGJzLffy}4!%z+Tay=dK^HT^ID zyycRR+xU=Wqm?EaIL$!sE7_4N%m^4bGG7FLCHW5Gk~>MU?fIV#tpriVfr*nu8lQXf z@JK5^MIgK?5G>suD+?IJT08V!cGQOcM;w)U>KyMK^@Qg5#_|+}fb3$RtqXu-tMXq}T9_p|b=(g5V}>m~3zN*IyQ|I3HPcA7&Szq1 z2G@^|{udX$xIDJU?<0VfLP?IFP^Cd@Li_!S}J*!j}pDL2dZKxl4<^ znDcw2U`qb{th)tIm>~A z6-Wsi6s>;9J6|c6d+X=^TbxJ(9nUOrwpcT+M3cGS4+NRz>NyLME{Egs?`t|q!#1n_dqbKgt1lr`P z(&wt<33RBBfMf&UWnjyM3_F*I1OE0V_`9YC@58^(LxH^-5SRR_m3TJVAUH|My1)Jv z?nJnE??Ii=ry{dL)+hd@5nL^NYQplN1WZ_QLxxyXj2Ju)mhY_W6G6knW8&0*k_^bq zN9VDu-pW-cyudmBUX_fBR^rGt_kDDwUoWQ4>exnWff_Sge6P89R;C(}6~QsqIG(yQ zk4V#C2IOSqOEI% zdPT!D_w|!LKT&Q|nzqvAB5q>x>7FupM)7#0fX*K(UHI~acE*7JT{x}pv5h6gp zch^k2l$+f4{n;W1M%+d=C6S zpGx1y*=gchTGrJ{4$HSj(jz)Ap2>GUfBsyp@cv->yBC-1)v{2N+aWO#%e!C#m($5? z5-iT8u0Ak@m!00J3z*;xRiazSF4Mx4u)1oU9lZ=Y+TEJbpuTuE+SariQ`hQr138*R zg!s$TlV9dGP}b@;FVL31xxI~-7*4r46?*uuRu*2=@f`QKw|mk**EQ}`o>TEUW9shq z=C27Wgfn1V_LjXo_FlbO0Dm6)*5h>J3yCp|TtoNx$VNJCmg^Cj=K3{Su`F~Uli3AS z+Y%QNqC9N)g=DvvZwDDna!n=L^H&3&hoS#yB&(3npwUV`Gw%&zSaEuJq(iI}SJ+7z zP*pNu4RxWWkk(rwMF@m)ad*LQu~oL@Vsj>zdxwE!4M~)m_WZ@&5Zb$-(uGD32}0IT zex*qmNb>%H%ZziTLO)?wh}4DOdMU1k-IM|$aE?|!G&@UJtxl0B?MSkNl>85qI#EHO zvV8J%R`Ph-%Z(K0oi5~Z(Cyz#UFHRl9aR|io*aPqZ`(jft(;~1vqUJ-B=N}qs=?y| z&i*{n$=$3h@;UUu+_Wd`em8y*6L0f-9OAYhe1m1WP!3pRs&;#J+TDwrEcr}1{a2QG zbG-#tMx>~{0Xn1Bx%-V@kp=dl#+Sb6o93sLniMv~HsA9tWj`NA={wgWw;&s)<(lRU z3JSaYMNt2hzW3fZi^ipeYM%_EOec>dqGdS>+pQ88WAU0qFb4UEg7#~pav1M)7*Ik& z+`mcVaedm5C>8o-W!H%cK9~RX`BHFmc3~kRLbtSTttLnuGlH*F+7UvU#BTkL@df8N z86DJU=y%bAgXRxKhELB!^zx)K}=oi!z{#FJrirAr+9jSl~Xx3fL12g)phP zz1d@LFDbk}d!MJjJbW-(S8`TalPWKN^>A4QJh>geU4W$52|{UU_X4CCOa{OgE7ApS zNcWKT9FuGgp!_r6#GqB74Z99WeU0ZCB7wIf`I{B$J12K~mC_Y5j|85Q)s|i-2T<{R zk!xyF2I!sZ9~ta}=?zw%mM_h?lp{&WHfr~m{5s;mh zUtL^q3mfKD1aa<7GaQT^-k+=BB2<*^(e~#nuPN?AA7( z?~|3}be|%ipZK!w>IQ<)$6GkV#uV+IR1sBKyWRs@t0@1KsjGh0n0RMr)@>C{FZjXan4-ILSnJyQM3=eu~Go5RwWaWC) zO$$wgP?x-G`e`f25J_A#ZrDCy*nH(&FIwHO76G$kwd0LD7B}uw=1=COPaybrmgdK{ zncK)EKs$b8FT!b16%CT4$XER4v5TWwW242-+vayptdb>=qnasR5DBaeyy#__5@GT5 zQ>5oos--Tg2+Ch=H$zlx!jd_H>S}%q{_-85Hl0VxRsSt~B#G$7Pukgahy=sAxct|4 z%h+jGA2J)fT?8=Dtv0Q83zdV}Htb=V+&hzraP^Blz+uGgxSqz&i38oES-Jv9B1iua z+yUShGBDGdR3o88%+^=*ZNec;QY4#Coqj4%tW`6^m z*=L6f?Q*k8rWE`HU{f3wjGY2TXVy(-&y+>a=l|X;^3xpAk_m?uA`bqgYvg)!qi_`s ziwgdOpezKM%|jYjK!Y2_K-Z4@j+ZVjZe7M2kQX9@aYu~N;%R=7GqwVvLt_GnAoV%r zhfi*QdSHsFGOq#80g|`EGNB8Dc1pdypfy;_ebaK}tFFgMNc@5^B7GianI31@<(U(N z)cJ9?%+dU-Y1aP!vFtt0%RInxL4m!uclb*x;KKNs1pc;o%x0;80`1f(r|f4`raYr$7<``>t7aoZKpc5*t7Uz!gZ$T;ukr56#P@ujpx!W%@@7~8Z7YWH`r9r zG;XsszUwM~HjC#>?yT)pWg4XEHOp6ouBNMnC)ZYNn)*01K;!Bk+rcFlh27I_He2M78ae5XP740?lC~evP=V!lA7jIj7LoTXIiC2lBwU+ z;fk)n;iotIgyp%Y#-TQ(QJC^3w8p3rgy}uwP#UQ6zR}+_(pbPj40zh7H3OJm_R~K} zb4cLxbU&we_>zLEP{RqB(~Vih0E`FWkD1nKy#h)sJC6cFa)}G zk4vp(zG)nX@Qjn{3RGvJOUrnxSq8`r+Rb(4EBpKUlK<|EDQt#4#u5Ngqb;3=?bt@4 z>)s7I1{ySgzTq$HydLQ;gpk?~+eun^;!?>7!Qe_sJ~6z|7d{!bhQ)u)`SOipgT`U9 zCL!Z2KRkJ@?GylD1$H_WPqM}@h9%%`*v|;6l6815#EgbWaOU}Z@i)656#{z z++tknh{G&?q6agLgi4YSmz%N2dtR?Ay<~5x%AR>xz)9%#m%&MzSE$BU)Ivi~#1nn9 zpDfLu=`yICi8(RkR;d9G*CD@${akX%hw_ds5Pj%-`*}PQvoryZSNzFgG(u(xxv%{J zV53Fi$GVQX8_50wL9FTTJT#Oeii_rUvRTP;j950H+WtEG0qZUhg)0lB ze!5knMM1%6K=X_!cI0Y3rN$Tw_%~HPIy)&0jK|tg!Sgv)-&_V=<#pC@V;@ZhMUTa~ zVT}F(TD8@ZEF~&JkU(7I=V--fYtw!;Sc_MKM2HqbU@Q$hflZhHV6F_)n;eF<()tXF zPUVafohr0+@sM|*8Te_jX~Bp}lqR)p;Dl@ST*j7CBZ`JIQm221`}IRb7ZYyaHw333 zNCrz7@U}zfjx%Kn9{9X(Kw?^KHyHoVvI1cAgbCUMp1sW*fMhDKkE=REJ?>Y5sW+>7 zDQPEIKMPgL{~d;T;wqL5|5u|Es^ z1;o2OQ-IvgpOi*X2bdye2*`ldqmdhX29%Cvh6j`AL+Yb(J+3n7@cc4J1nIz_XDvAq zuD2N^0%^a6teXHWB4d;?6*49KTdYOO&;@MUIrtii>lE~G&gK`&7`X#7vqU`5NR{V z8$%x9+DPMRr{MK7yW+nA<8$GXNH_SWuNG@fQKPd3Ll`KrfiCRLU^JO9y%|RkA859< zC?27Ylh+bCT#A;Ni(`bXPaeF6(^YJE{Yta_4K@ZEM*dHSRrMd`zdiMdsg9B?XXFAP zgCN@?U_hPjI$G(dDIEZ!ptpUfFr;SH&lenT*CwRv#41`XKsqPY$GeDtRv@}b**~~*V-tIQq1nEyyiaL1=OgR$z>C^1m$*>YrO%H_Iin-CVDrk$xLqZ5&2kZ3FBy=#D3hpg=lBXaaBV=XBuk1H18-{y8DF~EN_H|x-WSE z5)eT}wOK7g&A24TQ0xPuKYCQe91aSBEv#X{jPe)76;O^Yzc;QUL0pC~CXZ!*M^^-+ zOeJSETde_L(aUeXj>#=bQsm-}po|GR?eLR;cIAY-%lR(|Fe0;B+WVITuh7!JswBlP z{sD|x;tERH0y}gJ8b{1rl_22i`|76A=D^1LqH4eg+F*1|vZI#RwUswit#$u7} zjkS8$ej4T<3+%ZGz{2c1y0PZjd+d*Wa6_AV!KN2}=W#fcggX(PU1Nn_sF)W2Lkmhk zqsDzPUd5h;r;CR78y`i^{MaXq?7Q$rB)EQN4lJ$k@# zI$V}(_wAZ&nb!~tF32|f-$^Ec(eBIv4>hpczk5LKw1P95epB6r4&+GtTP|>b>pk_3 zR?an6-z=H_S#0q^r^wBxh6qm8d%te-eQx8zgonG%l<8jisYkyz0~3rkB& zzjNQ!k<4q6UHzZ$YrGIg;Q|)s$wtaG{5P7C!tCqsJd^BGfs==IPKVPn?B zbFy+!;7L%o>~2iF6G>EZ=q1J8O|+ByACL38C>Bvi_;%zVf@jg4#ZzE)lqCjaz24PN z;yt<=D53>q!hqB>vQD!+9(_eRgO$LFaqs~n=5QlR-6 zSqOvi!26H@A8z~5x5vwo$Q1=@_4KlKPG< z+<_Vd0?sd3#!$nPrX@wrl{rL7ohAZ~;73I~BR?xe5w5D&e z>E`r4SB+^aE2}1dSoP6G!e$QJw*Af=pvS?9(l7n1nc9chq3s#=)SgyJRLtORH}^i< z`$3&AtKVUL{WSka`B}4yXs*XktH2ntEV?l4nfl;&vS`byY2wl!0nW^G(%wdst5u=dtV5>t*{q z$!0A?LTkr_OXC>2L$TtN`V1hK2QzUe{tpU4k(@MP?O<*MDxU|1tSk~M!=U4R0Dk`U zm~2}SC*Jzm#tX}_zf?V3Trk;#(Vw}ZP2{^>sKA(0#(R$K$g|Db793^axXHkdPUM0y z^*5mFVt-3_2<0n95nRr!aB30_L_G_37>q0a&gFZz3h{K zQt|U-k>Qubv()1YqktF}vFHAr=^7LURUa>JNb1dW`x8qLD%L~*T@2v{si@KEqvM@F zr#;C7oZ+<3W!%aiy5@BC^L*NDK1abR4dgrs-d~21L12imr!i!Ub5jd(n=KEU6N{Vf zF4+UzV7m$^=AytNS+IB}of)5zk6aqfxGf*}fQkC(&jWw%<((&3dfMq4BDy0KF>vbKV7fIW*+3NeUV4>pUB??_To$nUBzq+SxI05>o=$|D@tHWi`*`qW(85``DLq0MRU_P*| zcwYPF6e6ApExzV{p78tBcx7{Gh=i1w*Rub|+kAM|o+g55MhDc{+Mh+}0MG7sowL2o zFI3w8;n1F-fpU@J=X)1E`iFQJ-q`eMv#7dW3ZX~Qc1Ku_76Ahi2iyne8K9|9aHgtu zx)O1a)nSxuOc0DJuQM?GR#Ej*xr$5U-3W**=ia*zB!%!*v5(=&YJ{Mpv_|evt1dae z6$JeZ{i6h_4q@?Tw9bhICSnOtDjT`o+M?%>KE3;Ty&d#cTnhma)1p}7vLfuON9>=w z)4;V0*$?_`{$-c;sS&HPX(-@ib(e)mR5U+SfrsKO3HNDvjw~%Y9wnfsLa$r@X(pa~ z@c9RI>)N-Rtz_?$xz9JS=?Rue1J=Ekv#0xNx21Xv20&@* z61D99SU0s@98>LfuS#?lBT@g8pCPqw)pJhFgsond_cXu=O>tDxQ|NrUlYIJ3J$ekq zBbevj&~w}E*1aVg+<19p-iM21vI2LI!Fl__!(E+Vv~QIk(5U8A%LauA5_MK z?JMaS83tiUUN$eDdgokxA{zzUN=Mi^>_dPcj4wIGUrjvgsZsQ4`J70b79K^`eY(-1 ze3e#XF&Gpp!Ek?doFe$TThCMvwmS6q+@K=tW_7aD{@3GnPUGz%TVQA#ai(ped4}gq zlCxCKW-ae%`sJbdDok|W-zO$gC_&M-+dDnS`x9ni%^HdGZ=#g_Bejb#z$%oSEW5Df zG2gB4g5jfKV{Qw=$!3HpGN$QMoHmc9^CnrJw3hkeEq%N==We{JU&1}#=Bf{I@!a|T zZODAHuOuKPQG14=($0C4&KbHaB+pS(1SwmY02*+hoM-Pw{eQ0MK* zLRm&K%3;Kzfpc}~)A?(!^~*W)v6fr$px#i&n5h5(1GkV`huuXnbnfe06zM z3<;oT7(9O~%G&RK@=9TFu-g3*rSW;P-;4~5mvYGYsv%i&y3M9bkRt{CQ9P?!7;|KG)vgVgn?K&Q#mV1+DxL zbFT1WN_|I{L`clN-45PaQpRn~KI9{9*q>$@ZNJPI%I4E~DUBluTl&cJHh;5h%BT$A zc#JE4;j}xzDE`JPB9(}Lc1qYftJn-fslqIXIeOk|=Juj?&uc)Rl1@%u@Kee|s6VDv9B*egCchs=)KMkj;>h5iT$9~WN}`sz zSTyNj(kLp2k8}=q`0$-|_-Fq>{}=00$*-l87*Eo2-eR|0t;n+?tx|m@DP#!@{n~a> zHq8WuqyGUPnT!!%y<=ajOZnw$yUaK(?4Zn#3Z$Sx1)=c?1-r?KAqAsY*vR_m<^gy^ zKl>auC+UK+j6~Y|*<;c3?TKhxoWvJW>{M=(g%-%+6{uK4QO3#*f&^H5N~IwCzVm%gwDtPE8&6>$^<8(Xny; zY00-Rbfv0-G_RAQ^Flt`OYWUYPBGtEYa9ahz|-aznnwto-WC_RR6gt|kWD?DN={Ny zIfPixv4=KK8$U=97lCgy1Vt}#K~1tRn~?>TsO`>A3gRD znahi%Knb_VFSIRwr`#8t+#5q?H>Mh*#O!sn*Ffeu`@zpAEC<@Y<>4Ef(kY$WA$}!k z6s9zk*0Pp^{VB)(>m??9PX;I$(tDa}{En1UPQzim@6ijZA0tnjv9Sj}7=Bo6>WcW( zcs%;33baNYjLOw|;U3l&rq`q{d6J8?HkR?^+jl!6B;Rlo{T))>iH?A^UYCpW=x^U{ znb4F>X_#RyPnauC?Llzhr7#mQmrXq~W?7txSf0{GU4+B5&buMyivFo3|D7pfV23IRUU@7+>O`)M5$!vi^T5y#^z@AVnd*f_ zm64Xm^<^##&pm89=BG$tH*F-+OUqIjlVN@pTG%+RRbvu)sAwKxtoFVd6|aCVMk4Wp zacFY{nq*2xy;0_(U@OcL(?E#sAIkh0HU}j(e=q1mfec@{HcDOA_i~3h1-sCnarFAk za=7 zlm^LjjT(i@q~Sw{xhMOLTS&q`1MopFv1cqUfEfbQs~*>uCH%Hu6dc-*E~`POKbD>p zspE^0UPozn*4sOE`C-d`6?M!gI^zn9!1}aN)1Ej-?WrM&?%sJ6h<*JzzADJCNBE;; zZGi^-hs{CegV4mpn@ftqGnM{v&x<_|i|wJ&n~}h)34uinLweL{RI_g(>0y`bgVH(mJ3 z-s@nECBx0aotk*lGx(z1=*plz{&o2Wt(vz^6)k4#2S26*vQrrbQ+4`VY6r%A#82of z9*e)2qVyi_XfnRtar`m%N_{ZHZO0eQVw1XI$tkP$`GWr3YDV48y{(@BHMLi(HojZd z4|dhii02A>7!kZm!U6|Aa$_lQ8PXle;jQ z525TAEiw|o>A8;iWVMkmf)3d$T=I>^JPi|= zmN^sR4edlkjWPrspAZeDiJut&qc=^D8G&i&#$j5hp64Yxg|Qms8jC| zj@b3d#Ck6qS1Qo7aR*_fT)bJ^;&Q!#u=;tQ8b45q4kyz~A8%7NAa(vq%6k~vmY9>)_%wbTU>$-4W__`@An^YFQdM}^zF>y*vz$-|YQ(;;}qeP>%7Q%2s!NSKFX z4W?^yUf*W!Y{V@BYQA-T-Fnd<;;~30qjAejSURsK$fmRY{@~-dv48RMdeHN!Qau^D zOy_VU+np~XoF&w4&c{Svmtn+~Uj>&gIdpKx+#+Of!G;2A$Vd zSpydO<26^XTzQ*kOXj;ahJC&I%~v@?X~hzFX<>U@8hG~bD8Y|$e9N0Gf-j9BeCA!f zsy|kM4Dn2OV2S1r#6H+e_rix4DM#jVgFN!oW#))>QIbD%HeOI1W5jpTefzpoF{!1u z9O;i5PVLXDUH@#U93G(_RjNQU{{ErHJxz`^Jvx4N= zmqUU63W(vEzKJPp-CC3IDBuw`M%YBDw8g}VC#0Vi9l3Wn`~)#*(1r^@wA>}>6X;g6 zGG_~vEcJ3O@Jv2Ob*jGAd`gXlRV058jaC?bnCVnfbA(dhG1-@j3b<#HRA=JXMMDOM zU8YlpnZ$;pTvV=Y%OCcfK0MI9>IIhfQoAWJHMMX`_k;)h6ZvGXgnx&on=CWl^yG(u zqwN?)x~+%+DTBlND$w{F+eco@BXj)CNPX}X?~$qeREVx?ndy(nZ?NG;Rtm9!<1=sz z72k(n&A4uSx<$AO;oJ&wov3$X%K|&kDl)M=nwQL5AWLt0B91wh;(QnAe$ccm5(R5Q z2NDG4qvQo2vA;gtQb;P5bq6yV<@!mncgd#IglE3nLpNid-F!b8(9bZnq?^LBE8Y)j zK5xObE*T~4mOtq^2+*fLP)~o8{os~S(9;pQJ!}Z3G2ll!$IB=Q;XBC3S7*kDQU&aX zp}{NkBUo;^k*+OrOc|C<*`L2OX>SLd8$!;{0V~51h%G#%YXhijwe0UwL~7^OlRX$e zT|JUG();jTk!JD~)%3&u}M zlK|F_=8DUKzXuU5xb_>p2WmAR5_ZapHCBSR+xE+0hl$gJo3pp9@0sY&+ZmBW1n&br ze;G(vqtCV?f*Y(;7)srnU#>0*^qkqKKbY1u|y6;T718Ya0)e)j>hz;E_?`9d=>sO}+6J zTYSt*=`y)xubB$A2M}1Is9LJeSIyR0w+9`OybnDqdtwBFgPQ`bSt`^s=$Cx&WWq>O z8%d>#Bjxv-^soq&t!=YIHv(<>gONw%fVWf zSI0lT)cSlLmE4hWR?>P|YrCK|_`M^3E)5mQy^20CcY%fk{WJ0gwm*e4UIq3E_uj4T z7M|*H-;t%I<-k#E+$59_9$vrR`at5eN{Up_oX9m7!V@YC?%+A5K%YIT}#V}P3}16V$l2Jn5(^?<4hdV>}x?L`$mTj6p~A1bz+9ZQEiR` zl80=zJGL+wn-Z1j#G|#Nr=UP+_~6f;>y8oM-h8>U6$q^GzUjT~%>b=p9p>AVogar| zPw=wY-1(gCYdZ#qN#3!Gy6+Pd*2rGr+Lh-_3(D6}LHr4bi0r~=y3{2f&GrtAQoyUA zNX+{k%VKh_xnsc9F$@MIP!n-Hp+Pkx=Y4od)IomT0HJ zQri(>(QXtnfdoO$C|k_@NTW=?*i$j@iJ&-NdY%QGh^9Ra+4;FAYB6$s!D?gLGTq|# z+NcX7AQ|`zr)mwOcYd$GZa2V5p>L3McD96E=&Ly$Rj+TCghJSOB`;5QSz5hM;>prU z$@+V%e1ER9dmYtH)s@0LIw&@fZoYRmJLou~!X7rJc65g$q7x4o?|ikN3x+OSrR}?% zXl8wB?sWam3;&JzDCCHZ2ndFJ6%`XY5v~Ts8y4Q*#pPbNQ2QrEc`TN_!jWbX4i&&U zLXcXv8qWB1>v#1a@@7ARy2Qq|PUY2S#g)-8``NIKEQ7C(&KzC-->HWhTEuIKNwfl* zmys8+4-ohA<)_}Re;DMP^pS$uVnsZ?)vZyp8orVHT5eo;Lz(W@|G1-@2<-A1Nuzfo zY9o7!UHM=qrIVEJy6QO!jg2EPO=SWmN)fGngs{|g^EpmArHC1S;G)x6Cma;K)ah1a zzwx%Vl+c%_vb_;IyeQJg*kWuYErZ|ojWo{y8yS9#)6*c-D}Bjy9-(me=d(6Z%~Cg4 ziqdu&r59`1{G+|0OtqG5%Jhghrz>`2&>DeJGG5tj>C(8ksVL1Qe@WxG=G@An+Iefr z=kk5jKBe_^6xi3sMqWFV>9_0bgwfKUQpSB~U#lvYK6N`1;t)iC$6ZrlhyP;D*Tk2S z=R{?AJ4838ipZRI|IWyha9uq>Xh2P?O<1Ywwv}af>&^U@J3`>>{AD_vX7^ZCAYoMey^=sa7@;aHdH-T8 z7MXGAD>c7wT8iLDj7M~h>x=%V7-EMf5lpJB3G&6nuz(`h=nhn>a%!*ddi-s&TTB7b zqRS)|*xv-7x+NnSRaSUvTpsK=4ixfPQ9a4-oT`VA8TxE7xJ>q}Ewtw(a850GMvrs| zSaGcRD13@2{OH%PL@Qr+kc&OXP2aui4Hom8MCc1q>~r6dis7Yy4AP2J&|bV@1-;PFA;-`YV^ucT5_M zNNTLjH`vZdV?IXyxl=-m4!^-?9l&sQeU%Wu)BYM0;$X^Upxh794b(E77W{JQzkNZh zt#ZP-m3PC*qlu8)VB`}sk}cZ-r-yatxh#d*{gxu7y@w<4LCPH)M*$c+OW3tNAtKv* zws3~I`f;V>)KmXri#x|&T5#~xVmke3>t#ka6MfQXipPh1_p%I^zHaimTJoQ`e7*9q z&AK%#AN4dMTJWL)S7ni{QG|OYsqt^#c z*`n37cW-P${Z;Lk2b0eD_^;yMK~`Txb~A12u2s+R(|wy-3ebs5k;5cY8o$HHrsx{y>%01sAI z7dU7jcK&|aIKLIuIY^lEaUF9C~e&f?Cbod1k zcU$*d#owDFQxdBS z-FgZ?)-4J?lu;KeTX()jjOB5?xt((a(!y9{5+U|eZ%4&Sq>0Fitrhv4`tm_Z`$3H` zzn)w3xpU5LU2w_L#WEdX=b;A-H){pX1(;wj!gjpQ_GF+yP~}V2N1skFGLdhOhvg7* zLSYz98*NvJmdveJwKKw8KDBIwiyGORA6@0Qre7x~c7n+_|Evn^Dl(M;msqDq z)5X>wd3|3`EjW*F&eU!CfW)%@`YOziJbDGrX*i%(qE2oUv6$PAf4W@rDab7Z^wbw5rQS zfAttvD*3!d=m4&|7i+E|pI+~x9WUT;vxD8oc#W`}8b#r4C}h7*kT7^GWWgo|U{Qv$n8>gb+`WB~`mxe{N)AXgaW8ePI@| zn3~G_P+<7DqV?SjMWf?U-7<-{`vIfJlFO?XThp2ktt`Cqus=Rn=%3~hMUnwE?R+P< z9O`!+*nb!SBVpJPaCiWW+sS#0*>Q$8M>c%`CTuyGp`(#HcwS}Ph$PkBC(hN&0!!Z{ zZ&a80*|qO?<$Ia0yO8bzXVJ1~PY%%Sjr<=TAP?;`l-YQqQRIn)%KI=t~A2~Wk6V{jqiiE6zFC>(xn(vIH zzGh5l)3QA&OZ2(Dsgy7d+tw!7t#a|hD|+_Z7(fuZb*0B8OSdtrNN+um_sigaz*Pb> zIOV@d3O%KRQF50>Yt6I@^)J1>PZ#zxc1a1}iZ!S`o9vp7$1X1qLp+u_SgX`v?7E-Ox&hU%(5gev@WZWjH z1NtL!)RGxv$OkuZyWEIasB(D*(Q6($*HGJZhEN56=f0B-B&_*}3k?$fb{X^fL~8um zQ5Kv_jqmXdd=7kn>s{l+*zGYRG<57@(2sex3LB-i0J!`tm6q>HTT_MV>7XAc=v)^B z(8>yC9T$~`x9iuqbhY2$toDNW98O@*9*W`Qv7;-c5D*dCRZ>!=dK_Ytecx%ga*A*dB6~E5;s6 zd9-l6_P6>CYPT1g_|={lE;(=gWjd!TW7D!8=GKEW|pPf0|;4aT`9B56X5_t4(`!4L@P(B=%JNJ?1ak{IgiF3q{T zq`0BxvrwlNQ&2vDg1LDi^EW+vXTh#f5+TRPuKT1lR)f_`{>MI8K&Eh!Q|!zT^GmKF zCOp5xp~EGKJdc6*){k{w%UPAOM_4pSa3w7vULqw|H!L~tb%~2Y3E{<1vL&7Ro{zUZ zH^^E57IyrtO`dwfqNai~V{1^~^; zkC2kkA#vhY)H(1dBP_j@G$BaE#8N;p@Hrm&-FgFfFHum-<)HuoePLqxHHoO}48 zT;5BujL4|t1H9A{S!?~bzRTA+L<82wzBJ4`#6>Y?OMw5Fo#VD`G7MH!AI0&g`O)}p zW4$bT zejFPL3O4T%N2w;+Z{AC(Cfz&?XuPc#EhrK?nb~NJ?Vw*483-TAe#VrOyPM)+!IqCmc(+}knmfb5LX zCW70fubTwMMPl$y<#l+Yxl0+IVM;48(PH=#b2~~9u-dgbdU|4f(8r)4=&MZj!Yn?eg9;fvZYx^R>sl$M zt^C}t90o-mkZP&Mh@ymriiq2tmQ zS-P~pYbYJXe-o~_F?I?(i&u0AHjan&7O? z8@D3_lM2i|LBXdxSNFUOn*nos>QyOB1@j!zA2l=Xxj9|wRUNB8t0iYF?hq<1OU zF9ao|gDKbu2!TTptyshs%EBsD1_Jwk)U?@+OQ_R>YTcSR-DqQ#kl@VSZ8^K1mzWL~;*^Pg(&RGJx7g zSrT52vz2o9LE8G%>-C8DJ~A(5JZ@MAiPh7)>}GwUyB3vpYUcP@*N`FZNCFfEDPQR* z@-Osf4EZZ?PeMu3_Vklmp5;wD>5r9`U2R{ayw@1HyHPdf{amGBvwS zEu{r{-pS(UtXM6CIiMr2ofRT%8JAhw30KMpr5q*YJ##`JufVM}vAH4WRi?SmPL-MO z(7=Ee4;F3DrRwV!yO*Xbw3w0R7(wwtenA_(#`a(BX4p?YLH7t?INzYgee5RBeAeG< z4;WeRB3IeWg{3btOEkUuAq)iMzI3*$fWbq&yy+jP{c3+m&2U&e zHO-W#o@=vD9TAY*qD`{5UCb{&*;-J(Fj;aMHoiU{^?u6FsAiG_oMU^-Fyk}~^$DDs zOm+Rf-5OERBmTtlebfUhC36Fl+lj;iGUgMe+ShdkqdvqiJ(NS4FVX?h0_eS1Dlau? z9&{rBng1`P*eE^b2V zw?r~34jK*FC)=N&iqtz6p7E!rr;B8p`389qms2)U<;LVay~3ZW6gRAK!Vamzo;%FI zalDA{KzKv9&q7lj_JM>n&8E4`pmxljMyY?r|HV%Io+)bb?JVCdP;Bq7>}=)Te_JaC z;U$emvt0XL_G4qvqQv%5N(-6Y(eK$|$tn(%Dnc`ox{BH2y_qb0ge;d8V> zPQR&iTBN|4Ag?&PaK#zbEFD@~!X1HiDoDYr;FwQ94vH+DPV@Niusq)K6sC80h|(}| zpM=13AYVOG1Yzr)sUhBNrx-dS3C ze-JWEd+Mb@-F-x=)CZIBX-sJgLLL}dX#O^Q#<+u_FqB<0(4nK=;ZJ8`C6;R%fQjH7 zATDn`JNL}8uI*Ip&2W-89eCSW4unmM8|-Prg%WEb1)@e8W+r`8FvFgN2pl1t7K~U} z!5YZTN&-fv)f%I)uKG+%iYctllNLz65e{CKqO=Ub@tqteQB-^vq_j&n8>aaIvr~ja z^jjhL)yC~wv(S-#2ipCyw3gZPRRmc~t8x zleFP#4f;+@U>8Wh*IgH?WtbVe&^ui;bdB=5t(U0gw&Zz5W8<@{&31in)(%gLLzzIV zAccm*-7$Lz%)3?jf}-B|=xBRxEv_V%sYjq;IjH(^F^ehUWb0^*vufYL)O$Ud3iM)@ z;tWKWqRsf1(8J3P9~eszBd@2UK_7AR!XA?rFr&11ol`V|r;oWW5|hpbW?x`*r))iR z)l76QQh{=tfm&}8DG}GRn?d&M%B=y;BT6j`7M`ery7|fh;Y@>=J8%PGAa0ckVI4+w zHG0g7q~A1kbo=y-|07XCDR};vltFMfc64*8k-9?`5k+g$0LK}2g!tKoZ7urjv~#xs z!7uhWbmyrMZ8FdP+br@acD6M+p({)zla~RFXPw`OlZ?9SsAr~d2Ar@%Z|zPOyEZ*= zBo8T*q+|n78AxKUsMV#2rw%mg*ZkU7{C{@vH;1DI_uep6XYk0Gw)FVbCs-0-nU5Z< zdYhkZ3U-P)hE`)|k79}#={25#$x$jAsq0;qqK;!eVOXv`$GH(sGGFyj$Q8lK1>t$Y zO8(ZZ2MeiuGAjWC)L7h*wDr7-NscEP{+&DqRYX#@`cu z&vz|?DbH^%rGOkVaTR3KG0s*wo4#@8;Tb*m6q`1kBk^$V!i#!NRm_A2$1+qT8$Xuj z9Oe49Q=^h?z0lH|>_L19S2{#Sjg+3hrn=U!17O69Q>9+qj&ZsqZd>1p@UBTeQ#Wjbb z=lz#@(eoLfMeZ6C*IMjuF11FHVn@GkU|L4)x2;=keXc7Zpky82d2p@s)p4FAVP@~U z56%q#K;Of~xrZ-HDf{dIDh&>6PgSw$uRYEmL2ZzMr`HhwjcIr=8CHKu}^^-4;=$@BT(~y)TCVpxC@$m z7DE`P{!VVA7;&#|{ah_pwRP-J;f8!O@7C1e`Q($bQ5pyo#zaTC;z6jtUleknsly%4 zW?7&ETccHRUg;*^ouA%tOoFY((~t?VWO^w4{Y!)LFZ%KV0=)nMMhPmaoS7^zW<<#2 z3AY#_LfhpTN-;b(&Otf(93CMe4jN?g$gJCs77h<322^c<^mB&^zv6@RPRU)hO95H_ z$2i%+*zO+t89Wqn<|B~n_vk*OTz~B1PoG>WA;ybWo-bR(FX%W3djgegHiDLb%Rqov zkOFQ;D5Bv6b>o`*sX7Zg`(x^S!9^cqarM#^{+C~FokagyEY8mFnp&|Mz*-Vk(#1T` z(F`h9It4betGo0^GLK7WJUqSw659PjtNd41Z(o5>8&OFL=$AK~kNI_!0Z^``da%9T z1tBJ-6!qBCiVM=8smqsX7O;CUPhhpCsHo3$(M%nXcpp2P;Kd$jflPP)Ugdix5Eqig zvur`%#!|g@qs5y|bG;h();aWW^uP8z9;)#di=hoUa)zb;y$Q)2>U|zPUV~3*z(x9l z;Gmq8YZ$1f8bggr~^jM;Cvia3e5JSKstHIGU|cCpiVnF{HZgRR24KP6fYu1|U_CO=dTctR)plDf)u zv8nL)mdQ&5B_$CQ8x+Unqulqw^AFbm)#4?+;r*@t$7@-E{#V$f(1fgT`k0&c zRW8nlhzMJFnJ{o<*J%ohXP|C!E{yZQ@#ej7=Snsd4MG5o?T#jmivr*7gCW8B@A4I5 zO4)748xts|Y6@TMf6?5YET-Gq+R`#KWN&sqNV(_!0#S_RO|Pm83IL4?&UxK+N8x86I%Rx3XCIphn2>|s5-uG9-jR1nkHr$hJ7k`?~y6d+^& z^MU;S2EzFJLR$N9W)Fh4EooNDOmtNhxZA8#@=W&t)WRSXgJM)9{5XBP47a+VV{Y(< zS2T|R${g6;JsBmc_#0PH!9(M*Y^KEL_r7Zg;=~NU02%XN6#}% z@iejHi@ZWfvc9tS^z0rpzwp0*;BUrC8x{YbDA6%KU?p}RC)7bslqg?{k2 zL5tLb51t&ou}pm2gd91EaHy7Ed@gZ9koGd0ik5+)$a14fyz98@ym@Me19Jrm`{(Lty5BxeG$VjuAH}_VhGjON*QArEF z*M%S4MIMsCHbpkOVH*OOA}N$MnC?sd`?dbtJ#=aQrJR<`xN?E**ZjPwXyIq!?Ni)% zB&?pk=U-jnx7dh)Sp=$HM=7PTL--2!_>~-uFh%pXsQ-RS|MecE`xhIP5IY_U=P$c} zz*$APh%Fidl%A5%B<>x1qLk9?s(3@ap%G6D$-?{W@?W}ke-#c*zSh6F0RQ_QLEDPI zb^^y08^;l`tNHn3$1u7t82AC%RGqi21LDvjdWw=jcxul;0`jgk1b;F-CPHzkmL(sb z(8w_afP-<`Lg8HTpS1$e4M_EGVXNAOGs{#8@Rpw>FyTc%XqFTt;)}+t8I`daIRT-< zWo+oA!2!!2V(hN?<3oe0{N+cz2Z(Ki_jb4UEO@y8u+{%WOj`O7bhIBmDcaMk5?t*q zeUqhduL)+W+ud38|NB?}{w9d~FHlj$j*q8cC=EX=aSi{pvbb+q4FyQPn#U+SBH4N` zqh~fu-Iq1m)b1%K%zNQW|L~~jekZj64bo$)`j;)kDM3Y9BxOh#;Ewu$tYCq~4I@ZH zqoQ7<0{&4m(1v&553z{@V339>b)ESKhzSJ8f`5ZRl2>5pJFYZB==;pknBM0p9VS1$ z2t*W}fE7;%=`PXp-NTAE`S*KE{-4D^kns1FWakj*0ReT!o8F;=fst4eK!l|qe`=id zgLpDL*WY!7vPo%=pq>N--`lY&7w2V>{|QY0g;0O_89~74&(I_YuAR`50hW+7p9XMd zBTb3JN((ka%6lb-BoF%aTqbf z8}I&(cN(FI${d5O%!0B6jThe8ejc+QyG6-+2A7( zYO!4gz=Coc;pVCN&OKWfhw%!H3gYa%pQs*A9C>yXFD?zg3)K+VH+3Jg@y#N=Hv^x` z8&&_fbifS%Vvo)=niYStz4yCb|I6>9eQmlb8AS68JHDU7goK5gP)wplTue+%;@Z;_ zddL$>6;N>trYTjw-{#3Z>F%39YlO|ewweCHO=l@o%R)ks>{WV1#LUKLnAp!z2uZ7~hW$S@8~(Wt8d+KnnA@GNtE!eBS0L&X{re5};jd zTk;tTGjVsImdBf}CTPA%{Fs^* zY892<5TPd*h4?55*y$8;3F%}Dv(|{K>40BORfpBE4B+NQ^1>bXq%z-82&(^w%j5oK z5WJ8g8_aNbuQr(}^B9>)mP#^#hT*KHfagMpTZ6{T9SLF)U?e4DfD@M%6O)1W3>rh| zrul=&$d*&hqx?a!L8y;ie@o;T0{w@WY`}f_VTl9;k=(Cw+SUHU0)Oua4`2-=n?Ss- z-&JPX1KMHKf@)e?S}&w8-p?BOFHqbTpKL(SZYI3IxeWdDTvMYy>j zGtNMj9Eqn`6haXE-na((Ku_;L-O5^_>v?@(E~Z)-JCN&2xR@8(HW8Ovm6bjQ9SJa@ znj?dk%{{AV1KAC06xjDZk@ zZ>y;Izy>88gLufaEbvLBhnb_E0mdf^^$9+eia3wRR(u`2JZbMKyk9qj+ zT*IgfYH2kF&RVJN>B*td-NEt^uJqvP9-P@TW?eD3JuUj^F4~V`~!;MPynF% z9Uetx?ClyH9wp%IfDucmC=&FzoWhlwGFQ|>4}GcKks`IBLD&yE;K0dwD=Ua*x*kc9 z#qtUTo`I!C9KwE=fP$`%Gm?Bj^>zy3onrjc1_JADv z>o20p8g&gh3#XsCSIs?Nt(+@pY}B3ltP{~pw{{@VthzK;Ro+!T_i?GJe8CD|2Nf}1 zLxigRAJAA1KqG!Q`XY>YLS=YCgpzECHhxzqS1d zI6f?zNYTN86ER*ZA)KMotaZ}$?oYnOZ7g8*fFTmngCHLLB<-FqpL@#QU+&p3_fbL` zgaMD~$=&{v8C|(S%`V=v{#_7!0M&FTy$PC17zVl{cAU`Z%gE^C^JYYK?1R&;qjQZe zG)JlawVUP7sfwDK2>{o1+uq;5jevmQSE#+%(qv@=1?jbV*P-1XzBWff5r^K!PRUd8 z{QV#$W12+VtcAx+-8=@y|w~}r~eYBmr%i~p2u@^ z$UMmXSr$7EhJSEImCm$I%HVvTWck0iD&OJzhtl~h4;EWpjzUrOQBkr7N?cqSfKZ>8 zkB@(rxyAR@aB1lp;kj_H2t7-ZvPerIE%L#xv$rCLvOu` zP|l zY~@fvPC!xDlMx1g4kMWF9~WRyUJIx$!hCxu2TF|>LLO91TNZOgEgYoBSF+kh% z?HCZijGIja&@?zaD}%K^+lgG(-@oPKFsLdcetC}+&*+`p=^;v#+?)XL)st?vOBSJ( ztPT((_Y*#iF9_XTbonQ(+He;9$|jniC^_u!0)Nr~h1^8I1_Ar@PCcib6tdPAQ}Y-; znhHV{BZx*s+36K1Yl_TT>?pTq%^Wxe7PpIuCjTS41=-2|avboyks_Vn(Cq|M*~uwd z@TtJlGb==ON6l>H`Gj`6*{3`WraqL&eRbTBi+??EW>3RBRcXRpo zZ;OY6$3KZ?QbFNFpr^pbS$mN2E2KAcjf|=TILy zUnBzfR6~e37J(E(P95H;rG&~45NMr(r9DaU)(r&f*X}`>MlN}q?H11rmc@G^26)bZ zE>&%>1kzat{O>dgnwcIc-t8GC%^4Ddn}~*Uk-3$Lp@8N|KP+gr~UFOo=J+5Vcfg3blyFOf_dAiq$VV ze@Kw*h+O{X{KzV+DQfBa=5L3848jz0fBfFc_w)$#FQWJRbps8w6<8~ZQ*Q78$0F2$ z0YMfqezJt2P@NhL!W@Pu{wSI8^SJJ%{^jD;U6+{nKfqEvH1zkigNH#t_@QWmjf5W; zu7OVgJJ24n-=w0NZg)B%1Z2`QfBN41?%|pZbj`rWO^=*A!Pf+UaxneqUT?C#fk$Z(v4td!KdsjD**sEQ9 zNd?iaM9>s^r?iltjDh0~MC8R^r&evXauv?!G={Z*Sm`EWLt@OEo@%b&Q{#)pDil*WcqRbb=e+D9v>6`ZM8btkQ7cM)FR{{V}SAe7(Q zAv=pe{{00}=D zgdUqIIoiBeuzHfaf@X3e}ATcTmw*TFzXgd8HyB*To{354$2 zGH1j;U#{)`a#A4Rav)SRB3|Z!cIxa1`Nt>8+~Q&%RRfuaK`inFc1kfvt+~c!-kvtJ z@$$Vo*WwJ;zL-Q;RW3i|pPK&>SCMp;d*{O}HN2reanunZ3$AMsy7 zSTWcC&WaF3bkV5!)kP0(;TbqrH5#f_&Fq>r%ypYKC_6h>mA|dP@{sRcdhY(Zt-<_h z#M6ebcJrsr!;Iy4X#bbko*4xKFg=6#MBYcpD?BE%O%j)01q!m9f}ai*>UiSyPr*_8 z12NihI6&mouW{u0D0u(G)4Nqra_1%`mMn!Wbx>{$~RR#MU^!AHDPmkW4U%)DI43IEe4}43#ZPe=@CN z*zj(KVf)j{L|2#STlh{}3w`hz*)Rs|2c;lZM_-9^zZF{Y%!b5B$MW%?>RH1|d8i+v z88<&~w$3yD^_xN!AT%^ayyFwfjv2DMSP=ly$l z!2dgRo5aLtWpNCqvL|77dIny%zRnqa%T-~remi{yB)bL(2$stFFif~fA;QAKS`|j( z&v!jFq-Cs;A_o_^#j^?0;XWq7?gjjPIH=e7ti8Iiaf$K>BWVq3x)HTJLwutGNjdla z$oOrGW=0SicGYA+S;D7Bggj=vV$ZLB$}$B?^zuiGAI!AtU!i1DIRxLY*DM3rCL18@ae98J zU~$-wz)hAsbL|fljqEZte;!sQflMjV;-(rMhO3oV#abkhb#M=CVV6Qw&NzI)frof6>f~B0K#V9o2yMJq;63M7}Y>p@)Iz*wLk05|M8%U zH|vI zGM~N;x3Hc{3O)B!zJfza%knOoX1qW|)OxWQ-%AF00M>U>{WR1cug`C$Nb98_gju!9rN4*?tf?SGoCpczxjpw*_ti-+&h^}9St0Q|fiRUU`Rbhy=Q zO3QhAa+?q@4V2@Pzb#@x5L%LqNI`%ksf0!4m$Ih@5T|nkoM+INj;5vw&*qwxZ}r=a z{Pn8t+fT4I|J}9 zS|n0^y}A2yYUvItk#ihFA(m$Bhkb%F%{R38b=k-kQy&Fp)!DBr^lW}-i{z1r=+ihh z>6%5hwR223i)GZl!A9571mIAe4a9x6bcXk0!d4%ts7CmrHP4{QSGy)mfJ<-YSZJCT>q(> z)+WB6s}|?+6Bb|etZkZPRIE;#lOh3A@Vo)}7~SG(B&&LRl%MrQ#ayK(*0=R0b54oJ z7}rnilwM}0xn|xHUBcIgS)FctaQiWD?>O)w=C$_WBF zE#u*Ykc$hNn)mxRNmugswS>v!UpR&XSROT#Ljou;yw76(A+a7?(0U&N{kKrCayF&2 zWx<>Ic6?IoIM&ZPD!dvkVo-Pslk*dls_%$e$)sc{1ia?5s4Ey@+i<~nc*O+_m1%Gv z@pLCo-}K%9>Ur*i0|n6m^mjE;+gzB~@rz=eK90b~kioDGu*JBKv_i3PKGQfv2W(JhQs3aaIzDi_Z5LW!|+YVg> z4hhXv+SU}L_aytr%XeU}iqSw|9qhsWC}1<7@nb?VZc!|eG)yqf(imunk#!X3Y}Hr3 zkmynQ!|FQ1=UEkJV4&A9nVuGq(z=iVF95`9An6w-{zwwXz{!5TGKd0j=1&HNpUl?V z6?7i+;xf`0{X7_*aPiIY6E+C@slc@<-`Hl#USix(|KRz&u1K9dTOY-O9%4GI_Cb_- zq>kqK(Z*d#xp-Y{*r`yf1PQ6WQ{vSGq2ftWrSnM^|wa+8ZIdOh+sGl;@^`CNTNvX>2i2iE7 zm6Z42|H(di#&2#>pqnkI4Ws;@d*KuSLsD)iO!`ESRZg#aN0iU6Q;x>cP!WiLQ&jf! zQy1J@zz?$i>IX?7hX50BVZg<}>jVr&0RbVAR#^LCO|a;`DRHS%P-kT$JgskcSC_>$ zOuv%RjN_jf(M0g#?o+vOO5?>CuIBwfsn{RPpPl8lB-`-*_@b8FL>#+suh7lrYcmil zmR}s}Wz(L3l@e=j$UyX1KZr&;6lr9f_mu|11z`iV96k%w^TVI#KT{8Gv z!*nrJh+?H7FVusfw5P|S;4S>XSoh8G0$pWuA{~$$B|W?LwIKSCmMcdrorKSvEt3~H zUZ6`-pj}uUN`5@9LxuA5tO5+`MPo%^_JxG44=T&!hXCvUroKe?D%#c$%9yRy*Bjm0 zoL@^S8v(|3$}lB{dQli6_n>4R&-_S=+l6JXhpYNy#szYJgwM`Xy@M)#Z%)d|{rP2l zp5%n@ErW`5o_fB{Wdy-+l%1T}KhJG}kV`mw@pT$4>jWSANwoOcXPH6EuBM@*CfF4JX8?Uhc1K!9rCnDiYY>XJmb=E%G07 z^5G0@-{0wW7i<0mn2VUUy8lUrxcj;q<-0bP_M_u4k^_Pu^Dou9x-Z85djHWlOn!KA zVuAl5C_e~_D;nhLU^)))6y1G2`;vGwAr2J3s8E6fAfL28hfWr0D*xrNqH1bz`mM(N zcmGfkzS8^oipR}r-0045NNczs{ai<-d%q18lEAFnVokQU)RYt-BF1LZmPu1#cECNY z7qyN8kPGAEhJPZ2^aFk&1O}e>XZP)MVh@0j`$3R}!(V8hWSZuypl=T+SeqF12k&SZ zIG?lZLci(4NGmA(Udu~i$QmJMNZi{a#+5943H~UP1(bZC@6F~$B=bBn@l{K@NXv;q z8a{CTRWvpmy1%MWuJk>xO0ij~(!M@gp*TCNs4mv(m!`XnJJ#iw-U!0rN{>7s`rxUK z^2oYKh2VdT_9uwemy}8_n0m5lunFc4}&n!d^siafQx(Kjcy)bRAz39=3+Ndr6-Sim% z!);%S!e4=(0^HExBrGi$F+?dRz?P69{$;ngz`nT;807i1&tTZ$!9L^=CO=@z`pPZw zKk=}gYE=fN&2ljjc!fmej!dS(M z?rtUv#fhEb-x-|X(jL4m5m4^Y1%-j6XfVki-@T%uQwRW8%V7NH0)kN70ZslBi{zHO zpQ}ee!uRA_5mpcD*w&M*@kAyMU(-*4T(5f7KMpu*dV&XfdJOq zD!HxFHjFs(GsR1^e>jQiqloe5szV3liwQ#sIyt9 z3Ab!#0sRjp+|5AVq;jsyx(4M7^pq{s>2hlAxc9aJ=mNTNE;=YXs6KTmvZ$GJtd4D( z&E?5RAp7B@%R=bP@1#cmTvD3)a-zhUO1rDHcl|%)2>-D)55}HN!FVv0nnEt=o!i|V zp8q#@3uSY?@RyHsk$|0h^WPc4zU4Qa1#gWCl?a`$-sUt38_kim{Uu~t#59;Q4W~mf zP7Csc&-JA;8n#a#6p>~o4L}^r1`+P(G%SnfZM-b<>+1tGR#zYL(>@pEuL@t=Q@bs> z=%`l}NhdGECB(fMd$Mm2seW|bIBWWXEg?6`?Cx8u;8H;3vPZft8q{}u(6Ye)=_5{* zuR0%Vjqev{@;7fh`Gz#Q@_Nh{T?lswdJf*KmRYv!4fI} zRlP)j|Fz_|eSRI`F+@p+r2-4@EwN{2cD@-_+PmYh9sk`oeMp0kWn*@6N2F1B_uw|d zc}qC;oE_yM$m61)TkOVi$NF))hvcXOnY7{aTR0*}URo|q7zgJ#bZ}y0q~$FdKC`m} zR;E>A6^W(IZM5?DP0c)+rFo}IH;|@3vQDW+lU6kQTAhZ_Nw-UQc^}yH=_sn4qIXzI zMmnx#+9p~RXjQV9wi$hqOw31}U+E{c0=h0qri}lu@;nFMLq=tEm!;_Tdv%#DiNW zA;}9OK!32pt%0C`USvB))ptejmvbevu@10O_iUDejqbv|DmRfmPp>SzAN5aP5m=+# zV3;@b^ns!XpTFulobm206&J?0&DXkxJ>C?%eD6Egf4{qJ(`G|W2#1{`ofR~m@9mWW zhG`?qi3h^|EPw@jN}3os8@|$DA-^Z}S27iI64#jfA#(M%cY-Q!fo`Q=a@sXv6MLgp zNI0$&fKx`sBxikAiIdEaNR~yEFL6k_#tenPawY)^HeJENd9%oMwz2U(Iw~CkGUE+G zbUOGvK^ws(>cZ9{I(J{Vaf~nYA)+#g%Zf>pD3L)E;u`5SUvBZIvxiP957UYn4Kjr6Mlm;6v*|6InSU$>;K)1 zf{}^o%_Am<>U_1?J1!26t~LAq`YRG3h?zo+{OQ5@r9=+#awvwKI>;=o)9XyeM&VZ- zn)m3ne=kELq|C=}80#KoSOipMstgn!#%FiFwfe)RveM|(a5&!}*@MsMHTs*fI;{DJ z!(6Qx+!F>e(=5y9mZP}_^{AIvoClzc^wZ}oM2zwGzWEcomO=y~CaC+9XQGs?~B^9#5;Z%?u*biui=9Sa_< zY1bUx5rA4VZw!;rG}SweAt}awdbFKK*Sx*3UwSd<);;lRBTObt11t#BO4|=uq@${j zR+S1PfIXkjbjMkLn8=e9tc8&P%? z0}HIvqa^My6ofIa$Bv)5(s;d$#PN{i7T?rni}3>{{5jAn6RT7sj?u|{X4)HiP(;oF)34W?~-lvZs=2nD-g*9eECf?cFGGlYt zD`kYDqg7B0fpWUNq%UaY-sYKL702n-$Xlf-k){!{$_Fb4+~-@p?94lvAEIN^DdwV( zL;iA`=W}habCR@eQsA@3h)kFK)?2FYXP0zQ!onmZkZ4U1K48>5AFv6&voTm9<|<<4 zYjXd*bA9C0^5x5~t6a1;F~az)$pK*ec}v5knp%HijfV){eX{Etu|LUD*!4I;5bIwC z+St>%evrer=X%}65pS$Fq@sc51CUblw?=JA8ST{v9s|Qv4t>v@b|T5Ly~#*rLQ!G4 zlO(RmW?6wdUq!8@L@URtz2D!^e5JnX62MgaP%CC9b7L8f$QtM&s{dr+i&mi?cY>3* zeM|FriIWAMY<#iCJD`CtAe3`$xEe743RU4-n_aed1RzqGw-Cm)1i0ggWq!Ub;0}0U zVBX!;;GGZjt1{@gBAMAeE1o<<{oouuZb^yWF#qLNg@qHlyGr8jyCwsduX8b)-WQct zRSVxI-1t8SdW{O(s?cFjW%{g<8gj>WnEe6vh|B?iJvFZjXrEj1xc++$iww$3c`vHP zZIkO~=t+^wv1O{WnC@2y`_mu9 ztbJKi#AK>YHeWraxdSX2iJh;ixN4wbiOF1-n6FXaex@8yD;m9GA?6Yvp#3NT!(KrG z)Ogbh)jO&waW)uZ;5kupQ!S8u!~QH)e1E{I2{1#J3(d+{6TeC3WlE2Aw>G`$OkcV@ zLWbmsc~r#EjHd`oA@fuhm`@0EKD?}JAwLL%M@XD=|JuzAm(U=gomUmDLpAkf)?R-2 z+je5%sZ?5+M>j4gNt%f~TnAIc`MIe5v|FZZ%xua?{w?r(iFtVAcA>_MSzU)P6$UWX zFvK9e*i~K%mDG^0@T{;#6Rdcu%e69E`~-S6&fvWEGaV%JO|ZJR{4=SB4m-JeD^dOO zV8)?#)K*ar+dizi{LazxM+)m*KU)MawZ!UThDrbJSk`;CsMQ;X(v|{0rC8f@ zmWSd*jH~@UCQbNm5@5hEma-TW{RD;=m-6=ZE7Gg{WYXN>xxi!^WS}-+EVotE769)j zLC9=~SlmmiwR@bcww|@=%zcSD>j+qMS-Psf0ols1_eJWZ zhdj~DXTv>dy)|gwMG#>Fl9c(HxJtw{D&44k`Z+H1*q<>vtUn_x>Zk% z=p+<~0erlnWaQ}Jo5h~Tm4VW}KMu`ftzYN7oOP1ln@x(cG#P{^+gh^n*`B5&QvZ3n zZg8}m`2<&1RawckYJT|p7!aZWs}6%2P+w?y=RNjq!hp+v5=^T2c`yIkiiVR~g6L~Q zYEPJ##`VJIl!)-Tm6U~=38s{}i|qjc3a_F>D&wPfils;bAr{o~6q&hg82i&KRu;$h zhFY-&P*1{ZBG9ydpbSv3i13$a&Fh78x6|9cs}}A3M#t@-!;9bxBIVlLAN}9JTX_;D zYsX*IvY435s54PwvXMM9oRZrWk!4KH4ih`MOV(-f=EdRY-uKfi7@>raN3EW0N*WU% z*IZkx#qRf|Q1u_ktyzed*2BvI-}&G`X<^yLY067bVs z`UzC9ct3f?A#)cN!d zmhO-n^G2K`+x7QxtUW3*O#iK7w!NL1cu`VfntZVL#L|1CuIK0%n#U_r=Tki95H=`^ zclNo7G#%G`i#dD@e;{p5@RGMaY|(9_ir+djC7qc{0`^;EX`_Up<>~Q+DI-{v1xp>q zu1+Pr{-FE7>F|?VT~yvsEI@oQJYa5;>E)I}-+s+LVWOh+^`+?M2?6f_2tBj{yr}2p zQsK#Qfz|?gWdc=-5;!){no0nT!5?)Qyc&ZN;B7daJNfvSpSAf5ZE<_Y)j0KTN@ou0 z6lVMvYB{gcqXww=)_jZPuTt7;tSMUuSal9h8{TFOGs7ke2Bj$qiHgfB(lhsv-ae3{ zpwt5%0zh|7%nN?-c)r9xV15_E)iEgg^I-aYL$)AwM+aP|ZoTFJeS%wn$_{!&x}doK zs&$ch*Q?Er(dR`sQsmi{9q?MgcAh@6d5Qp2yAnFeTI?y~>$M>Y5_^LTJO>kBqyAYX zlL0=HOd6O+OLZMjrzCyTRgvUiyc>L3OdwPvsf%(X2 zBEHz06T3{W)Uf`ak650w6%7~P$8{XYc?hZoy;;D2NncvxVW2Ge=9v=?6cykIoB(Jz(P9rHH zi7T6%hdvVpdKJx}rv($b?)Bq6k*!0-$JT7mT-G6BJ7YgLa@1=|_J4@Ol0JBC&Z6`U z4X7d^-{12{m6k@rH1aIsg9%NBwILqY6zEL;ydSJB=f0$-o<~D1CbSTIIZZ~)54cHX zYHQwKbghl{Y%}S+^>N!%HS@ZVJMS<_{WUqMf=9ugw7B{`M9nlNi?ag_9oDFO@$P&t zRMmdD=UsqRr}yG$1+b04`CxKvwkCnF?%bzsqDMY;+^~4Zz+K1+Cy+H8{H=MoKc+?` z`Q4m=b3qdAsQ&@h2M*|VeSzvgrl7FVMe;BZyT^=_)H<54e*#`Bp!RtooXxTBK+Mu;p*D{G`_u^i5|WM)>iS^p_Q6}~f}#r5o0XnJrI`NRkU zaz~676_qc31%Vw#*7LNS;C=ub;(I7FM|W4bgDi#{u=; zFq=lQ(Q-9MGF&B>1tAHffq@$_;@-3U$;#LapnXx z&Hm4zstp)+ZJ?+`T7joId-awu%Vyz__Gi3IML|*?_aD{wVqdWW1)+6XRkX^^CrhOWGPruH z+`H2S0o8xoF{q3uik{808ZWm6>}h#wF2$Hc7s8E*_*{Ku357nRW!xqMf>sTaX7lHT z)pYs;TU^txq(=#>qSB?>>N_o@{O%u?(}dK8M|AR-9X-)D=m}kpN7ZPTT}tsK4GlLo z+XF8utS|7b)C1?5$ayO8*PXasqgcptw0kh`6QjbhYe`oB&bpi`793sUiJIq3GoiEc z(54$03x`&_F2y}EY7s70d*z*TE!2q8i>IHRHar|)aJf>wtX@=ER3W2U+fJqDM<(97 zEG|os;cP$80Z#-Oj!^=}?IXTtJyR{rcySm$job{{2dqKbmD4LVyjlo6L_Onbh^IeY z;NDz-nu)%B`0vfn#=Ev~-^#0U8zGtl|!$sFkm11i}#AuDF~9ygyJPn7V$B30?Z(kfij@MG6-F~GMf#&%Nr{zN?k~4hW+oX2AAgkYe} zY_rZw+?q2znE7^H=d{VC-S7s3f?-24imieX71d~Fxv9daYp3F6m6B{h15zgDda%;) z$9U5~Td6aR`@vAN%&0)8+5hNba~e`K zSJu{x2$bR8poqiiyfe)TY>`k&;CsQ*Y}vD5ZiMTO$?DmfOypd5CJ6JaRsIp}yi!JK zZ*r3A?|{%_uRZLnSx2jkNg-5Hd19-80h{eIQ!-v@L69p!@bsIlYnGI$M869nA~uTQl)M=@ud6)<8|Ja1jfQ=ks7fjp&8pYGeR%1a~-ZX|na7m_5havZ94s zr;M!p%^mqV=k}ex7_jK%(sWryb;*H?2bPe+*!+2w*~vir_kjUi(NXWyBP^4??qW?2 zr*+r8>GCGM%4pS#td(amZ6gHj(`HjMp6~61O~#oi%kf2)cOywQ15R>(cQ=Cos!bUN z#mV;6=|4!mq>J<;7>#-7-Oj~ zHbVAAc&Hs_Us>>yvEO(jhQxJsTYn1SmkY=Y*s<^JmXd z++j+CW6&W0eW6l82saY9MUBlL(dtZgAt^*r+{6i*B1c(KRkz3bot@QC8;3%{r&A*< zh3~j*H+!-wP#t!(mhJ2G=$}OA33y9vO{w@7oIIA3RAi*cxoGeRM7+X~`OCdAT{5H{ zmgGNpg)_ZHcVUxTzMy|~>Bv9TL}#sW34gx{pQc2M_ogjX@X!y7py|Y;Ktc|!*B}V( z2lL}IYf~<`oWv|TxDj(i%zPEFbFgCZ@^JpsXLvFLF#0&!uI+NdSEj zk0J1Hg$v}gIMaZ_gK_`b$*yXNt>Mp6BZIn`%$Vq*;)A2<3*|Ql80^`uZC|$cY6czpank`cg`|IHpLxT;yek#tA2IKjfSu ziuWZioP_ZPlPr6kaZ%pXC%b)znWsP^PsQRx?bG>%s;ySVw;a?N4TW@s2h13f<6lW7 zHt&FRD!0k^1J|1-VMHM%Px6oin005FG;-aopVC-n4|JB)C2q4O4xr?h>vUK9)l;P* zRzaVCkCjJ$+iRvML@`;5H-}`sf~~NqNpv3!S^0-k!0{RXShya zP;Pi2-TKG}0M2kFMh~$Lo%;H=2Y}(x%m|&11n_?T`tHI%2D;_3u zScivlynDw5l?wgF&cQoKMO+Q*)k8t-kXD`4=9~fZgH2=b7Hg6P?ZnBjF#eOm9k&(q z2+Nq~Wz)qcHl^Vv{&w-g#n*T3St59|DY5eVkU9w*qF9n7A=NNI0vS!F!)kGMc%-ZI zM`|x4WFh=M0kKpmD}R3!0)Dp&%MVMf1wszpFm(!0Q$UMn`mJmlr_%ogGR}~d_;@I= z!ool(4!yslcxIv-Nq$%UR2wY1kZnSd#?S;z82+TyPjNLXziAlN3PizDxDvA$z^ZSf zRWdJVZA80UEhbn3*Mhd?zU@LQyW_!nIi9BFrKd#|26>ZBl=T6rXR*+4ldzCb!p4hD z!ufDnn_-l^2mSZARYitUnDOiQq)i@?+miD(K3`Jua5LWbk2PqV)9fD4F=vL*oLC~M z=k7}^$HTRoH(_ZoAWU17MxF!1@5J{7=%KU19;fo?pvIhfcXWa%|M&5UIiv z0?D8uFF|{LK#bV{-mp+I?0LO5qH=>h4l8M+ZFhsF`Oo_=^M{YS+6pp&sexF|yN8T`vUm)L5vY{XW2C_z9W-b;_;B){{jsEk=F zV)HGv%r5GDKEmf0dJilrprh}-uft9ZQuk*-KM5PQU_-*9gO?~(AF<*@>k~``kH@T3 z>{iCjSF@2)GLXW;pM!3iA=;L=tp!WtvNqq97)xLr`2cs0kWR`w zAPB+7ct`wty8iRG=a4~aO*(h9g%_t)x#2o{uYDVMPe*!X;P<9W^%Y8NmSyLcJuBav&jPU~AyDqv@=j-;GR-mt0z)m9;^;>Y zFdio$^v2V8+)zZ%){YB}Dy4tSI9oe2%rTi6=0EVhX?~jbM-i)O{+Jm*#Hm6rw!nm& zI4Zv7^YRc>ZI8ljNZvKP(BS+_k|H4;!F8}Zm;@n^eDjSjMkuqPB<63}r~CczjSV`5 zg4WZsOd7E;t9@`t)QtC*p(Msc5i&2EVr=sB7%8(X!P4?ApnFS&v~L|uh^Y9{YlwXz zV=dOU;Ff%-E>AU_mXh zHZBB{!Fh}%s4kY)<}GW+YbbbfY}75ekheNGitJB-D^}E6#kDRvl31gx*C}k4A{j{` z3nMZuSpM_Y8{mI6Af-;)3G`{op2KyHKSzb+4ixE+95Y$zJ&Q-1c}-ySQ?yWsIB8vz^FpknYE*)v-{G!^d`k!?;S#?{h}Jwm z4v#Wg{*@C)<(uF8E178}ng3&#;mQT9e8G?=-~2XemsPTiUAY~@TXDi*gYbi*nT@zZ z-G(iSQg~~!U6Dxk+9!ePIie_6c>vSvWZ4e+tTrpTqn8VTk$Eha8MN)~FF8LhywK2D z?DC5*lJJ<*pOxG3Z{V!63JY+y%xg>vcN`1fUic$1n&G&TpKW?I?QY4-e{tH%0`r}n)>S#SG`7L3{3y1Jc7?4TkrRN3R!uYCR- zGmVE=!TaIHr$3ijTL!JdI%?5=0^@vg6DenCh-dFYL=$oBd-dUxkD`NEcQ`2v#>4?I zcz-uI<@#?J76S%vkbvkZ5eh84%k{<12*CmoMhAo7 zGlQ+~t&FI*{vm2yab^+}(h`h18VUE-;YbT;+L#7A8u_M+@Sw6wTl(c7FjT^ z$*WqkzY?e_`k-eJvaw}mLgXUiAnzy2{u(v^Z^)<VM6C}bFaO?iXI zUGHqkIwEpcYaDElwCwH^R8L>R8zEfOhHqpVPWoOE?F@MLtC7O_zxn%T_${>%wr%r)5c*(+vFD1v4!2)Ucac;{tpM`Sv2Ttjiz<0|Fm ztwRT`P*Cp=b_cCfUesfHREfvd+Ahn}us!^v}aZSSh?C9sGQ$B0RW^+-F3J$bN?Gi$GN_DZL}F zXS1kz-w4}#S7N3mRNDI=ME_bccve>7TTJ-2z$Ucy+Le27C*L|c}d;%vTBdCPlFW& z-FqQ;hOMX{U2qFUo{j8skRiMx#N1MU=;%c9HL16+2-4n}l7&pG*^Yc=GDU8{-8~bf z9i5HoP$l#YKw^oB+GdS)F&ShSo8cc%tB!di+DIM+Fp@c!hvS+hHd8w0Z)pymt%rtV zsCyLhE0Je_!cN`~qrx_wtqlliIJ5f5^tC}*UbygGinlfKb}~+tvHm z;Bb2KYebC>E_?U6iGyDwaIb7MiK`G_H}1C92{B{|9cxJ;4IS%c#fy}-EArRBw6m{q zRGhYthW7|B<2f?-7wv&)_uo7Y^k;+nj(EGuzuGq@6^}Mq^cnz;}Aoy7WcK%edo4W28+G%=BDA(mgbK} z@Zd&*?k7&=yxvxCFV)#r!bO+eXv_I}ReR@SwH<8n0HsOCI^Ft+=S&05o_8v+oqw38 zvDy$`Aa65mL!9L_cs>v&FO%E&UG6RG@-I(o^$_bQnw}VI!^|~mKA*1_)|Wb?R%akX zz#NLWfvaE~k}S|cu*1$kiI7J?D#fbrQxNrG#>2_f#T2efbWcDJVEumRu_W86v#dFdqE*nU5mThMw?8O`Y2syn|MV)GTGaI zNTDRNv0xP9cbO~?3D0y9d6Y6{EdplA8F}8DRM%bDz4`J0jxM*d$FM05WrXv9dVqdM zC{pr00j$h|{9lWV$5(BTjbHj~UI^b)bcbf7^ib3ICOoKc(x~oFNl!e@xvUz*Bz5@s zP~HO@Rpe-w1++Mm*>YDsIkg+an93aF?@kOMu${L_5*Ox!+xXoF;pTy%mVr13wvVUF zcPx3Ny9F{?R4&3UT(WyTB?0bvH++I^fr^M*aP!gS+o7=U-=#h~xu9qKFQ7Y4AxA5q1DF{hwtDzW|*5~O07=$bmMa__;bTQ_;W!-qek72$E<=Tu~AGJLV#9Hie ztF~F5RUTFioh{N4PA)D^JhiP)=DXoFp1P9-a%_kwYAw==~L5)C^ek`FY=>!rM?97Cvx)C)HE-6K10} z+SKhhEDuI|6Qyfh)dvZfG1BO8%^E zcD~S+mYc#C0%-By_;5y181~K;SLW;u!b~UPb4xI7gjEmwdmYN|>(yJz9%P>mCz9ew z2H?t^D@qi+9)-8k4mNbW-L-Suc*254M*?o%QnRd}^G;yuvIxF*Z~C!u;^Om?hf1lo zBb%SL5d-WY!r*P0`mQwV&pPY8;byBN-*`g4w?d-HQNS?X##g>NF=F|BAO4}}%Em8{ z9VE7b9LMU_^qzZ0J0ZvZqkJlVF`t0G$yR;DQden{0_}gaDxlXA&Euy7W@Hd z65*e}x1e00r!?JFCPKl`jPFS+tF7PrVT^b*!gbTZxtf@AAi*na zC=AmYP~X zF5an2-atW1mKBUK>o?t7fz$(gey+)>I4l7dKYV8BQUJliIy-1&Xw@MoU2?jChhfPa=%#E51C&x)hMoC{s>Q*>CI9pu=_VjB3 zTeDW9_og$NtY;#Mwam16lP%Pf<(qmWJZKCbiN<62(NTM3RdGaH^EH3do<~U1%G5+2 zRry+k{&WU4*opj`!3>r_(Bka@Z-zhEHFoci@vfwDWlvs_G^8)8 z>bAB1S9=sH{NRY_ib3SLL-xtX3jRJ-fX)G*d92lXaQiZ)Efho|p6_#6Y}1Rutd&pb zeR_%w=qM+>^B5!nLAqJ3XTNPksCD_FGc}*Y;Evc8eW7G}2e$dE{?(GU0tq-E2or>E zb0G>34dUZ@qzVNf)8XzK0p-Ke**?~ zQw$kmT~mz*i?bQ5(*)|B;P4dn5jrN!YVlmN`Bmj5nFFxvJqA&Y0pYZA*+a}c0~pLn z7PM$ua+HUJIC^Yz2so19=w`1Ga?L7!Wp2y4e+YL5GWh^%=^_L68_i;OWPks}$t=7s zDB!voQg1QZ@sepA>*^R(pO-G`Ks;a7ob$Q56_mC;!xfiDk(ylsBfDCc?DH`US{oF* zGw&B)WjX{GGivDynCyG|ZjAjv1W&$e z=$ZF!FFT*-WqvZhhgu55{Jk6{9 z1KCEe=IL(zZ^#BnY}Ia4G&}bn+PCe-!1Lko#Ols-Md3L{fPSoApIveu7~03RoD-Dj z;@{M6ql8sYOF;qo@y4Tyx$qioKO&sB^<0F};@mdN(-G~$^X083x1Jc23|1ubxDWn% zQzb;ErIMk#S%*dd*zV&P)}o{y}Ws@PdSU-t6@t96`CBgN#<4>+U#+K2Cx2B?sXaJ%N9*E~9vOu8s%V_7U%+*9lH+pkdXpu5ckqTwlx zpAlT-VO<}1CBsoy*!h7EvB71moSLHW6xfRkrMgR_ov$;JOuP47IeE7TaR+A?Hdj!yZc8@+TVtmJX+KOS9G+I8#cGP zbWw)mu4|q4k0KoBWyAJ0t@HKE9O4~3i&w*#c-38Q^9`f{qb6-{@*(xA$0qmJrk<>Q0YLW219m<=8^#`5kKVO70JuK>>6&cv< zID0@5@XwKe0jjXl0sA;q2aT~!j|_qtEch@>U2m0J>CnERFmQ%k)hd2~YXn{e7TE7q zo(~UdayAb`;{D=p**9D+h(BiUo3PQ1UR0u;7da_c$o%SK z3E#<$Ad&vvp0;pgAO+4K+1=$Gn`19q7Syh5Xm z>0HH7CxBva+7r;okqTNX+vM@^D@&?48_-|wN5{c63iN|sZxbK zuw3TTthSE>RCy)&6uv?#woVEiG`n*{SsVfx{px)0-EXKBQAvQn z&Eb?~qEk{cOQjr@J^Lxq*-!zNMFT6ckORIKLunI_&|CJ_;n|Q_m~XhU=<1lc!<1#q z|Evg>AGS$K{_Lc>%39Yq=UB7Y=jx?h`=y3q$1%ZppL9wx4@>JDcKU%I1^{?}!Tp6w>Py);r3y$*e$sV% z-+F)|8iaTs#E?DsuV_WTtfTG8(1jCUkDnCeEeL>WwR=|M`5C~0rE}b7*J2?TvilOI zGBCEWBz3}Eo<4frnfFWc|s6=hrgr-C}4O*Hh-o=JV znvxw^r8>5Mq#BhPb6My-7k;zmSU63s%6s^xLP2)7Or4q9a{O2V762%_Z4|fb?YG4w z1!hnPUc>@uQmzIiaG-C+U49`fK_;KMtc>|4iZGW5H-Rfcc+Zs2AMA)*F~DG}+6Q(F zLyyh`Uw#cxm1;eE*-Cq+N+jXNt5QO#9vK6RvQZaF!8`E2S6L*Lq1WmFe0&(tOS7>< zelkg*YGQhw`AP|Ijp8hoqPz3ufCzCuy(WmP(-^~ z&St+UFjbrbg|yMBHJ|n3T2Ufz-+H><6}wD5{YXsp!*9e~cL%=hRZfuMlnVmZ-od)G z-Udz)RB80Giwr`57hS--Hmfo>Ya^fW_~A*CNYX~2BWc~V_sQ+}XTjeK|B)|7I!&-h zy@|;Dz;_aQwV-`6Laz){-Z5~)=ZpYWo(&j)0}*&jE_72okDPGXp!=)8EY}=dp&Zw$ zFS{wcDq@#vj=r%sy{C8oak9Gq<7B~TFi6PFC>^k0SYST@myDGnNmE)(AM(%sT~w}% zjzvN=xIklHm3p|We2_uRni2vYlK>C^6CWKy2P@yE!?c4h^L0dPzr5a+WnvIqSop#& z*`;SM_r4l(j}PmLk9cE)5$Y>REIpBF^ju-MM_Q@wuD{UtRus@YdPQ9g?~y7_EAbf6 zHHP@RoW<2qro$QmI}`vY$p6$kjjO=;%D^E|;L7)F@m#}IUjBZA1LZ=t7apZWt~6R= zse#&f6keOyI9$jvFk?>gCygf!n{*gGErkEw~)TP`q5<%s*%C zUekqxF99j#9~?a8nEH8X@A?+w))7(VrmS@;{w5p?W-$Kz^Z7v9$E7d%|KAI+Kd60p zv42Ifx#Qz1=5wRJsz}IZ>MQvET3EW&qLvWCk}-uZzV3Ac7a80`@i)KA8yTOZ#&T-I z*ZZV)=1kvK_5fHY0`LH=aLhnF8T&HimCwH?wmk3w9?S*4*FQdzHvGn5N?+Mn5-nrX za*X6rJ%r4dsaiCs-r%|Qc(18NC<19@3a@5nRrtH0RlD${NUMe3S|#f2D`p;P`!AV? ziAJey%8l(hE$EA?vB&AT`leHi=w()zSwB5KJQKx7j|}b_Ma)!ZPoa2 zg0jou=hdO@prn6HzLO=YGJ(R-td83SpWW!jyJy+EZ%6g%brQ{jSM#uI?^_3*b%M#= z43Yt-V92Db+?d_{$jowdS6cBTJE>{gz2aoBBK!Blr}0`P35=hqm8Rv@7@b^nlL$w_ zsUG(K(D!_TBS~;~e4ch%`syR}EgITHd%#u%w$_3wAK}3l$qB&ItTX8~z~g8=zuKed zeWV$<6hwhifIT4KNK!%3?nu%Ss`a@>LAR|XGhmPHKZX=oe=mPqxHhC21ZZ{p4t%${ zCK|8f(X3wT-}7kWr4Nj7@R|BKsE}Grpu0Te=ZIOd$gO1?cLv5Zh0n90bfK=ccF17z z%g;t0hw5eOgbl#Dn6Hl89Qk^)W=a6H4Rau5S~4-1*WDfOe$y3pVBa|Q2Fri`{OU)Q zmX;nH7Bw?7tJov%g()XdQYtcpAGk%9gZx9p1pA=mZ}w@=!C3JD6MEd?;o%#~-CQ_$ z9v@>Frl53^p6P!%QjRPuI*hNPg-!#!IG(5|`jK-p*A@2OazR#l-0ocE0v6- z_=9CUGIdRwjC}=Elv~^O z3_0|Gq}0$L4H6|=2fG)d z+IM($7bP>psQ5c3483kUabfVnzrf|C&07R)g980dV;;19EqUkGdWrY&fIo*0K?RiN zSript|2K)o1S;)1}X}6+&Y$0Q&O9Pxat9B{a zS7O2$x`S(SYaisoj@z|5qXb2jqBa_Y&HXj2U1~D2ZmXd-D?6i{s0L07#gd(P(A7{ae2F8n|7UmiQ-t1h#|dk9qw{BMVj~B zycaWQ`+BuHBs?3pPYY^s81JY%jhJ|`X(1jtb@CdET$DU88 zTrSlF0qBF(?^%v$hN(O>Aww$|AD(gG_g#0bBaS}UUg#QrXE8U$rIwE!y^~bxvwS$c z9z=PHidMKDM^D+`vjKBGS%UQE6RoN*J?mHbJ|Xg@S*AHlK#5)Hm7;U|K1_F*lg`$K z4v}O%c=OJk$iVd1uU}hlwhTi3_<1jerapgTXtY6co#CC9cpYNW;hj(3qfwIW%m+O} z;SRy9Z6BA2azAC60S zD@{)0A8$m|&s4#-TW(#}s#AM(uRn;oxAl|cw$cZD&n6USjc}Bd5_#)_&*Pq( z2#}%1{s2Y}t+@03v9Lb1wl5Wlw);%HEM#h`Yo)EZn-xejmmGD6YsGfG&)%F|P$gR0 z=d?!LBEEmLHdx z{#&ob*-6Wtvd;GCHgFQ$AF22B)Ri z>&$M#CFTd+2r8@@r=jr^(Caz^n!b#acKh`YR<`+Xia+djD8NukGgykerbiDGYrlv5 zsI234NV+(VH%v-Ab9^{)R#zBcYm?Q{RwJXVr1UjB@JNv`o>s5CZxHbSV;vZW&d&v3 z>6pdJZE|_bN8Udypgh}27V9!J@YHBaeQCl=NBnecvPuV~FagOY8=ZEG&hjn32)T+_pqs#1?}4~jd-*WvHnbj1oHA3yX-Hb_hGsd^Q+ zjH_=on4gh^KR8P6$=XrF*<)>Ap)b@ zJA?NkX8CmMZmu_aRbbEH_cppj(Fm%KlFEMdS0Xf~dC=-(62rwg^+J#VL&=KBxdrIe9RhyiSdgop#iIY*U?UrtFNk z)*VOyQu%kBH*NPltW{0}UV3eK-O->xPKL^dy(`HIw5}Evz~Z2k4@3)MM>}3cB2YG5l*rJ}eV>@-CP;sb z&{&o4eZ4u63(m{Yql@tzy3n#^$R-66@p!V!QPr{3`=RepBc-+tz{7!d$HxNZ9u(29 zu3C9$cc-E?!ExktF{#mqTZOZUcG$w8g3F5~_Qp^P&JZCxpK@KU=wyEByLkwx&`i|7z1BH@= zz#lvBscRnWF}SdfKRx-5PIdUJnDHd1kuFP~fwYGZhjzM9g2gFVGRSeRI0 z;hjC5I{X69Ci?E@&GK@gQ5;4itgrzM7*jx%*` zV4z2rTE=BME|k_+zO`)Rz3FVauaM{D{Y%qR;&z|D7hXTL9d%uFH^ZrZ8j%`AMEj=U zsS|0yw<`pR?VwonBb(mPj#_JGohH{daYNdT@PPSI@4gb|vfu^A<&P631~z;5YoFx4 z31pTjr^=LL)iq)g#u#J`j^kXJkqDHGO`J%2wHh}m-83N}t*UBe`9vz%3Lfkn8{eEU zXq0l{-MkOkJ%HPBd8P1FXew3ZL%k(IQ0CEbYg%VOSK^L`FQwI7dlT{gji;;r%a1@h z&~MjG90yjMR%=v}KU$_!abba5_ZgGY(W_;>4v?12QL8SOVCYSedRx#F9WqtV`E=jy z;c!jT*w?)HJJX#bCCW~0_P4ov$Ss6KbtY13qWJ?fmQq01MGt!#}th`>yT^f+utUq3|@E{VxmPnDSa(7>f zmbZ&>uS+^ogo2U57rkh-Uf)FkXat&n8cm(Xp3E>%c+PhxWzitYb&V(_kooUqn4e9l zaN%9h#CEBU(lhnKatc+CAcQ~jEKZxR(&JKmvqGTANeut_M6T$kJU-&0`r91Nv||0U zB&+`V$J_MiWX0qNJ3JC%61zN{^K5sgF#bhefw`#d_^Sg#IO4cy({g*WZ|KQfWU5rQ zLd3zd zzE_Dif4a6}{W}Y&s@tm2w?Yicz^Bt8Ks5g#FSDDhMg){NTKVcyD!a z8rQBM?-?ZX-H!D@EC>7TuSV|O>>oF;jmBw!$@hd68{WK+A3r{PQeH?uvh472NHFXX z7>Nka_ai?z6-$lnp&#KgBo}jXSy9DR|F-+m@c4$l1nwpd)EmgNu#=?So3d$3RieuW@ z4cwfVnapSwa_vy<7_L$tGbk#zZ}L9qS!_Ekw~Aq!0e}&y_a&A~6hU>jQ7)iOT9MD7 zE_(uBu=4oAr)9IXGFk)dy9^9WvJjlKxw)L!h-e!)|TSB`Ph!A$q|_GM&r?X%<_xNl&^&i zOI_CzK&G<2k)GTp zd)`*}06NynYxX?$(q47u1^J!&92Snfp`$bLL(jDz-m6djxjKA1`o5Wd6n&{vFTP0DJ7Mh&Mp zu;62Ai+i_&eVA&i^E>j>7GMc$=khbOyaPP^YOF@$KAAV(~Ev=U6~WFdoiJGn-F?q83-I%04x@?DJ0nG6uTw z$6m^F!&y!o&eE34%2A4s8X`1>9sDSb=T8|V9MvsMA5gkGlOY?3j|S>?3YI*sm0V_$ zzNY6my$;SMaRfJxEanI_BY;EqqOoK~F+RhT1hU(NV+5ITzZVQkkyCt%8Ab{EtkHce z(X+Uc6_9LR*V?j3jY2?6Z1s8~n{=jf?y7}{%@5vxO(W#GQ+0n7YRr=bhpAvnoyKr& zca-+3{}iHv;e&K~44Cy|;t~m``R4Z0Td;YX4sI6x`oil6KYu2@JESB3x~ZGk#`5US z**){_B&|GKlDhZ)qZkAM*5bMhsREj7ivwy=q~#<54&oL9np8YTx8`|uGgm9S`G?@-c6nWmRFVW@c(jIvSn7)Fmd1!BTvd9V=ga(D{4m&I%o9=@AzU&F-3FvpmZ zTpX)=S>E?F3xbjFzFO&d5UStPJNO_aHYIzxRSb*rxca zubY+|p`eRBq2PDWlRChJn?Y#8qkrSQbZu3AHAezr(?YxS zNB9o#Bk&PeHf4m{6oWi6xBMibgGYkRK{uoLUha%oGCb0c;?IS|zA+)|x%;IGtN+KR zJqxAXo>*SYPZf|bX{iq#XUB_29(Rm+*2jI2rzeMcr4RKqtAwyYI}mkS4~yLTQW2kG zu=4yBl5+Tty_f7;?ps(WBYIk@tjzKL3mocW`Zb;b488Zp8HWP%_)wGD86vYqb!RmU zcPLg<_>wC2)!e~6=>XFOFQ`A!BU!1mUtJ9XMdLZ%5_H!GA9*HzE`J#y<8WV$uC9cP z*813oNB88a2`iuUoqDa-sXBJPp8cRVs3GwiO1Ju+$c^nyrZN`w^GO8sLq~4o-glXv zj#&8Eva~K(qRl@d+_rR*tbji$Wis>Fug@%M3fby1m3B}|w#Qj_7A_7gJKS3`Wv!*+ zCzsf`x>oeb=M+P!O16(1=(g43h_li2WxU;EIQ_*bb5HHs@BMW;y4R8@X^9x$#gJw` z`{*eo@98b}Br(1R#+5{)`gNwela%a7+oz`%a{_o9`wg2>KN44!6ZwAh5>W9iCW>{> z0=Bz*?AC*Y@wPfT7yZ7cU&E~Jupr>cnxmjcXlfy(&tXIQIqvbG#&UHaM4y+HzgIa4 zJ?c=kRd!lC#_lp9v?EsW^jQm0%<-=E(EZuSvy(XCtp!H1xdvjf(xL`M?0A}Serj1< zKaqIoAx!&b@oAH&?j75a(%YlsE8pwuyQeAbZY3KIs=t#GIHC=>Kem6!KlF&ax&I<; zH6P?|WaavvQlt`+vfi**A*QuYy!^=J_HCXvv%c2Dh%AD_v*4jPFv@yBRD zxUA7{Jd;qcb@-FS^r&z=XB;aaK!P0O^&Pq*-_PPMFx;~v?jiuWkv{Y4x-&QsLs(H^t=c+Nzomu)kd^;kh4AjNmg+M~8`x>tM=Be^2Z zlGLs}bp-O)DpF*8uC-n*3mX04Ro$~fQa(A9F|G=*Z{E*oeUW`lc1rw>`T=^>Z?r9` zprG41L*#Ru!-H(a*O9tN{(;V$qaj!3T7C$Z+W!y&pqU9j6|cUQaDGgY#-qo#{^?lB z;&URk&nw8*2J84En2n{GKiml%8`?6VTVU`Q*e?bqUf~z<;yrHlN!yg|;qC;pUw;Va z=o@ez&O9dpS>XBT$>YFavPmp0DE=~1l;B&)z3tK-4j75lRaf8pu%R78tvJ z5<%n@Iy`GJyKg(jGP>sa91HvgaUj?Cjpq2YjU^5Y2W&SUO~ozq{n%N?mZUyxcc8vq^ z)M44<0jg3EA~Mw)rtiF9TC`lnSs?n(SnwWkiKd{E`JEACUmfAmQG--g&ckO49^R7| z34AFR4AZ^ZT<2ttR}?+Q{Tp2OhaBjgmPLK;TVY_7f3-)2$Ee8R#dwU;zKZMw+{he&<;2^|mPm12kNf1+$W>&f*R29b;1c8-Ov3!+ zx$nX`qjOoC(BL_BXCo{f<*QQ#7r57g?21L_WT~tN!C6)R)vsj0Mx7kHKlCA(3Hv-% zUVf+vzb4Q{Ur^v_@#e@57r&-`b#eIpyB?RK5Wfd>9=%t~1fp~X+wP{sf%5D(9?VbD z#U+b2oBLR+XHmQQoZW9Rh57E>V|T5Rl6VL`IZ5;>QE*uuecNjJ_T+2wTR)74prhtz zb#3*S%W{D#0J42qAy%Gj;Ar*ljCAA`A{0W5^Qoi7^F{e^C4n;};AmmQP_eMK_@_|8=;^=bC z@$Ti9J8Sjn_ac+NJw2MpZ8fKR_v4+6WoI_Dv4*h4}K(Ag7V&sC?Oa zM1GB4RGuJ_^TWc*NbdEst>Vk0olK((Yu;YjIuEh}E_@%Ib3l)0n{|Y0TnlS-lhV!b zMfZ0_Y*+U==a?8~(88{3==<37%0UNuN&IS5 zOI^)^OA<#(^C|8}p2+g-m-x3BANXwa%Wq0yAB99am%a5Xj!8+$A0%roaJWuPf8|v{ zybze*AjH|Lm(zW3S-1ezeL6*L4p28T$5HFaU-Oax7_#d#?~1*+pC6o^ZH#3EOO{t@ zgxR*=-fq>>=$S3k(CfM5oD=3-AK$eVq(9w=W~-CD~Z&JjvpF=>$KZz3GTu8i+9Avy|M#wf&t z-|tJcAN=k$LQI%;o!ZdlY1mW45rpzq$b(qg<}?9Bi5c^7-t2nfkkQdwuQPc zA1*VneLb03M#t4CEDUt$E`f=)H@|<}=6c2R&ZjgP!=98YEK!oj#drMMEPPz_@1JhVxrll0Uk z$PicX@_4L;OHfMDGe6&Xd^fT4=-tBge0%e7+@ZAV1pN_TzF;?DpP~d^e(pP5@!l%) z=8_fc>v=9f^FxrLUuVpo{+hj<;6_XC7l~?_=kH)eZATezh$=KdW#_#YBy`l$*JbM` z4R5wJ^y29HCi$FH`fdHOFW#^dlHpbnYDnWdBh zjiPdH`G!T2sX-!Ys%7yQvb7|PGOrrHn0Ezi*`&F94;J267J6~H-#WH!HAc;%x3jTX zac4|4>;n$L%uwRqi-#7;_Tj}vw`oPc4)5$U_=?_ayOR>l(iXYwjM!z<)HCXp;D90e~#gj@P&S;yOIYMe^XHb`eKW@l{Y&@)ff zRBUk_-Wq(QH*%OInf7DO{cO4y#-gmGmo~Xw6dpf065&Kmz>cfC$yX(lxq3VEz0BOO ztF)K^RFr0>9pU>H%_P&nv{*-jm~%wwW78nOa9QY1m=P(38J?sj#=VWUwri9|Ok1ETrHeBJJk&Q&SXE>-Fom*a0n?NW)Ju06hjU0=PSU!go0 z0IExTKkKS_-#f57m*VpLLHQ~4*ln$M6 zm?LM3INkmVK~9Rs*2^7X*!7!PeunDlTz8oy`syrmOE!diJoTS+$eMEY_9*UG)$v4( z^R7A2PF3W^4H)~_KQhJVXxydpx%*Z_*@8IgqR_p7JTRirneR@Zfpv}P>X_gp31p_b zrk?BKg-V}yO#boMU&a*QHRz=_)w$m_FPtE6yfim?P@C(ZOBo7diX2c~a5cVmW$eLn z{r67EsN*&b_sIGI2T^QEr+FjRLHd>2Pl6AGbFksUbW&%X6ui-iT~G{Yix{T=Oc6P> z`tVLMv5rOqRJBTR;9`0`nX{ag|(L%FoM@v{LO{Xu`p=zr1TdOn19> z>5v_O-%|rUiVVf4wLY1K_nn$mnsEvV|n`$vB!Mk9`g8?i-2WFU_kq z632yhKsGf9F>VOX_h@yJ;;$EvI-^X@%x)AJ)N-Q8cLhbC;A_d?!m5)3T|aIN{Bd8n z1k~S-guc)4Rd}^wE0O1cY_YgsLyQi}`_QsA3C_q5XnFeu_P>vRxH(=nGICWOIY}vzg`tFSZZ8(?`JjiFi;vtaX_z#=Lrugmv$c_> z1l^4GmJoE?vZ*;f9Ux$fCNjagqG<@7T{@baX840~66wr@>L$VRjbfCN4T!iXVr5AQ zbNmE=buPWWlSZ1@R$x2?J1Kmosi#-wA%*R@GK-f~;g-vzeVCL|ZO^oFD{`qReDt3$ zhsJdPLZ`$kgv=+lr*O3#>fDDl8>#dV!n1NvNjnY$W+?wjWXTM~7h)oTym;BNff6w9 z2W(S%BW{rJKG1t*~Tnofq2$$Ck&$m!S`m}}J=<0wVTUF1VbAZw;O zUGoIyH$7TrT&$PziUuE4O5x+*XRJHB3(X$UH>%9|WB=|^;8l*<<;J7{^pg)dc2ybrl*@7lM`fWUVg3Xp>BwgL^(vout8Q zH&-RV_8!(oc=FVu96sRhSKLqxgQkY)9UcgG*c?u`ayJmL#(}KqS-R;MP{tOS%?F;P? zW0;4-RUQds<`%T&+*$hT@qfQSUIuc3eih(Y#C1F?z>c<>goMnyHGNoJy4olt9ZJvq zDqQ8WuPT%{+LGnclap_d6W%M=J?>^iUg$1;AlVy<*+O`2eZ0CN`}J#Ktd|%{bE)L$ zS+Kt(@1GYiUodX=1oS%*!`~7{-F$xb$S^~oO%2<^Q~S!juTV1Qw!o_Gct?L(@n>C| zWLJsd_y_~R5mj=WOf?8s8*E1H*G$1L@ADsjrdy#YR(Vm~FRyALQ2M;Y(Oa~HoucGe zt~T-LVm{QWTlkzk3ep{A$D2adZdI#19|;CkP*svY1R zMCETM{PTK2QzUC_F-8HmC#oCb`?eXdzIUyXRgCi5B`QlYL;)t^O-i8cyVUcCzhACP zzm^LeG~Wr~PT%VQ_t#mq`$~b3Y^IRH3gyrzv7!W!l=qM5dv>mLZ^q(3qX%gn6%i9J z|1?eKLBQX);ZorF{sym7YM+nUcRK&6Ou~fn9L-tc!mRs;pHlT(F=CjQruJas$YZvb zu^f8uakcLuMlR&TsdfDiPJwmAM^J2UHo5+uUAhynL$;(sRQYgWx<jjc7y;)PM{dw2^n^xz=4WIVLV z`}wwfAB~Pm|yj+2^NYZYI!Y4tT-*b;} zvikSj=&fMofuX@-T9oE3mK6~9_n9mpSlOk!lp!v|Y?jXXK#GkH!E{yfRyR;{~(WUFCVzmiCQy~f`!G=Kq$h7llg zfNoOWn>Q&B3|%Uzczf?TE$PZZ1~;`P_Hi+T7#*Tjo{gvLhI*9zfc_Qqp61QB&uadT znK~M2iauV8A;3(+t4S$Z53dU}BjcDkTRdvb%si?6R5r*T(-KFQChMp_K~d5$V$aW9 z^CqT~(clB6$V346nMTE@9}V(cH$iy9#=;_JucG}diAzUE=X15|Bfxf)IGfEH5)l&9 zGHV(V7WT~F-#;@A5R>}(?CagS6@KbtW0Nl(Iq}J7pO$hv^XckOk6N-B_>IU2Fulrc z7o`V40p?v0@0wR#LI!6jEWGR7Y8f1u2b<@<-iiVYPhOFyk7+rC&~ZZ&lx(;cuQ1~G zg2wjC-I|09mwvw#AjyO4A0TZ+&Fr^~8{#W*z`K=vlgYG36gyfDxsTc5C^=ZxWcW|+6`vn4-D>%6LEWFq35FyHTgMt5 zPs);^CV?gUQfX)vjyc$$)cw1;{B@lVi+ibRwPW?Gl)(z;11k)wrDJ3INkZ;uzHKXM zCy+-0oU}Krtn#w6v(Yr%**Q5KueQL5HaL2&ie6&wV)>d6cN#4alFnx!F@}sfq5MBQ za52;hfyf$ZPP07*Ap@Q(;pWcYmEj-P_vruOm4FUPd|=(4=<8g))$~cb!a5yhQwbY| zs_sAoaoamfWYH&?Wg4cw%()pr-NrZ-zHU<&e)qpg(2Es8PqRNZ7%5Cw)2PJ1kyHc} zY@Q4|ETmTL^`UeadT9n+e>!&k)($sF{}MW8u=?nZXJIUGoN)Oyg(q;%ydDpax)(>L z7-SAdrh8%;zw=~JSvLnxOu|F#N_AMg9i_G9FFZynJ2^pD;6TM)*2&+C_UK|Ake9P;WfDd(fh&#h>HT9%0ras?x}iCE!DJbI78 zsXz07ociGGVZ{LMTvXVGurxD;3h4{EoX^6@eCQ$Ome>b%KBW2ieXa*C|KZdA(}g<^ zIP=h)-Tv6oN?>4`YDifWg}-~8w}KSJt&e}o+Assy1h+e{p(R8h2yS>@q5IdbJNN#9 zR)KR=dzmrb8X>?U+Ijd;46}uaxY5H;O6HzIa1LgR9Zs-&Amw`Sfr|??$lSOD%M$zx zLSs;8L<}Rq44r0?4$JWHa5sdjo2wCi-5f6gqiAqnXTSRFOe>6AMlALZYX->Ae5x;g z>)baXd*g<2`Y!*HH^F}7-`|Wv9)%Gs1!WcEzx(Re3)Ya3kQccrFQ2i1uKU;*FD`L$ zamA*j@SNu{L1bCkz%W$e5HE3Ay*GGT^*xC3cz9H>NSE?Jc!EIvTp9>tS~Mt%dhAo{`48{!gEN zPn&M?%Y+WupF47E(G=YR@EjPvux@5}nv1Y>(@rc_Hz-K(6hNk!=E(>x}O0 zgZvL%fro4@BpiSdtTk%jK8z8T_P@wD?_V`d4rDz zqtS+xJcFA2N_UX091Ol`Zm~z%<0V1R{(vllq$ks#4$$8ts`G1gmcLdFE!Q@PU?Ut@ zrUbm7w7Z}5rb2KtI5$dALFk|s1mdRu8r7QM1Q)52>2nGIbJ58BJ99CJ11)E%6pa+#`%{0>$}^eA4xlo% zA-=^^k0o}}M(1L=wF-m9>4YAAm+a{3de-rbU55aT@ip4fo=^Bng`b5|>K|dOt z{Fh{A3b%*aw_v-6rF7JFNer5r!F`M*h0)|@D0Lka5NFx$4Y*#>Gg-DyrjOfUJ{D+M zd3&f9arK{$?cYsC09eN%C)5xnGVdmilKn};o3K%@8vInQT_I%NCb2^Dx&N3c_3{4_ zxHu-%?7}kBr6NpIfKR3(RW@beDSs)G`Ek%qapDW$d~@~`NpWm0N5>iy?W!G+Rf${O zLzHO?5AH4k$5(ajSs?%K;%<;4R00VY_BEteqWCQ{K&RjlHzwY3hOu?YrxwC)d8gIfcZVN&zV&Sf8ii`SJh%W85nm zJ`+aoKzG7Tys~16utZC*tw>z%CX6xajS6&Bi-VF$qm{rcuqduMBLlRKc`&v{^r-Pr^#E?X#QkZ&IbpFbSFA) zThr@z64iWMr>g7{3kiJMobLaM+59(j8WaE3w?+RF`JM%2Dp8Q>%S&&W@DPJHLC97f z?9Q%D!vp+Ddj&zTuwAz}iKDYYAQBc5Tlxa=1BkdTOY5Y9Tg0_S+?ZlyFQT2@rktj4JSO;&v0 zMx%7BYVlnos>wBe8D@2L9HqrWD0z}qZ5%+)HDb#B_Jq+lfCq6lj|d;LMcHbsO=@*e+(gvt^r{GF|G93X zdaA3pj=iY+dpF+P3JNj%bs}jHrr!?=c__5yUbhw!u$?VQ3>U`q2LsU9Q5Z#9{LLWR zJ$~3S@cXFKADo$7fT;MuNjPy9N9$Y>v`q3JEx8Y%`x*&2fphyPGB6oag^rF{D(e41Ya_M zlS(Rwy%J*vq$h7cx~BT>p&C;SZ_qH!IioIH_92ClmyfcFu_;SssQnDaZbkBfGE2jj z)(!6YcMGbBjX)6*i0g*5&F+F;y-GGmU?`;*bxZ%TwTG0 zV&zZ1W8D7w_lcwcP<;oH>@WNM?ZYiDFz^zAMRE3}5VN@xpY(9X zn3L5?f5W|YVqF?6AqMTMW?AOgRo_(QU{BZd&udO}=rmdCbQ)%x40|HKVZxt%ckN(0 zBS-Voi`bZ$46jX4GwCe19aV>H5?w1&$qH;C$7gEC@`aNzG8Ua}oVgL6MYTnf`u&-G zcu@W#^n4gDv~8K9qKy}Hwg5-K?dNOd8ipeA?d|v1);?{~OD&L@1x75k;EA(@*v7D_JHb+o-Q)tA%#19Bp z!bOLS$=_=R>%I3NI?v5Sr_Cen8O4G!G!y_xc)UQ$4+!3c5A^7A^5?f&{27=nZ&l;C z9Q&YT^N`u!2h{0);C6VEkd9ue#8LWE%xrPYmdgxS!gT&?4ferup%KJ6gAWEXj6n@% zTdo&UqH5WB&nWLUfP%P;H@^t`lB?p9ptMNC1eUZ>2=EV(0{`P$+8s~Y-x^BvSsg7z zhd4O638decxKzNO}@UPMc zukpJC$)8Ma68(=Y`uksP(5)yVP`?!t|8%W!Pzp3u_{qX2>-=2Xw(4b@El|J$c2$&Y z#eN)KpPhnIF!2RF;%E_TQ$5WDbJS(4uS#SW$XMA$LC#y?yIZY?@6M+aBb?yXUOMTR z|0x-NJ@!&K_$CN3{53kr*WYybv!ZkPh-MnLmGLY#7Tr0cs;XMXmy(dcp8Ucp$Clt9po${_%3~>``fPaN z$`KBK_5%Fw7?+O&FN7e5CwAGR$jci_&)t`4!syQ4{ovHkWP!3KF`LHnhR;cFimvPj_5<_jKx%0ZW~x&0=!DZFXy}%kjr&wiEZMChRJ=&CIWP zI%)u56N?Iaf>Ij{+=3FR5}|)|Q-Zc-Ko9R!tmd&7c+>ife+g9<1zl}^>HX! zpm%!SEfIr!Li|Psv9mk4O3!g83Nf*05cVjnPTN=jy%d{rMDNP> zb}NMfPJ3O!@<4+TpC-1~_U3e2Oz0cfxUUHaNIi(3O;W-cag~ZIicC>1^z4_+l7ME? z6AO+ur*5|}fz)?t={pTH?N8%@ufd~|l9I21HUfZPy;gII zM58B`gP1xV)KrYL%^rhH_ZlRuY551F1Z8DbQnCyISi%VgwI1;>0GtR$@)f62<}dx; zF};Ki(kihvDY_tnSpt4nBr+E!IpusylA)PzH$vJuk87ueklB4V$Ut1F@j5VRmDEH* ziRM_Y6LRC$*A)_c5UFNv-S|}iY&f%6A5Wfb@7eCD*iubgV<=?{tt7nB&NN!es+a9L zr|d8JG%16*r3fL2L48*86PHyWtpN*)#jc6yCu&%Z$Lg@wNszz&04x1!(fiHR{QRCf z>Kdsv8XB%Amoiznx!3ERgb2j%O08o;VMquD90kEbK>oNyl3>A+-}!xWra*_z=eL-q zt@}R%!lRTI``(P+eoII&Hz$r85Xwcto^p8sk zW)$Re5N5V5j#4~-)>$yYC}DJIbM{hqwYKzr4r{O|F<=-q@7>%f`p+UUG_S;_5 zm=nm=tt*Vw74PY}f?U!y-P%dKZA*uUVvtFgnR#%*&wJ)05e-q$bR{2Ku|3LWGGQV5 zA1m_L{o$4h0FCT~OuIm~?`kKc#M$Wa)SG)@Ek{QkM{4mHrAAe@9~B3XC$Vz_GzeN3 zZ6)FcePlCX6axj15K=Y7ukTIrjTcH}ZLzP^ZhVpdd|4zd`QfHl#w-O}@e8lvVv7H4 z!haqKw&vSy2s9g%wFpioWpUa+T$RUdTOKLC_wAZX5A^f3Wc{Il2+Y~3EXA$qR)U*G zb0%Pw2FIgMuKkaV{o|hLt{5FTXKI^wQGDmsL%uylfjTRxWMvOq^Q(%8%C zqmG1HJFg;@67H@wR97mcZJkOz?UT7CT?q5JmE zeLCakUJMEuNl}l-#f6SO_d1Vr60*IVhWt254#51M!u-cK8xg=jl5ZCBP`a-E=9Mc& z>hXIoaVYZms+&z0=n>_V$>lVNFxkEc`1A(_ysx>T47(V?7LTuCMf19CIpP0C7>1x= zA?XPa);*Ap2>dQOdfEjqkh5YK2h&uXi)FXn>JB9s`5)?!{L8w>|jYvGhuWdVayy2 z6E}3k-2dm2fKIB2$xGt5jyI*Onf^Z%`90bKEQw2dJsqVYuxzX!R&x(F_0L`wy-7%V0H-{n`iM50n#! zpVCqAl9P>wN9FnM2X+ZVO;ya>%sMxQ(*4Z1QTH&oMKTp%yvpyg(&9=j7XIoKS7UhX^j^$o}{HP|0qwT&4asR0lnJSYQC3DE> zgbG(+KYB_CXTqvoC+<3B`qP^J^OgGm_D6Iib~FKTG(ello9klX{F`2JC-6Uho>FAU zf|<>P93>=~A>cH%jav|l+hn?ebn-Yj$PHjhuIPZc?=QyCF*0_wG5Ul<{4le#ifCQ_ z#gKzX-@B2X^6*|2lsg}7eoG*+SCP8mXUPsr$IjTol6hx7Z!s+&J%E1^PkH@E`pcuQNh&_KMN9SM)--Ed2Ceg-0_`2w+94XCf|71Fes3 zDap1~>EWt81@{iyQWhImoCh}{5t3zj(i|WfS zAYBlc*<%GZL<9#eM9xuFzEho7kvwaons0}|{IQ?+(XMTk%j!iMgnxR)l9LVLf1ifv z{qwzz*1bxs1P7F7Ze7p;>f{LX)cx{x4IHI`tvOOEU_8u%ZumsTmWwoAG(-Kw>Jw!# z1#{5>HnE@GqoID7rWXuF=`Q^4&|)uM#t4>>+N!@!3@-?DIy?1J!&u&~c73aCqSf*>G-lXXY%w(NONv z&UIFnA<63NR{UT`m*n+h;e#D{VC)@I6;0>5qC}rw86c zGW~*1nNVexvrA0)x|477Y8tcxBB6ad3~MtdR{J(X$#{jB^2~a`Z4#;Ym1K8 zG-u~242I4T(6a~ZaMN)jD z@rxk~2SRj2ty?piFLGBZ+07efxvc9-my^;K(%5swpZa!ae5*eW8oFddepV_u zH{|(>t>|&yaEAu)N+ggr^cSmCjE4{ZJlqtS$iU(D1#;?+6MU-`qCda5IFPII#aGa^ zvKu9$QPY{bN5r5iiqQ@R-NPZ7Y{i;*h-aHEV>SNiGXUtHVi@Z;dQeL?gby_pC+{5% zhkQ=P%pTO#Tko+2nya&u{iIttGzO~Lf2u)b&W)1vIn}qDsJo2=0}LTv)ApDDbseCv z$MRsIqk&a8(mR{lAYU}Eu(lN>{hg_Zs$$KkRRKaT&BO2Hd`ww#FnzX zbSL4e`iE`^4UytUXy$JIl)WX7?x6uaQ=Zkuu5T-MWrS~w_w~faN2p?drU|~KuN8Ya zUOa^Dv!x*Lci9qW@UNVg|I<&&nXQsFeDlCf-KU zUHG|dC`or7FkE0dOl@W%_j>!6+TSMX)1upx#LSMz%rXm9)HwQPo3?f8QFCUC6|MLJ zxC>Ps5FK`uB{E6^8S1+zj`Xk*clq0+{-gV73-py4t^as(G6c5Zx;)=P)6GnUv`=9l}e@Xb>$kbh(hNenoD%zJZ+#wjxg7>oAXr zJ?PPI;uYk5^fx|tLy3zDVsv3eKiOOB7&P>;0WbJ|^iKdR2d;VO%oa-pTqg~f$ ztg2mAT4waF@oCiA zEQh0q$EEe}wZdP-9(`{gt#~v?%jk)yup1Ymp(kOKjst^?-Smj1t$VI*-~Gt63m2GC zz*_U7SnyK$JKd z@cVXb%i`smVFY^@^1+G3fvKFJ4Xgdli4VcpfvtRinA|kN_VqV*Wh)M)K~!BZP0GrW z!EM72X#M7E?4n|C1YDKNs0p;cD3|pL^TC>Zu8Wb4F7zE4(wsONd|=mm>p@dV23p*9 zQz5}Gl6!J;eGaHMKLW|Lcj`Q}Y5x+tzdP~iPcY`v%w2u_yUN?{Qz2u;V-SSdUGSf_ zz<1bF(QB5i8xMROjR9a zU@J9{SXM_D7xOI{N;A}AdF4yyE3FOa~r%R|@Rk%-${96S{`8)#kJ1qhXGc^gh;( zyaYOh@M5dwIDOv}7}Dso{AMHhNPIOhg2mt#lfldG=1T{u;)UF)3tfjD`%JmE>$Xdr9Z`C70y@>0**~x~?zg{67!^m}!f6kqay*)e(n@G}5zaBs zK9GC?n!Q@ky!&6?^q=X4L@ZrA#99N4EKvudk|Bp3YM`Gyfrdd#yRj62R!YaABh@zG*9$Rp8C}Cc`vVLh;7yD&-^Koq zk@~0QC}TT!v96V2X{tc*n(oE=Vaj8pO(1Yw-;!x3_dxz1Vc#83b^FGDj$`jpN!BqF zqL4j~O_7orLbk{zG7ibeI%Lo6j1Wa+6>{vol9WBOmHoRU_q1 z-`91$$8~)w2?HK$u6KQ;huhkf<}VM88EIjmGZopv-$Q})8y{Y5J^T6d=f~vErzJq} z0NxBn3y~9+Pf!|=1YS^9H~&|FX6&JA%PFT}Tux!tydHqtMlGsBxR*DLaF?2tU0;ri z4ia@kqPgYFYdigp$SE55#Sgg^HC;uc7}`Ma+s{YUNhJo;ga)L16|=Zl|A6+Gkzo4!uO= zVG1qRsWWW2W)@vQ+_AYDs?jv~I}FgYFXBOL|7rTQ=AzM}k7} zSrfhleKchLJ(TY(Cj0jaM1t(Es_w*}m?ZdwRhcT-&t5WY0_|D!RbnTMt~pXyG1vHP z$Nib^;kTRo8*!$439SN2*Kchk;2vhp_b*+OYRm0&=Ij%Qk;8eFcR!o=KxJIDtL@JG z#7J-L9`4_Alo5sRI`55iY_JCq1kWw)4`m;CafLtiG7rdT3L+|64Dx zVJvcEr*NQh24|~o}?q@0jHWL><4Gum8^Dq(WpbA9HWIqkaq65xH^&OI&S)v+FC>-YKHeE#)xX{* zX~k9H{0(B|A#KZ>)wS&OBV;!&48}^0kldM~0?0K{<%5Y6RV+Q+gK92p1Vk%Fc-`8I z#i)E*7G>6yoRR(@s$E@c>P z2OEE)(>Fukq-R`_Kl7APFH%{A=&u$a|`J zx&*vgAQhoj79Tpz^58i#iik1QPL6uK@4zCvq(Aa*_}5DF~{h8UDz$dAwpyFsm}O&w}9R z&S0_6TR2jstW={C5@n7R-5WRayDCa88A|}y-6>G{ zaf68+yoo4n-CQ(1ys7yL7TTk(TKL=v8N&vD$|Q2*>u2{=h3yt*Xd^x&Ka`bPd%l9h5PSd%@1=JV-6Filj(q+F2qy{V>5{tfT`G_Z!iZPZF%FN~OXZ#E zUEHPsD(76d)Y24RxD-UX>EP3cfOVQ7HP$-;plu@RHEODK-blJVECgAq zGoq5#WCn6s7oKxUYvvHVIdhf%H)5rHy0>Sp#&zJ3caSECA^40N+Y45~M%&Z@&pq^% z!)gW6F!D*fMx~qa=4z9=wh*-fKTFmX-$Z!=QKq=$B(q|iNCa| zOOwwHW2F++5Walcu7B%mCm%v3p`b7{COphMBhb!2jC+HHNUDnkTVPK%&T1y`n|9o+ z6o+oUZR%;D#4; z5vdxDTaf=rGdpf>-IfZWVGD)^QV55xUQPlpAhxWxS6f;*Ek7uoS@D6ZIRn=1g3cgj z-~9MU0@*IPC|&$2QCV}dm;Dzr+!;7ipEs~j>y|<`#fju_BKm`vIbB)Z2?`TLE>Iy% zwa#(9m8U=mu05ZKKOtD_NW;k_gkY8Yyg`%g>r#9cL4>5YcNENhXLB|OxT&YaR~p&| zlgyZA&0e(kclk#|;wHV-P2d`5JZIb}hZk?f?%zJE`kKB2TZCFwBfiDQC^dxJ<72L} zb@&UJ>raWv=_`mku?S@7WozcZ4$^#_P70d28bx7Q)~sT76=!JR=F~WN3hswdq+im+ z5L%+*)}ohYmM_D~{d~+KiyURPVVlOk`(gK@;731xsv_8fi_|%YDLv*(IA!ubpVl2Nls;+(%VHD4#>35)bA((z0H% zvKz|L)8|nb&WOOv#K}+VK4EUwhg{G>kR!?qpE`-2@TxebD3a3G6M~EyQMx3BpO%f2 z85QVSa?kk{i7=j1D#8j1i0-0;rxKnk^CauovX`KudWIEL#D6XvATvMmjBr#B2bGMo z!6bwxc+z^lz=^5Rl(S2p-2flnCeyvuJSkd~7*`L1=Vl62{o9-JfXAM1Z3iW6h>2^& z5WC)d!-6`7B$|ql7TIpiIUQDHX!B&{;{peQLO?``iU?Hex9R7}%8th>IA*HG(8F!Oq)M?&fx`-o7n3%% zwtn3)E0hS0G8`_odJMpMon~+~ia@xOnUZ*yvgWxJvh2c}d^M zxl`-649mR@aRWY*qH?JFhj5%D#MZ8()d(u1|Rxf$^J?2|7E@MR!9OA#S#FQYF5sbp38!-cc-Hj3Y9atNZl!+&29M=a>R3;< zjxj~oSNW&--U>JY8l1*3*-EpdsPhrowzz1`OWd0mc>7(En_fqE%5m>8BfmgI+}yu_ zWe&f4@-o*K5y3;~;6R7cp8_&vZ6Ou?(vY0VDb^=4{sFor_U`TnvACgU;v4|S(GyCh z2N|E~7jAA-C6h*HP)A=^n(7a$by;kO9D!GHfn2H)e9JSDbouF~vLI-{H1Kh%H}V=k@fv_V3x=_F?ZB~9$T#y3n;id|QGLy^TctkT zFQ+c&q{N^!U3`xoXa*L#r6u@NjgtinV3!+YJ{OG^KGbL-5X21_1h1D7T24})7XZ$- z=l)u=Bap)T*65ofYicC;2i%8T`>B&25kZ9^%UB(l6_z+abv6abV?myx zhr?*l&e>F1trtptr^`vBGt%#RCtf3_z80r_*6fV_VXP@~in_|?b|gjA_=^=0b&!b9 zecE!tMyW~e$Q2p9Wd~ecVCtLjYXpV3w(r>r6k{2f zHrXk#Fx(z8qF)*re1>AQ9@$OeyHq`ksil*uAW4&*Taix)4T$y{#0R{~UkQu@qi5`g z-hDEcM<{6TyRjCY6GRV)FF+~CYDT}$K*4RocLgkkS74*o+3PlVs|D6~FV4*aK9vpW zA@1Axt(Ii@c{prpdwJCHV0SAUu&$4@-{WBgwcf5i@z-rJN?i>EP?-hvaP4;`-Nn@< ziQOOh5k)eOPG1Xlxq!694Vrz356`z=_xp7aH?Jd=8b4DwLH@HTzFeh1K->)>1PwPP zIHJnn%%vA;7#?ptpGM@hZtB#@nyCVlG!4V_ji;5Y{XPMTqvy%7lsIpa zKN+mM)+-k!94nA=HIY4MCdK=HPSf{~^ybWg-bKZpwfnWUdnFQ}qV<);zhv_nPjmbK zDrrWqL!hQFA$R?HXbIt(V6*Ktf}qeo?TEKt!ABoFWvH;`|5+Zk^`(@v5vYn5P6RP9|q>QTTZ425_+|j`RYi1+>WHirVrhM<9 zfL;}?AX+>@&o?pwmjNaA*~cjaaM~`&)Bw0+s!t6Z0n51Ru zKULvOYCpg4$82bOPhV5~IynLj85tHL#rqwK`{4*zRT7;-!v8!IH!z9!dJz;>0e$^# z&I@TKsc)rQ6$AB`ejK`y<>s&G4-#3XtQnsw#PUttP~}6|1AV+;5%2Wg3EU529Ncp$ zrOpD_Q|&mt<=BG-7HWL30C$64;)wj!mG{#Fgv;Na;SPd|$R*^yi#?Xtc(m8b_=lg4 z1QAKeSfI@5V6YcH93SrW{n|F!fA05BR`o3c3bXSH#NB0cy7KVmb4X0Zn3=u)W`W(S zjpxolM%;8RA7H*Ued#3yU<5r(u`%qjAvxm1)0f0G=?`kp*RHf#3iGWP48;KLW?g#I zDdoF(S3AaS(t~aBEe0TUtHdY#NcxP4s*osR^}&&GR{{ym~LmMEmqw;dsQ?WTWiRWb&2e+v@!&1R0)^|$e>d# z3&i31q2c+{MEv4}v0>NwC98n7ysA2XnjS2)>4SFnPHu50<>%schr|)(WKn`x_)K%m zw`96UU8T!ckPw$OF(mqbqtOxsdU*5(7S_&IgjwVoNUz(^gbU6V;f~QwUoOzl!eD(m z0UpMWz=e&Bjth~7uRX9yrhmGIjThv4*Q}H1gxe+`<^&0Hj0iqR1J60H-y4b&Bvfci z2fd58C1rl@5Hsq#PXCEC|5QNwgy7#&oc&m z%90XaoDeljynp8ON8MBGNyO8n*eGp92~cP~Hg!&fs@p%0haRo(@AAutwnAEXO-Eia zvzYQg4QfMCA4pN}5Fps(Yyyi3Pi##F_TVmn>C7j<^!#6e=AIruV!gJccT_66v7k8&XU(&C)h~5VX85@@!tEvh`}wl}<=cdqj(zesj!MOOB$&C@9}OOKenr z@wpROlml?aI&n|}%3)MfOG_$ItXBG1_b?ZLZB8d@sew{vafft9@_K%Nr z8IC@GG<9A(+fd`8p{L$(faR1Pub$84%8xW~RW%D*+M;~v);8)ZH)sJ6tW}>44EXw- zN{*f>btR!o<5bVOPVGvP`)MWTWB)<_mB^etVOV7F;?<}e%JVO?lh!%MQJFErR=9f`u;}m1*SRe&ArgJ-?{Ej zjQAZn>T$p$0foj1YI?@+vp&Qs0a|*gA=9X5PGHjH05Crk?B+X!k3aCivX~o8IyctM z9=!4j0K`E1Rij8~R3bYEX4$ny@3fqxSj?qDu+Vog;NNZzVwD7SJWWi*wHd*bW|zEq#9FQzBzU?$wR+XVyF{xAq}q+&h{Fve{;=m8^?Q^Bc2d~%SgCTLe9T8uD(ahr- zSfb+M(Yb02Z3X$nsK+!6IBiO~G>m!+a#ok?rP?5Q=+*d*Y7ZI)TSfv^dH}PnODili zZY}SdrxKI*(c#1Yf2e9j4D69;)^J)P4DJTCD@sp4pVL(HqFrw$uMaRwX^xuv_jf{W zcEu@~V1%Ebu!%o?%SX<%8BtV zeHvYCmuH`*4ZZqN3wNBGHS2*vv+{41(P15sbNHlhJDuA8ZyEJ7%HaAK4j#wPH;A`0C(GDe9gU|z2D z+AG(u^EP02Zz;G-P$~$i`KdC{H-eFFOlJBR8)3%e@a>tOH8ik zYyK5y*!)4eo-;Vw`!RNgVv{E2X*=5?vq|R2=c|`fW9rufYg>MtB~cUx;mXdDv5e$| zljm_B332=eXyg{)h900oFth+9cwZ9M`14%P)!^0oAoB^eV%SOq?K|~c(40uH+c|re zmY0JU-vG0eYWI$*3IVsRCmmeRg~*=o1n7AQlc?$84yS1Gl*mn)=D?2>hwpYl=C#M4 zpOdaI_#B~a_6^d)#4VQMP3pto&})8FPBJ_vqS+$}{^~iuJEuUw%`vz17}EPZx0K?f zhXLCT#NPKD0vU?zNPS}I;@MxD51jlN&7kM#GAYrHALvY;a+}+4Je@|`W@tCRfhD^z zY2uA^%xSi%Ays?!PQ-429bD9%fZN3zh{2OR@suE&=nd3~(40`oC6iHGuxwHLquv=w zt`Os9Os3vGP{^&iBk0oPQue)QHkjYOUHJKA)r7mQIC5vhYfaxy>vA(y80pzv*|H9M zVS%;|jafyOvKK%?%3lg?ba1JwP5ACLPS-`(u+{!%*DC5sYG!KO;j~oa^*)U!KFU#N zeSx#5p<6|Bu6wHiE&!Y6~yVq0)DPnT7pe>|@etB?X#NJ1Na z8EXrqsbA7&G&Cx%eqCbx6WT*|p`%c!_6f5G%OXjB+G6$R{+WQ3gL#Xlz_8Dr9xS5& zsz}O`uDrEkQGVKwz452$=g(Fj>Y)`0o?OBmBv0cI*oZ8mRh-Eg1V(cuyF)90j1!Zj zl&Q+@CoWX7&UmhGB%T?kmLomTHjt9kv^Lz@OjiE?Uf0tHi~26++Ia6go<3-S&_M{e_2JUn3y z3w`Ug@Zn{Qk#?=n-WQlNVMRp|DRv?8`bN>M>0!&krhJ8A+c+T>IS_sdwtl`szP`0# zouX~_hr$RBU(Ep)UD6ZMXc#%huGeJEC1@jM=M;V)XVBm8ZGmaonNQC z!0JP!>n20_q}Cik^xywx=HCx2wCgSB9hW~|NqGL{tMG?~=%H6E7*Z?D^PapPX>iz6L5$~&lu0~L#KfJy}rA(xC|Oa=U)1d;13rL8ub?SI%D|=4mWTkF26!MT_kyk^|!-15e445g=MV@3{QKVg+Q8uiU<^a`A|fb%&RDEDei zu*Pb{poKe6wFpNED6{=1{S)}9ycXya?kv=^{`y3Q|G!8bW3%j;Ja2V(Q1zBXTw}7D z_np3pJ-;?tsiC-{xVk4Rv_izRClECdtQ5@db3@Jv7lg+p5~Da`0W!^lnMw*)iBcMP ztlupBRQ$sGC5^lB>xOhHjDn`%mSn4drVRYBP8@NcPi8?F@r12J0NYEPrmee8E+Sn@ z4wB$Pj*U`qBc%O1QIB}04QgU)e#B<;#O@DD-UZV z%AICyp&vy7v;b}>2Tf2f#DWQ7oqK*|Ctxf@0_fSu!`R=kbM9XM2Kf!?bpkLh4)yG` z;RVJH56@AGZONE4U*`!(9@ZyU_hOR%Qm=eEc&8DZ%$#Yii5R61VZu(K)ahX0wmHG6 z<+}x5^}IA>UH||L{w#~!7CxqWS7aR?-qi~*Q(Q}Lndu#xXJ+TCKkwOcg%9>a0fBEW z{vKL-dMy6wzkAsJB~T#PpxESZDRoro;3E5Gbwevd=X;8<;$GC=9C_!V^A1GD1uPqW z@tKR_1Ewn**E%YSQ`Rnzc6si3&w#2<0Mn}D0ECPAueIiJe>oS0j}SXUt&76!&B%IO z3~RLvndkUfYTyORkAAb%dzP06aqa<~IW`scd$mwO7v!5FuE`cTXHhy*Oji_%B0IUn zk%|E`20a@TZS16Z=M?X!XTJOTzZ4a_<@w8fO1-Qxmwtlh2Bi!Rfh!BIv{H&pavZq- z+7*>UiIYCJ_Eg~VVoqM_wY#0?rFdOD+Q$nvWFKDu)y;!u`>TI>CQJ#ZqXJB9`jFrZ zr3oe3?ymNDt27$;!2)eL%iNA`;YcQ*kWPEJo)dtBy@Cyy)(=Ne6gWdgeoEg+zvS@3WHYdgUcwi^W7g9&h2vcPHuYYa89 zvkGWd!22=kGR?IDu?o!fxW|QuZU}VT->*C++x;4Q(A18(b}>Y1Gpx^Kj*J2brTJp? z_Yg+c@T0e;FN$2e{ufy5n+m8Ln-5^~O1&~jb&Ja<)YWX;n|F;N4NNFwlG<(BdO-&- zhnzEbW~-pI3~+9y2|o?|1GE$Y0d*JEnj`RGl&R}P*C)m`Tlc38 z65ztv2F2VnMXL`$6Ur{G~yJ%7DO?S>KqNJj7z1mdA*XROEBUqJkXny(1Yv0=yT??V892)pa zOg%8H)!huatF)YZiS|#K{=ezH2Gi>z4atjvmS)p1mBaQAI0U%%cIENVxYwV>i8+Lc zH9@1Up7Q_jE;1|lrWi1;StTkZ=FB+pU##U7mb6C}zASU(I3bb00!+esEd)qzjW_#C zu(gCpQJgTD(dqJCg^;3wyL|(s_@GI^1zJwSup=G&0)T+bG!?Rzt}e%q{m=>ESo*%* z=v2dvO%K|_o`S#WP*-;0q{11aqr8UPO@Q7NL)JaU#T&&^y+~wy@gkG!JU>E^q$Cf9 z4Q1^cvqx@j+BV)l39DTODX$0z)eh|i7+%aT6@%d)MsZwCpm%yC!(%nfV|dq6=NfL1 zEljYAk!#e>C7`)-l0_{zufiKZ+{M-h8BD;}9!j1;@4WpU+B~VVs<~dD z?S_2SaI41UbUjdSe+p`bb@xTzN^-x-yf@Ev)rbkH@m+54gos&h4&oR$skC;aY#Cxx zQx77ttG?TNF`x79fWc?Am4b7=6$Z7#>AjMm0?)VoS%F8>(fJv~@ zZRJh~G0K-Ty0wy7J#`!mzylR;giKlKL+uu!IM9vxy~6;fBpi%2$0EYR2lCe`yXe!$ zUVyz5C1XS!sO|(OA$g8LuMF-N*g22HEq&L#B2(wF^BC|pc&|ZofzX|D@8!^l4pWb0 zF>%7?F1CtyDh^!jLk>s~6T^VSBjyJ;7q~gRu{lpLg8oz2J_&|(5H+IGb zV}Jced-(H5Ul%F_L>F)mDd6CMS@gjj5Fir<^J_zy(LQ$nu zY+^;WxWHI%?bk&hUAz|dKL4as%nVjp0w3O$8t*W3l}lEB@Ik?{?df>$Q;9JW)p?#z z?JB@au3cHlfoyE#J0iI`LGukb+qHXF{;lZ_dxO0bVsk{}6mWc@B~kE#8L&~LK+M{t z((^LmKX|*0e_(B9xq7hT>6HJKdn3*^N3r(ZCw2ACrQr->vYJ}9GiNt;o`gmawNWcM zNbRnjXhdcMj>l_8W7Y1r6dKY&gT>PpIp7-inG0C~4RfF}lW;H?s&=DO^c)x3NS#gC z*;kY5(?0BO89V)-rb;Ec=BRk9f119GS#O@YN{2C(V1z@R_m}p>L;nUxCci25HWnu3 zENm+ARVaSj<*3z`?z2A+Gvy~c9sU>o@#i+!1%V-!2r#}D=7rnfL^(P-GP|vOitMp8 z{cy{$+VjG0=8s<;{6drOe+p~L9mJ?z)j}2O7jx;Qj=? zU;YrlBkX={AU#Q5yZ$FyEa6ShJKw$qsf>q~zPV_t*uC*0?#1|1xgLQsmg14ut#59a z*_7zqkAX&IE@n4so9(&8eC>&l%?6CV8>VmXlm9crx2_$}y;3L95A4YBloAua#fZtY zt_QZeT>f0In_AcGnvYj#vd@7v|p|57&lz3*K*I9KsI zK`PTCfs0NyQ)#0-yv5^_)nLZP{k#i>c9W2u9Z)DcdxFM=#i8OhQ}Hv@ctP{7x9H$8 z5#(B!^k4E2I|%1L3p z63PCv`re;GvIX5cB+^~5P6BB~NF}kMP4Y=yO`%i3l$br=3EZ$W@UW{!BY?`yY`238P?4j)p|N;Ok^VL%S#9 z&429@9>tVU@&tvT!!7@uj&0m5eSW@z*1$T0sQIm(5A3^*r>ys+H36NnC3j07ZZrzZo zILF10sNGOieIZikZyZO!)-I9%`+F5TZ%$`}K%Ztd zs-0T!4O1z_xBTsS&IS|LWRakCPpRF5Cy5?lQwRqP^?&>%PFjEu zaw~+yG|=jMg+I(Nphw)sC>DpF|8)uKrTL0 z@i7)4L1#-H8oHSg26r%>)0qgQPdYvWGk+lWR5?7?Fb8x-q=2ew9@lA!_J`Og?^@qs zt`q8N58y5-)rW{kh*32=SQEk{roiwKqV%!HPj0zzpLS2u9zf^c{wzCg#W~(zxvxoa z0X&O`k*jf>Ds)E7il`KX;FQ|4e)s9%iO>mQQ;flAW%SX(NeVVUi1P+Sa9uEOqf>ZA ziX|(%VM-ikT7=E*q#sRxN>9KpY3=4nOsvc6Y0BGU#Gp0Lt`)TUUo~>se%%m$Qb@-L z?$@wU!m)6C6_$Q3-z!eY&0)`ST)%P!U&#tXNm1UMkKLuyyYBb!&^$1W%B)_fI*0ET z*mNzwI#xqwh50^@Qgv-LGVV@1tR)VVUGr4aAG)$Kz=okLkG(}!h!;MBTo8%9;vtsngwEud*95R)`~Pjw*Xt zyADgz>VTLqcP38*QPsN2Vd3ih^sDgzf{TtF%&}-LBi~FCvg>RLYcU0*KysX*=U05( z5gwU`=#yAsB!U-Hmc-SI5@BtaY^5D;<^tOLUgg8t@L-v4#^|~SJH7iHhhM%Rw!eQ@ z6?p&kYr41>faOsYGq`->`J(FC;@uH8CdrJN*$vKM36xGF%iAh21qwPh&6|_a`$syl zmm%E{8^AQE@(R3@jJ-y-;5I<9y#QKpaM9*l0A&2w=Z z!{d9A{YIhL+os$~{@mEpsnh?Y2-hTl4Vj@+Dia*Bm!6yhApuPJOr0XRm-~Tpr%?-j z+Z1KB{4;YB)R$`Jkal0iL#seUybtyJp}-#MV&dj_1v5T^4;TRM``mNfIFVTOoW>v!UXAcZ3fXxjMeM(A8y@PdZeg0EuDN}HUMPPuYoP3P*U3mrc zIdJ~@^s8F&F;DC>Jw4X04d#qvlY@~|8;tv(roo7nFhjFss^j1EqI%wcnNxdik9g!r z28&CB44Y#2kIUgL0=U{1$v49Rhk~N$?T7c27-`wc%7Jwo7jnIYG?OUgeHMf9Re2w- zri+V-@{xiIkt23%Erz9zuP~}?l%DfaX3pN!n(&qABO5lVFG6nxa7Xv7CaXnyN4{+} z-~UJ`n)dpG-;iH~Rhsw5z?x%rzQi3oua_`F5HunT6nikFuZ$S0gJ#SR6g^>P+0i@3 zNo&S2d$mf0Ewq(anIHPNo8h7X0}c9yfm(jmNHsSq2Oq8(aq5r%%6?eU$vE?dTqzJeVq8}U4bJPB)#5@1l#H0 zT4F4NrHc8vYLarW6L?_UZS*0?kFoWl9*6ppdz*b5yKAW{f zWfOhq^&+ELW8W8Ti8}Xka`e84b`fnQWOSYNGae_<)@o$6K4w4RaI$t6^@@ zzU)Nn`1w@uKN?@wXkXS_F2Te+9&+TeyS5zL$FQ?r0h3JJFdRbN?gAI+5Hg?%QvlY; zYH)_9WEkn_(1(Y$G}MwmeI#HAfPoUh^s7a`9yl+Emighs75MHzEOf z`Sef_!z~ee^~#wKj(s81iL@=L{8xCe4PJ6TC|v6o(ZKPoCQhibi^@Z5=W+Sa?yc8R z8rELbC&+jm8(0R*%189z?t+K|Dh<2&SG~|)p}scKZZMDC zQVT1D-1(}}mY3Q~pJVteC~H)?%EU*P<#tjJt0>psa-f^Wbf z+|7-VB%GjAKw9Ku2y1jPpt*$wV%358z&~019{UX*x0o(9+~5m#Tj|toZU^1f6&K2? zX8LCB>B-S!OuocZb}WX6b;EIW*TmhQic$tUG2AsImqFvtL&V=66N5%CFp-@IE=?X^ z{-;CYE$EDAao;LTfs`7%v&3Dvt=g8kp*l-FMhuR90i=Zco-;vrK6Wf2C_~w?4;sj% zqSIGGCaX-5mN9_>dmWjv&58FyAAi5+<>z$i_)WuaJgsN`Dl0Wg%esD1D00O;-E&Sp zXX^BOMb$TPo?8z%*9#h-9vSO1$v$PIY(adQ@KG>dp^)KmH*wni6(@b^!4j8>!=||L zL0>u*0_@I>o}MVLH|ZPiwW5YgWE;*hhL-Sk8ypmf6TosNYZ#!<`72_984=MJe*eGfki8jNGhWde4)D#l2{pgmwEoZ0f0+5eI%WbHir2t3bY*X#aKCd`0`bw z*DJgd5#4sf#--rfQXf1@S<5neOVvMi3ig=*p`~;woKv~*+xqvneG%UKh9`A#vjJ5P z%x|&L!dmfo}ONjJhTQ?QsZ8$pts)h4{-0 zkB}V>8%a3_mYxTrUD5TNhp@Mkp>FrMo{&7>rI_9w@7>To{bl!uoXs^OD_pcinio4K zG8Bvjro-f*L}!sdmO3LC_L)jafyu8q)Gc+Zu$^$~7KZ5}!EU~|4b;mEA=9mfjj}r- zmaOmknP{gR1=%cqkEIzF0c#0LRG?=DuuOp&kDDo9BbV!#H%uPC=L5ca(k(^B+2Ze~ zz@QKHn4TV2_lR&Ry+Gw}`nu(ZSa_}Ar9K@X{f(D;)_{Ng{^#lo4PP&T0dma~m8m~u z4L!Sr(jL~HFIqHT6b}DUYd0g&#s5bYWSaP2Rq%g}>pzDjDn?>U8x4Y1!`MshlK=Nu zFN_&(>LKgD_3(ww_&Q$y)Je!l<%%-T*zb*{6$a+HlubzhTQ^{-ZPP zb)CI|r2fdd@PPFrIg=exm(Civ=_4CiDhJZzJuO)=N8Eo7=*xznd|d9~`Mc(qsLSZ6IFc}3CI!%KicfFMu5*U%=Z{5_E zWV_#@fd#|2>*1~DE6>V5`8rneAz zhv;enMGy$2%7JhFM_0x2uMJx!^Zh(f`sm2M1ZU3mi@dQu{Ngby9+5w=Q~IuXz*k)s zS~u*uk0j_icHzuQWbq?x$0zXOFIB`D*jKK+ubn0(DF}`DKm&)p1g&KUpPpBQqJS?) z%eRaFopi zw#dwN^6sPTYE^oXK}Q`hO*IPNkFo78%ZFeCEC?uhEObKSO;0B5gM#APD9~mY z(__i%K-UH=x@O`GZSUUsF0HJznes(8G|0N|PDSodm+|sW)fnJDs%K}pu<-fp1~JOE zw9px+q^A02SQ#J86i5hUjIDs%^xPh?@PAf7C5K0*LDO%QXx=yCx#Q(RRPv8n2D)c; z_l3#C$(o*Cm{1goAGtIm>>@_`$SJaz1vPKDEV;M0Slrs>-+h&N+$)DOKy-XWI;$gWzG1e|(S_ z*UkKG$OSW`^T*pGoi;>91~`uznLS_dBU}l-Fe`!!0*woNYT|+Y%WKC+w2I}%8L90Q zxx;Km^a^p8sX+YI8SQBt-wui&niH+OeiA!|!BTD5?!2^KPUo8=kBiRX3%`Q8AedUn zvg)qA^d@L9+x{8xt45U4eI$oU1s5#qr7VlxCo^59>1oy3ai3JcUVg#IExEM>l{&vJ zU(d#J;}jy`k%o60+0% zm>Hv>?Hs77=iN=dky`;-Tl8aXIDUvbE?N+_#7UJEp@>WRb2=ss!|(CIw--2Dk{R7c z3ML+clC|gb$obfGTAi zuK{bKci+zCac%HDgMv)J8*_yL9qa%=uLSk%s*< zB3S!xIAf!@#%J{5zwX*-^jDm+QNvaGJhTuNK18o-SAIz7)et&Ue7m-s7{ym#fVCNM z6ix0-2Or(mY}`3MmqxPBIFL&sdHdl$oONQvjkGGf`z#;L4GUquE|@iM;q?pNSDm;f zN)PudBoc2vcGTBn=*QcTmo1RI1y2bJe|^G0<#90Q%PGSlKrv<`BkV(;-7?lss!_u4 zk1SWeO~LVmu_$ZZ!0?)3%~Lz!DQ_FYdkzXsMzwCWcpts=OsSgk?Uwkty3c5_ z7C9QnI}%FcNPs2@E(lzVBMP(fJ3Tktyc@=@P%YOUjmaL2-RO9)Hda(uv#tPD@s47X z4kHHkFxxkgB6!E|bcq10>)683C!NFP)fCUJh$Xl8iR8oN(1SX?JA{i}7aqS(*ZNZO z>$v%j0oO1P`hHsTG6oYjeCKk)Lpw&EB#HojlTH~lbU%*7HikO@W-Wj(<@z+RVg5@ei>-OEqyAy?CEYJ9 ztHMqt@)OL0QJXD0#kwl@hQrabszd4@Ef<7ay2_}VTh>*`bRSetlg9YLvK~Kb08KNP zP|$96J#>9NXXEuZw=%w8*@EQX*+PCzc6^0QeW#`;Z@+U@C0R9@yxAW?gtaS`>7yjR zV|K~c_5JR#7a;P8W-n^tsKws*6}KVVY42f8*5Es-1FNGaIK0NHh=fxUvWNbW81W1|GFG!SzR z&)rKjzf!TWv%WXD-1bKj9(Knv``&zoH%jU(2z+lZe~U<2p^sxk9;RK0C!G?eOA<$s zQ=_NtG>U#ZGDz^I>c{fn?KjbNIl-p)L&>nXtQjo#Zz_ z!65*{hYtCBpK2U7T|yl>IddKw@r3EWE0~FL4Mqx)@3eKtNfEI-M(vLH#F_K1BV6;& z+rRi7B@Z#X{UTBWbnua^+VzU4cdqLQ+7;mQ}RR9FH!rS}JC5Hc#N|Ds zOQ`$&h;w2Bt&|@`oxY5Y?SRJIf~!Vho=dxF&6(-zj{sPX9s)2XWMQHM;a}d?lxDFI>zwC+&fN&Mvs0i6%XhkHtWV40=z>^ za!*^0?IP1{=6aW}5Ss7+_BlEz>m&Vk*nD=!$RE$0m~4IYXMv+yavEjMItk*b?~1v6 z=Tw5F#uM(8&{F3c&mFOBPajDpp`~)XwYsUP(f0);wSSKZ&|&$tMbFW|-Ax$ma z1z!<+YiknSq<)Z@Ww-gvCkVhgnlGBzAEy#Dz~@BprJJFS7$qS~Sfwi&qMfY$CprmQdL42 zGxF?Qc()<=fSGQ&;{Bipc2(jmV4P084<&UnUE9n?*F80%ytP*k&uD0(^D)>iQUlga zAZ<59F>>zbFU697nf>k@)`ShlfGQltKY0+NDOvOM@3wB@V_#AFhwYy;dBJ8{%rc z3bAuxZAuigJ5K}GyJ)9z;cR(Z+=IZMXG!YeDk4?lAv=KDWYd5m6v!^3Yrd>?o3vZ*N}U_kd~{v>7Ve_m-6I)7 zQD*f0?<7SGzur^#X|SFNFji7FQWF+`Y-w6(777|AcEuxs$4x2TFtp?J(@$16oemgq zutvY`?VoC0qy|``eZ9mbUc=0LGw1OYJEkOJRNBFk#d@1X*HpyeTm(6ypFkbGbHgI2 z_K6voAv`dkO}Y2`5G{em5J=Q@dGC(k>GAOE(+|BlcDdX^x_tuD?F&0~MQ;j~siQCj zzK&(a$8_Ya#s*EXvx&bkz#eyCNE7c;z z8n`)%rS=lQ;D0bcwn^8{gJ5Lt*D3vNjwvJ@x#T?bW#V_{#&iYhJ0o9_N&9dY&(Oe0vctI| z*KU3lP9VbO54;+-;{i+?^ykb7GV>Jr6-oCO_G(-2-bw(X?)sN^uu0HFFeuOldk5{^ zZ4013F6mY!eZMzGfQIv@fY9B4yC$}i^9Uj{UilIG74cw*J*&@|C1oQ=1kW6NBSaCu z^5X%r;PIjD~W)z3clL$KzyY{cFKTr zgH%`P%w8`z)AOf1-kzH^K9%TBve}d9T6Nrv{Em!#DzO~?o=4=HcdCTM zjvXmqlIxkFa{q^{w~mXdjk<=14rx#thL8>cX@NnI77$dBZjkPf8l<~Hq@_d}q`Mm= zq?xlT2_cGZgAu0 zae1bMf=S!=q(ghuzzikAs`&*{G0q>WCH~h7G2%p-Dmg~+S3}I+x2#<0l#7#@c*XHZ zBw&J!ySsL|ftEoA>^=5DNe`$Ow&Fg3nr|N$mw1r?@G0UfYW)Dlg8k17|Nb=t&rR7tigX_l$Yu^9sz#w7|00FSTi~T)s#$u^*!j-r3Zzk8VwSMX0 z;d`6hox&=keKbi=o707)n(r-W` zHD$QKEl(avf}iMvC6J@`b{N<0m#r2_b$;-S-fP|(uJnynh+OSV>9Rmq?-3f`0{QuW zok6>QJ>!G%N^h0`Gj02lANZ`&@a?&PlpS^aZOoL%j zV|EGy9dC>rHXy&JL2tZlu9p9ll*0EnH6?%OIi&+EHH6HqO?W`vhz71jJi;i^lEx~w zCQ4Q&^!*z44@Y2~PtUM7K#)lHJWrZyaC1mI5XWGP&c9=;p#KEuY@Cs{wEl4uiC3OhFUq=tnW^$hKIvnGDr^U;TfYXxn18AVH0cy|ZCERkz53_QL ze{BooLkk#}0%)Xg65fW8i?|_$5#9?_X48v7CH(Dg_L`&_0H2< zj^JjjuH)xl1R*9Icyx|Sh|`t(EoXmzy%11W+m9ds6(+E_yg#c9$q3lYk&Qk9WNc<> z%QT%eezlnf6Giv&%8~-?1}rORxjt;i zU3?e|>A4?XaBr!7v$%C1dCfPwV_g!SZw-CT^96?wRUz;f4uNpg%k7-`wV1`ca zn04Xdi>nv&4q{Bu)ial1R=R(c;(bEse|?IX&EQ};`Rfxcu1X@t zE9n`lVJBQ0;@~|N{YjF*^#(Z~P6&0ELHt)$8HE;tko}wz9GvJD?3uG#zw>_hk>kwi zmU)Fe)acPqu)Dch>ofi|>pM18lU)JGXnaddosG(MAM9*1YjVeb7B&HKyqeu|Y=a$b zI@ksEIGm(6n4B7hErbT4M#~^8qABm#(@+G1iEwBLa0(DfC%VD3%jk$+VzklE5E1g< z5kr_r{1y);-A=i3)%$jQ^dRY>UzscSjI@vI2FS6H@q(rBPRY;q+M-K`$b)4x+h(XL zv}4Zj?}cejd`ZD5S8r`|{%UL=A#>wHq&kqJXow&zcMVH8WXSY8QrwV*gKwZVhtGe7 z^1#i2%v+o}YLEmPv^OJhJW|laFZ(gF!E6O9iIJVqOyh0({<)VIH)QlPFG9!9oI*;5 zq$j`o9KGtLzvoFgucfBHGBsrs5)wK+>WQW^v$2uB_qu&%GC=Z}ZJkpcy1s5Q2$FsL zSrHAL68ypAycYeB=<8c{1lA*o7RgQ|0)HWhBuKEN3dxrl`gTuH)k>HHF$h?^Hu@Tb z50n0U=F1UfF3euj{O8G{hyig^1Dq&jP$onMEvz4UyeqHA-m(5pzK+zff+DYk<5J-u zWHwdorhbFV#Y<b87t4+QCuYv*_L_YWNF78Fb;N$#{GPxOHiuVeGS(Z^ zi41>i$edme5JrHDBV>~);p|rsWPsWTef}O;hB9zq&yccR@`B?DpUMZyX+4v1PEi+B zrr1maMsr|U9`qqv?A-7Bk^nx}J1(?;MAeD5f!UcqLiaxAG*K#`;AS&;G#gX~6M-&A)2fm6P9bDD}=*dDp)#GZ5y1**Lsxd3Gg&^f9>X$+G z6?`As_GjNcA9z(r6h^UFy#`7WqaG|!t3ZCwmgk2P_0lt?R_Bj~;CBzi#!zazt*m4a zEzPD1B2XQyrBlyI_(FkIgSWiuGg-**4Td?-T~wG0iQ>zG;{QG}6nO>sA?lC74(kKr znwsP$!(UO-hx69TCWs0)~(&yZ8a68;l67bGl&-39f7 z%dqe;!GeYxn0;Z;El@NTUfzc{Gx|ahBU1xzX|hURTA3tDTfW3Uzcl1QXj}!OP;VG3 zY5+M3kSr367#n6r>L|W@)1k_4lguKA8l(+%^SFirJOiS^NFQN=@8s*%EBw{dP5OR7!#>i{XmNs%GsO=>9?;(LH z0X^6WnoWS=i2*FUZ3~jZmX^sK9$dR#9cjxXAVx3be!;e&WM`lus8Butl8xXWe!s%h zu&R#&Rx=(PMOd-bYpp*pSnG?|KxBP~8q~exGrJcsyyH6y3+P|_47iuEEx&Hq$i{Kg z3;z3Fiy5mCTwfZnOiRre!g!q)x->1izeJEn78n%p06{97_35Hz~yzkfiMWC-~Jyn|HQxk(ZqBLqoduPqAjwTY6WqtBru?VoEq zhfcrvZNSPZr{XzE)E6X4wD>?gtQ-!X-!n5IN<(R>-B@@$^>p)w5u`L9A14?|OIZqU ztq-rft#rQ;o1MKm=Y|YeMmk11^Ss1NK!{;mUX>p$bN;R_1W8d;H--iwb->QQ@A%?G z?XQ)VUk=#f|CQ$S!K9B0zL9WF2V|MkkmsJxBJTn=(1(VCH)|edcr-&3xCmj)%SZ#oc zK2G|LpV=tL#8M7kuN2|t!>^G~2f_>sFZlQPzuO}w)d0cp?Nx8vymS|$%;zUDL6IDI zX$t?{sb1g`6a7Mn`M5594EKz=)n8NKS{zoBk}0Q+T~(GniVio1#+7ubnuUhNS9Ss8 zWB_{`16)~IXX(E%_1ID+{i=+9-4^9Qs^ z+W@h}Mqyo+jaSUo5|m(P1Y#rUT^5q{uF#Yteaw$rRC`8HHrI{RIGRmI=-WJ`Y;ZbV zaonH(!14uhfG9KUQVD;jfTE8ogOW}%SNRiJ1p}-Su1-NIqD@(*rj8nFO7zz{q1ZrF z*cF7#%*}n4a)XQ6BIbOqZHhaT`CS$3j(BjM^60^an z-X)C?8P$n4yA7JLgJwSx&jYlorp6!h~vfm-z`fc`wRN%2Gn)H9Z@c7{#HAGTt>Usdr6f*oje=(;4{-TT% zg$(Q~JZ8!V7TX%T(i=N>U_aRs6L+>*Otz3o6MKy+(`3;9h|_HfMK69WdhZ61_KiU( zxBtzekS^Mo3^Fi*x_au=XmMONh}MW$-_Guwwf#w1s%MLa2fdHKDy%0)AO}j*;tNnh ziu(O+=|3wA_{j)_DKq(%ilX9UNaG#e;0OERqa)*Eq2ghpE??X@QF&thk8_E0lMvTV zY~VOSMM9ALhL3!UfX#&~-6mv1Piuu?;XdFT{0sj+8BwMZ&R{S0M}ij2i9GrUoZqk8 zEA;`9m2bDV@+)y)5*g*B;waRbUqaPlDna86ChEd6}SK0o%M#l0$kO4M}V?DV*gQNFB zj-gyK9{Y2(W*%JS{p6n^ai2~-D(7^>?lYs>$Ul7`W?#OMW zVf&vW%aB1XXZC(ftoRKi@ry@T87Y7_^FB(y>J{piRFJUia4zn+o25&LB-DrQ>o1#M zcyya1UG%AYdTNXs&Ib^+8~pmvX?2IS!5Ae3dVdBLx0V{|{~ognLm9NS55ixTVA&4p z(Z_rU{yw*Or~Xq7=CZ8^Kk|Nyv7JV|*rcu+E_ylm<-xH(9$*<)Jg?JX^>1JCtj7T& zH=ECI#tnY{zaHIK5lo0cZ>N*c(g$w|#kknrvFXkqEA~kmGpT$MI2L@KaQo$C@gCE= z8^2&0c7Rp~J+Y~!B;>54P)wYiuvbicgyv%1-o|+6m%1Yc9qhC$svk8eCxEDglCk-| z`?!H{5%|Y{V2T4TsR<%=+vJuw@YbNcJR8Aiv1^Ag>oBn}_ju z#c=FKsK^=Ijro54W89e`2M(SI6xcxmS0bwZTN$jq0w=?9rzJ4KNz|tK22P#CO~DS< z_gH$9gUj}rhQo^RYX|<<)TEVdOM&pI-DDB3VnUR z=`(B5C{sBU9Se$iNm;frlK*~Y&gO55#_^EbJ}rHT;D<;vs4x_WlPd(EobJxIM(-pgZ85@j99sBcVD{1UGWxSfC zK#W=EkR}4j6=MeapU@Mdp`!pNf+7H`mHT_XBo7|dorxJrl;}cJZ6XcYVm7Cjp@G$d~;7mRAJ18|lfOSB|aH=rzIZ6~j(x`A$O@E;wpuB?U^DZE; z^M+Z`slNJP@D&d`I9?w$D4ve)p89<~==#UYCzRj^=O>x<#*a?lPl860_KhU1z9cP1 z>qfA5{|58>jcKf@=jr{VahEQ4!%W5h>Ah!Yp&KRNXHF89-IpOJ+-AK72o(h?={&*+ z^x_1}E(qE5((CcpAKK6VoDNh3crZ*DD0t`5lg9;E`?H{G=4`&Hh(Z!**6U(CRtegAhpiO2e0o!U3Zj}HCvmPHvE zQ5@M9!SAz_dgMS>WM&jg!tp0C$*&FZBz~gt|I$f01p1qjxK$<7bBmRjKORLn82Ok1 zTH`BFl8Ypvr7y{X|2FMB7*Qq-lF(xQLkGm{W7xv*M_e9~1Jqn9OlUHh+>jVb(knG} zx%n1lT2k;jUMi5F@s)S5)w$;J3BNN{^aE3Wgd2RJP__?cb%S!e1V-R zU{aJ#-iTG2$TU&q)QhlWVvmJt-5m1^Ld+)Us77S_Is?fMzzSC#+QMTXn24FSlG-{) z2Ofb)9CqJ2Q%wD&9&VODgPt0&p@m46&F3*nK>cTdbi|;wN^;O~|u&rqT|wJW8) ztC^v3gGYuEf3JERa6ifzp>evXG6T%C7al4mQLC(U^wT~uKyqhyJlmL>zR%~Sf+}+) zrc=8QiFjP-_%kE^KD+wD0f3!;x9vH$@0^$?u_m-|4~dyTiS8YG*kd5H2v=Cw4O9wZ zdqxyu1|$QEP3@_vuJu<)mJ5|?{&Xj)Y%Wc^XYNB0-lpaE0;T_)c1##n6bjRRm)=3Num7j3w^q+R`Ki}>J6Wl_zxCZ zzBMV2kbODusPYxyKWO{UVjydHQHvkaXTw{OuJv6F#m28a&;wph&3;0!$E|9k#(IOE zpVB@kLgUl$uZag~2l~GXkbc5j+;?~0vL9B$ZnQUR9uEZe);HMTt(U1L`GtpadUHxJQhibGP-G|Md8 zP1qKHVx!?uiVINezv*xKa$|)FzF6>NHA(T3Yk*aFdOvEqUy5}#{>al{?ZTDZVFlcy zw={tB>lE93NIn~sH36T`RIbH|5yy{E3*-LojWm?%O2Co&0v{$7*^%LeEwt3TWlJ0K z{rr_iq@&)RwD8eP`ET_a6Qmg-p?>%7=}*9MJv?oU4MGP{{m<9$xQR?XE-$yn!DGFm zxw?(fQ#SL@f<6QW0E+4`4GQlO_8EfU$sg1cBi&54Ztd>J1xe8!m9veuMulPb&Y=6X zO)Wa3+Z1U8ZaiV{4xp^ZM-Mn&;1gyIdjxiw59uEgc&a1ZP=btCTXS5eJ;O|R|ILG4 zp4>O{nf&^+U`9$Ha$g)!p{!5~0MBWv^POD<#7y*|I1#xo77EYh8^3&eTC=v2|3$!r ze%nLT$w~N>tp0{5TnZ2C)vtwVe`9Lv&U76&@&lW>Z?&Xf$YNfaj9r!|O%M_wV3LG!a0>)(KibD||G4#D85&+5H|H8G@0SbcMC6BG>I z#NB+|V@n@q%j73RA0k5^7BsgRAH9G4<-KrF9vwFByUOcWlnaMJ(@U>I65P zQh+Z%eZWM{f#snIPdx0;E&H*e)$2ygX)VKTLE6S#c6Tjo@FYQ~G90it~>y?2>Mwg0LQGp+BpgXJo;&#a3 z#qk8K<)TaJcXxF;#Gs$XbKj#cBb62-j=ICbf`7bWo3BB+zxzc|cw>e(*GpORZFHpz zcxjlgz`&kyo+gfW8L3TAP=KklB|~$+#7)v8WzPiq%MEk zO6%KxeD3a($f<%FBq%91pC!A{=VNf@HB6_z2|yd@C{b)p{53NEgfdT|j+eKW?fo`} zun3VEeOtlQ?*&H&Jy0(aeskn5>apysGII~AJ-wd6zyvz!!^7l9M@tqbWz~DO4MguU zkK*G%1i=PvT(2)jL|mBY8q-OG^?CyaFBf#v+P+!|V;uz%VnjhYAKV>WLhe6jcLh$K ziyi0|s>LB-;y_^j;MB?tJFMo{^5N;GB6d;i-`NqJ2_{Y~>}abbFDu!L;#0V00#0P5 zO|=_#%%jv9{JMSlExx22-%l{zhmZ0x^S9g{3&r}UV=c+Bhm7}q9@AopC}KWxRMk0-$DW#Z>u7lLJJ1`n zps~!sFu)Tmr^Y2fWiy=mdR5G2 zBFE%R{WBjHLWCCqET)J0;ny2}ba@Tr4>2saSAb8u<1%<8oAC>IRj8p0*43S-3mw)G z!S=nr;F+VqK})ci`@GnA$?&95ClbdS!h`Xer|wj#1E5&s!;ZEJI65!6UK6zB^*h}z zHr&Ho3|;k)F#QbI3di;W$)vM?CcqPaQQNg31ghSpdvR-}NS&vj8xBYp#I%*|+;&)? zpJ*(GR3DC>GgR@e&s1w99K=E@fwu{G<7{W8MG$HMI=}6c9X{_ zD~pWVOp!lA_2HY*UpzZs)xg74N0c?QNQ@R-CM{b$ha18=clbXfxW|M#Z?)9SD`M?W zLNze>9czF35}^BoUk+qOC_dI%)&0S&h9H@cxJz9!8}4(v{cb5*sQp+&4gB3oV01d? zhL%?OhwOqweM;SZ+Zvk7Vt`$iG#R4I^N@GRVr{s0*H{ylv&B2nx3Ra1i8S;F{qB{b zCC-~3RFT%gL^xSv)W2Y3kFi)-F=^KBFpO?kAHb>=YT5rA3Id^8AA=tH*X2fxE?xckCohBX& zichH!^!Nae0mlUQKjG|1Fdqg5W1Jq1anGMzV&6JOKhAH50Yq~EelvM=vRnE~m%jj0 zqp@kxJs^>YFzwc>YyV&4f zVv-u993vo$wl){cs6V%f&te@$A^@69b!Q0RJ&zo&DmlYT^Ry9k-sL}jQ?7(NAe^6+tzD_^rC#0EmNS)V(yhUl z50FrX4UjrBi+cb6Eg%S+F!EztVsSGCuuy|kv_y@(xp@!IOK{`yi_@<9k1kThJ8NXO zKPr^k)vOx`vI21t43I+J+syjZ-|b8ajIJG&HJnOI9!53w*wF=SS6&?Y+*XRw?!@^> z*K5ukE)R)Z+D__Vs}a*YwbM(0v$qYd5_WC2JXMXFu5CtnG-);N^LFc6g8NdggDPoN z=;bv*vh!Z+Hz(@tDniDO1r1^*n|c&t_TO>p%*n=sK9L4J@yDbV#--@ErgSg0+pyjh z`t<{;cochyC7tIX#LofG*;@i40OVP4!{1@E7F?)Otduu5+DK4sV(X@HLoi+a*hCYZ zarK6T6)%#aj4kX8i_aOnK3K}6+nswmgBE+E>imF_-$%Ux%;Dsv zRWzbZW{-U%y|O5>Bq%!hTDQSEVBPHtVo*)lf%m?XlTS~jBa50Z9JM|LCuRIOqFNfS ztb_RT%Q+gvK7IIg?d14$sz7h$U0HkJQcN#lr4lV|(5ZCCt^R5diK6w#L@W)BdM8F5 z-7+$p5)Cxcx$Kh;dl?2w@31mPA z>A$LaZ^iLhsoX5^?@aeuqeOu%U2+r5D}925iXCA*itox^QE=)+_HW7u@c{iDFv%!_ zraZrOYyUw>+bDLjQz;Q8Z0$r)PyUg_KglZ|-9K1L8^!NF9SeRmnR4h`g8GF?071;_b2iL;eidGs8zh+mXyVX%5H)Qd|Gt*ph>NnzJ6CbPeZjD5Ey{ zP*PJDD?c1^b5q>U+0_}#s3&vpC8Z0wrlDo(5GIqpq4Uwho4ugyh&@?sS3PKmY%B`= zNon6JX5ItAwr<{PV=h~xoT@kg-@%q^U5!6fgfR8`JLdTPPPD0^s3@+*!n5nzYFU=p zyX=h}hLvzthqBReM6}+L+wP6?*p8v7j#uhzoKWuZCxt}e8+;ogW`4}@)Ro%Hf@VRz zOsYbe>2ut$sSo?0*q`hRKr%|Oa6`i)iSpw9DP1}_9;QSo*q{HK1}I@ns_!G)<3q9~ zXgESj7M~NUDT#=P*2yA=-Y4Dszc589H-%s2sb;{Tn_emTG z6zg$+e&Zn-|JrntJh|LDx!4+k#9oI(`q;TExO-)chqLpFF*1DeMZ#Ln{^7oF(Tv@; znp%NDc=ryMAD(0-rI`2j(-$ZEDu$=(HW=Y+UghPt^vZ4jxQV5QsPVtu#DI?qf121U zZLrGwz8Wc)j6In^)C{p^S3w}j3(;mTVzI5!>T+^~qrPJ!f}2s)I`UA6pkl~k%r!T! znX_vayp*>z7x6&WJf~>MjKCS3A?J0B;!^LY`1Ls49AoAt-J6oLp&Ic{Po|{X*mJb{ zYG6Q~%cBRq$_gs6=z-U30Mltvcsn7jya5+M~>}O)!)&K2@2q48E^}xBP|GRCCD3uB_vKrAFz2rY)vA4N#ap;pl7OH4^FL31 z;|uHGaMY8R1(yI%zN!WSU{2;a`jlJ|q8Rn(og>st5=Kg1X$*APnB0&;qKG-}mzw}u z5i_ZaUtCCwzDf%vAzrigfUOtYn(lNUnr_E@um+Yc3d)ubV&pnWUTa>=Wz)V)!h z7GWrjwdZ_vcRcz|gIiumfL1h&l#ggkDLqIvf;x*X$>{BPYZcvPx?sstSv7GcD@jPA zKY{#Zqs#mcYn%$gpMMYy4cfkeJv^FkWtlkqy0$nb!!>xXCD2H31A}Oq8mc@h*FA#@ zvNCg}c0jtHW+cOYN64y6JMLM0Y>%Y!e4^BR(_}3#M3_g#Pi7c^Gdk4$Wm&>@BWFWg z&Lb%{?$@WwLI0^Lm*(;*r_y(OhqT{ik|Rj@kkgagFsua**q)D*I(hgm<4xEs7Oi#g z?JeY4lW$F>*w2}1aeqV%z)cv6Z#BNHO1-SVp&l<+e+yw*G3-rck^suW5bs|=07`eu ztdaqMbt8bg!NiGZukEQa_RQma`(w@a?${ef@l7tZN+X+~%hF5V*W2ie<)+kCdm>QZ zz}d6oNhVSY&rsds#T?{)Z_jm`XBnBIO-5^V(;_o68=}M@PQRqFHSryCGQCF-<*cHYxem?)V<7*cigzre~knrb`9W zVyB+FM%DEUzt|EO@4hCaZ5wdA@TB}wRekoX9%JpyPS4`x0YmXZ!2_mLfY!XivuBYX zspN#FootC%3}~r$UI$8qLKUcZTbNeAxX-w9%1PL~mf2k}Nn076L<@ST1Kr@*%RgBB zAOLv?9HmjA+qfm?aW_MHWUsbjj~BhtBv@i*o9T$PN`BIjyDU^PUd$`aSn1@rgv&Xo zD(t4|&Pe+ve{Tmm3~Tkz3u|ATx963@rL=U!n&W7wbM=2CYCfrR`?Pci6z!JN?l6O4 zcot0*R zRp?Qcbov-h;Lmq{)K`pF(KDT5@KY-i{yJvsS=}nTM_z{jN-@ij#KfW9?3k9#R~9!~ z2QQSpoC)WteEvnDoC?D`am1K=<~2#b4BvEs0K~d^^~IdmR;|#Cf;Vm|2mFvzX?diY z_~SD=MgVnN0Vo=yXajUDVeU;n4mORmc=>w@io`ev^TCT=TYQdG))f_gLoge(;=8^{ zyK`)pwzVT?PKPhn?AH9}RvMAS00Nhs!0tb5=&ury?kkEh;rYT_y`BeKb>KGRPkC=4wz3phL3mGF))Pw%0V=XaZdohxDbEYWug( zsYHhctqmir1Pn9kw<=cZeQMNcY-JWJKc3rjCO6hLxK~Kdy~?2@CL+LOTRXJS=C~cf z`G>8eF)3OYDNf0Y|4&1mCg3auZ(-SN_>GKCN=NZE)c`4uS`}Z#Sc4f3BJBBZ zpu?v9`)wC_O7v!%Zf8DxKXbD?6U>7>njC83vd-TT>*bmZ<9zn^cZb2D3uS+5; zy#@i`zti=4vP-73fN-2INAq-x1&lz!CN}3xnX5=<b-zJp$gv?@q;`I zLkh&vzl{)4Z~DlZ;9Y}lQDr&#%ic*o`->zv8s3JHCB5y`?3Z^ixA<9l(Mw5Qsn-+X zq{&~aTmIR#*n*pH2;RzElu*+gbmwxm7G|X?rZE~;R6K#MtJ^Q_NrKhC~#Kz zFfeg(!kbKr%3?SQ;r4LxVlFs8FN`1ABayIw(x$80`{Ehj-b~)p`s_%jqwJ&0^`gn8 zRYPg=mC$ydN==s~G3wONiwRG&AZo73P}|rl9fu*(nSc?YQkIYeAV=IGKUi|%Y|5Vo zPf#Tt3!6?E5R?Ku7Wzd1$mWI^DB=(L@Ul`p6!`#T7TdR*C#`(JNT9Q@42w8vAnGOd}a>o z@!!8&*Bo1SPJZ+(pIkp@3PY5M)TDa9*yxdtEEfVS4h*Ux+^;pM!vxc+_t3X>godg; zl$W1zUH0CUsrK=EF4y(jcK`im&T}UTKj0Vj zCBwLhbqZ{|>?ItA4VMf?3etgu5rZ7@q;N_7O6FXj7K>b985$+D`|P=&!^EhP3J`oo z_g7_wl*W{VNiRIF_Fci>#>a1m($fJ%oqcY4`5ki|LA^48`PuwR0deZ+Z?&~7jW3wd@Cn}@`JyIkMRE?lCjw>8}w35|a_w|M{ z+zewjT=*QQg)ENNLn^0g93pjQ8SBMYYga?ZT}4<>d!EtR#qSDD<>t6M4IcfLuEhG? z?6Jb&Vc+F1MJ=V};{*1(?9qa6{dUtZpLG1HB(zWBRH78mtxh}Y*XCO9MtQe*Ks8c` zGhMYiDVrs=lxgG5{QgHD$+NSmhF9B9M)(9NG+5MLj*5vmd=1FhS#?}dbI@r`*G-MQ z4aB*?qHgY$??^rx8IF-Jwl^R9I9SF82#6qh6%_j3hu<%r) zxRmM~hsTUuv!6H?BBy5^2s1>QhdJLQR2P_akXy!=z1h5Q=s$eswP`pSm>-n{30rP$ z+;53@@k&6Hc?A$+Z%LQz=|K;EZZmsW*PXN}ZWRZ9l83K!%B-j3>ZXl2LdH%&FMEkCdx(TMsjj8 z0aaew+@8&Rn|WvUArYq(e7;RbgLa_VRpR}Lf_hJQDhu0WQCf)L-s{53EUv|o^yAvu zN*=C=?_$GYK*S~Y4So_jSZaeMgmvg^n9ZUWyHt{K69vwIZY9qBc^=~uuK#G&mJNr9 zlJ66X4&HBC4K*kAVx8r<)y@w2M-%|C%!Kn$g)XPmJpR8pRXQYfxBw~C=-01on6x3# z{2<}71$nIV+O~1@oi&}}E@;=|M>aJo^9TU_=E$8bNo!PR&$Pu z_I=M;OB~z46cWz*HWy!cX@)z;};jU%vI;WVz zZ&xV9s3>X|Huct^bvw{0PW>Bu<198GNaFgU-9IlP5+zRBhm88}aG+mfWhdy~Y?cQf zVx+V6OIEg+Q`cJ%HMoX={9UQlfoMbHv2WbBD7RRr-A#OfTR+3)lTTr5<`#$5VolUG zg<8JKL1&&PXID#~WLh|Y19FyKvk`r0HcU@id;^V6N<(KE0TwQ}xok9Dz+@9VxDqwc zSoWIE7T%K?WnZ}x5ut<=m@tt5J=7g?dn`X;6S84Me~?qC%6-`tLFNNgI`6A5Gw#kb z5+-)+3kW4pjVSp&O4kpQ_}>6TPDZ&r9S(lEJKB&5iM<+|q5deS1HK{Z zSwVNR=L*9>%BKJ>aQ|}4ak!?V^3=X>aAL&OUoHT{u{=Vo}%mtq7WR@2o~hLGgS4Z?FJdUb(Y6ZLe}=(3s*UjI+%Rcjy|2DeFY+wJ)eCRJuo&< ziw<5_8{!vrek|(wJLl*H))oB*;~xrU3wdyz9*(HU=0bSWi5M-Jb`$Gr4!2JNycX)I-P?{cjU-Hs0BZI(@y z8vT3&JBO@yAySAk^q;!|J_W_XRCzc@nqkg&>fwtzWkH+KdiTU{*3-Sth+ov3TzI z;0zvkSY46L-7Kk2&5!cXm3V7Y=BD!DC#Lb?=cNjuzD6SF-Qo1|Om$67_2Y^M84S@1 zLRSRDlu$i`HR(ylciX((mbe@pQGUWF(woju!brxy&&D_fpTK6D-8*`a!r}=<^*f%y zPr7Z!#ntEKR(_Beyy<2sOJB0rpHJ9(2*UbX0qirn%aVw{r82&v;Pom*NPYX7$@ZbQ zRS)kU8kO$-#6{Md0f0sYbUp!A#}g0HbQ6{m?P?reUS6~RGeoclq}QzNbj%fWI{3`D zNtPGqjkN)eCl~QPJ^*R9d!ny7X4_K;vI)wsq88dO;q}o%#7pcCyjqfd=S&jb#!T1hXE#ANNim_{&WZzK7ef~HHQ_sU(iuROxZ$f zly^>+`2$I;_ak?P%i5Z?v7 zi<)RVX9{T#;3R56jb;Sxt~@UH52#yJ57o;;=r4x+n{xWwf1?MKLh&i~AK5 z<Dlj!K*R47TovKkJ!{`61m^wTcghS2k& zSa=1r`u=_BTocJfNRn;C7CYvopQ-9)znYQf`2URaUZ3Io-5n;!{ZP`~wgX?`F#s8P}#exgJbShC={Hgc>gM9bm_w0X9%-Rpfk%9~s!EL+AN zC75BbmE~wAwh$n6gp!)3gm7w$A$3QReMAQnP%VrE^J-QXShP8lzo2SpvgA$EC`GoS zO#NUCHABny7MtVRd*N|>9Lklk{2fI5t#8Ik*s@wWqN2vq>wYEK)pSsbQnu@%K~(cl zFC_8A0n%L8_u1Ks)|AW#0aGS&4@B{2nGh#WSQxm80M3$BWgIrHY;3q&L`|t%rp&He z3olgcQW+)6e}~Upx^>TSC5$k#li#|Q%v4bT(nHJ2<^DJhlXU%6@MDUtpE79T z3DHA=S{<7H8SO`+>20(8wzGqUYGNuFpJ!W&fnz`^TEaXzN z`9Sq^Z_+1?^EHVV^cnEFeA8`cL9_%~CrZFWS1E$}jzC(d-jTl}>29{f8+Aj0#6lf| z*AI?I<>ruSLSj8#q14}Lw%CF2Sn+NR#=oqprc;#e*F z?eiF#W8FxRf}Fx##d2z(@99i!_Nso-#)S*Gy^ml0%T@B10r>LpeYaPsIw;dg$Gspf zm*$R;AY%aFibi*Ga(V#ynS0QZS@$#_nt#W?87Pr3sNxjDS@RU$`-n4?eHDXGyqvp- z?+6L?7JhJt2W4Ic&K7t6tY9F0T1tUqIa@)kTX%p^I9=BcjVewl>d*$00d3>)1P{*M zU1wyx3af|K(d;whm8lwF)&wnQtW3Wt2KXjGxe%Qwi+=O*zy?6C zMJ}HMo&8LN2<~7g{f*u4>AdCSYMsz%U$idds36q#`!Q;&cQ>9kZBd2o5EJ;~n!R%g z{#DQ~JI@Qp4FjNSuZ}e!aRvy@_d- zyL3C;G*Qt0p*VQnI#Fn>*=@Fxgx}h=^YgjK15Cipj3?>v44$&OI*Te8EXg53PE`phaRG?n7{ zD#Cl7i+Au<2WqvvPz8V_xU743Dxjk|L*e~eM@_tYt;5A+zR)<})m*e})lJd8CkX(F zW%_J4o~2?zrNqR4QfyUEQQE$zGR4-EMLY+Ps$g2YD0n0!ng?PqO=)5x9og++?AI}2 zm`30>fL;bW?fIP>ZSDq;u~c02rVxn5bb7lvw`y-AuSWz;7u;}#goQ^&MlvI%BPhh( z-G#3&b^^H!yEd^quTC}$-#PH)Dy2u{<}zRl%1B z-GJERV{b1j1fe=OGiTo!Lvwp(Dw^Ai(UIw*?ILXiNg+BQKkZVp`5J7In}(AgSU}gL zp>b4DTe94H73viC;YWlI^ZV0+qIayfnGq3jC!Lhw^hQEhHwj(+CTr702?@jMxi)dE)BO`BlBSFpoQ%QY7&az|*kJE|A|Ls!&y>N*Amu=LSxc z`OXfQP4igypP@yGdKta;NjBpJI?2!`na4``BfE>o#943TR6DoxLmgI#fplW;2G^V# z{M(iJ!@`C$?6`tq*@9miaNU8j;J|(Oy2y>~?1gtkd#ZNb?lh2brZ?So)<{TRqbm(~ zD`{`F>Iv*uMH#Ps5nt_H4=!Pc=|1!#Y7}ut9QuUq~?3b*1Q4&HdG6snhV? z^V=tTD`wz^N!Vv29@KXHf~nJEfj3if1U)eiG!h{Q3oR18wn?B{MAAXG&1zm~;WORg zgREODJYhKr8hSEe=s-HvyjPo&YxM@klFCr}JdTdrhUp0l)U|=?@oP!?Xu#~U_#e+( zSBvuBZ-q-yYUyhgo3#J~Ko?iQ6!b@Rbta0J^b}G|L@)oNmj_9<*;R5AVZD6mVf61f z5i8h>Pp706xcvVz9?hN5UPjFZ2y-b$|=wnYk7YniMu^wj#zBdc#knr#KWs z6vX1Ym|$*QLr+TU+D(c%r_N3ihNt!4@Y(e%9bqQ$(_rE<^AqW>C^2)W)nooJDXiAt zrs)v?PGY#XRg)detLRG-{9=qpHZEw zH_FoW*wmkgb$GG)Q(T^yds1m@!=Yt6bMm8?d0BXZg60vzGFg5ThVdtsOF%9?vxmX#&kO3$K z@tc>MJrBI#Tk%(}&LiAEDLziRqs`}jKYc@?5uw&Wh(TRagpU@C|Kx6>&*`$Oco@PP6VNHHP)G% zPn}};m~|R`w@1kTk(6zVu-BP>w%swef2pLo>4>B0WP{N$L+zN}Ge(|4sbz7|9$Qh= zIc}uHUZC_33kPWLV*(Cv6F-RK1e@{~E^!JsmG2F|o(_Oq+is+#&Gl`JKXffkfy(Nb z70RaDfT#JMee0}dikImND{~p5oLy^Ub^U?K?4vh`Y`q8ul^6vVJoi}BBGBnx!T{Ma zaGio><20ZZ#OAXW08Dn-a$$l+tlf;f4D2unHYEVoAs%z%X^FQ7;-IFe3gD$46VP%H zk99|q1XOfU1y*#YLNmi}!xMxlKAmWPZuP!}@2EFkWx1l!*iB{|O#r>JVAkQU$kK%# zap+Mhvlp5C3%_k0wn$_GI}rkfUUl(>x4b4+Y<;IH2|9uSXiPiLK~u?ErBOL6w1@GX zb*X~T9SM9dTn*q=cD2&5u!YstoP3>xl~q46*a{dFl9Lls81;$LXeNv#4UW`yhdD8<|(xTiKJ_+rVt{;0bU)1Vhh+dkhVP!1`Z0V{MWKCMvyZ)d;!+-B}J|3#x z;KlQ@E%JdnI`ZH^h3whqMgPnlMI;K9S^AIpx4XNnRO{AZrv}clnev++RwJo-uk8rj z1PCu5yTbT~K56^RhI=MYz&daCJ9CZYm-(JsD>4Ht<>im!i1gse(v^fLA8~*ZhDt_H zNSDya%zv1!+wfBUoz=7h*tyXHe%!b8?Ts&*L03}VO75fo$Jcv6!})z}!!u*FXhZZm zT6CgBbfb^nJBer!B+*-t(TQH7B|#99kmxm{iyl2Bh~A|Tf&}@_w|-4GIi9_H`Z6oVF=nQ-3iXSK;`!;Iag*s~ zZKt&zjwE!m#lWnMH+Nkz975SCJxTu%^t}dms@g>eXZT3C!~$k4fPo)dG|SABt)q5q zE=TKKhTm^3yWfxTXh;zt^YU%(yFGb||R)sgfOdl%0P%^2VEmfAVpB&k1mT3ivZ zxr_z&SAC_n%eEdz+@e`K5PU~+5r1M_uNJp_PGqOVy**F@k{Ek6TXy@XP3rgm{vZw$uvIK-+^YSR0 z1`7&qcG#)_f`^QcEn4DA(>2||G^gogpSLC;8{a}2PVuyT$;a8B+ubsbMV12R_tnm_ zFDsF`;l+Pi=Hx>`yrEkiefo~&^eZzcNDBA6t_&w4H>ZAmf&kqn>lpS894)CkVwd7> zDZUzVOUq1+=3&b`URbF!GE)~+;;U-3B7H3TRrz@SwIzxEr5pb4A+b0?KkuJd{1(~h zo9dI(q4Sk#cK>lDQ{<-v6UI3mZ#pOsw*(}reCld^pGS)nRW#F;~H0#Hz0!oYXAuv(JyAnh{FaC~a> zi~qt=6WXzhWKFk?>!wxoh!Q@Fz*WvWapk3DhHBouDuLGVWP%%qouyyDFj;3$unHo` zc?#8in2AC(AyLA~r+b%ZaCj-8%TBdB#&a1<3-Pc?DHBIR`naS2Vv&!X=$!@tdWi{fQ|q!-qp zpqTl3uqE2d;jSsq!5DLsE&Hnev$EY{)3qf~KbL?LHGOZ{w`bR5Q0RTyw}EE!{+paq zDurdND^*tslJx@41vG-ZIT)q@D%ak-4c@tcKF(7X{;UYehlu-N3qQB-Ez zG7-76^+eRhNd?>)5fNaiS+gm*W@L%$-amL6LgHP&x0fn>?_ET|()HVOMN^{!W*z0x z{SkkfKPdUxp4tx5g2?}s&jwVeUsvYweP{E1?t02fAJomix$jR=#&1nkR#TF@G&Q+b z4n1*S`IzcH&1vX<{QYSpNppsHk4i^Q*lpfmnO{E@1@t+ewWV;8^XNFyN8bP4IVf>& zW=D0ZQL;&jiJ}}_owwo?L76T$5o(JOTNR})@VcHL7H1|nu0HKQ&lu|F6U&<7LAAXylJP4!MDSy{ua-1f zZnFnH-p`W6hh5tBtKvV*3-n88_Q)+r>s+X#iStFi5j9m-(;W=@-FBJ;TE=h zFJC8JHGSO`R@M@>Dli=%Y;m1Y?@lUQ1B1FkgiLwDKNrvT9WN}TDXq0K6>94}C9scQ zGQGw5J@{B$cU#V|am|F^;4O`)MTfD8$aHNWGB^U!5K@4ybySs=HAj@~e@0mrx zhI|sPBL64n+HvutLiSO*?=IgDa?+IoDw_RHE`c^5nxD0`w|aR+zt6mX{UWt(tF59V zauHM#-s)CuZ=KaCzQDa&S7r6Rzw6D^NUn7~B?vEsDlsw5DpQX^cb}MFbwSogQ|wmE z_~&68heS7Zgf(qA>$Fv^;+bR>I|hY7AhjTh_=7mdPtw9Fr1)>#l1XeeMS0+u3EJr? zU1;WRci+M=X7^256Rlx)u9JkgZR+-H)2jC&$g0_%B+TxRrdFH(5<1y(asRZ_z4+fCI9VIAoO zvnOhw>Tawt|8+AyRjyVCku3Q2RdmmD<+d1VI`?u+ZfnX-#p(Xpt4m*u@@KTaC-U6c z&+fheS729~;g#IN_oidBAW8kK-wqZ^uRx%=FM&fbWubE)pV!+ z@c@J;mdsS2KI@oJuuruinUSyucmFvnj-jJDNAN^%;Ale&j#={ycJGrLhDYJzl?_ zahPtJ+*$*tDq;$9gJOnDoyi>|Q)Q&g#YC^ZFiq8dgM7F%9g^@GGZKmJOrK)E?Ob}* znL?LVlp^f+6{ptUIqBl1pDV4mXo`7h9KGBgXVDfTBPW%tPWRNGWdLb z_d)je!&v(VdnVFTUw7sML4$1rwq1vz#Wfvt=8@ElT1CRpx0q>aA7Go(X6iHA)I~DU zRt!q`dmS;_?3ZnA?vEcfeY$$f<(pB3UH{h;DK`xbGj+MAUXXw{8$y#qi;#?CS#qvR z33L%paXN1;4$Jl6Py5=gg_tmYT{qf!TG4ExIg)M2P4VG&f9vzlc&xqRk$ZO~q}B-+ z4G5k6{ENae!lU+l>79dUKTANk#7VlNLvTRi#JNQg4-<2l{UrXUj{NacL_}hzXNire*g^H|tj!HQ9zsm39ie>7?%XeK4A+wNzM6gI39oy-2IwZM^-!; zu91MMHIT#h3~BtYOgwKG#{I>e@Q5w%n)-Kk?7AwHNiJJ%W?QSDnoo7N@oBfbH@xe# zJl+ZhA;zA*veexaa`-ff-Y%HhnBMbQ9%$2z;V-;;lhkBs{&}Cow+5OcK(D}I5k!ZK zxjz3c!%rWuX-$jPJz7{AAx@Grl(_J4AsLZkfFe>q6+z8>KXRV;k>*-GZgX9AE6Y_9ifE? zz>$2`&s4#OEHuUhTs2z;G;4JsL93U0ue_7Hfo}}mIF*?uMu4{UJ1)x4mbC^>>E)w% zFyAN7xrAQkU>m%0vB}l{Zf8E{8UU7Qhd2p^XtRO*CsSz>!eNKs!{%yUbGRPkkhO3l z`6zjj5->%{y=qR{`mzt>zgs)e_x>p-bqSysyMPwIeghyE32UA4i4Vd!;V`s#F;m7l zgV2M&>x?>l4%y{FGFDDPGKiuMzP8_O@#B*stNUn)hiPG|$MSASUJYc_A#AG=g}nRH zcB;)XA93dS%4I)sx%Xy51e9PpOODRyp7Y?yfmFCYd>6DBM&NWBD5$3rQkWXKD%)hX z>q40|kvN?iuu3ReR4zqS-T-J&s_IP2lw+CL(j)-HIrf_F5%N1?7*DkJsF|#fg?#S( zLqCB`gr}&nFS}1z89V%K`&{CI^;ExUiFy>hU> zbRXZXYpo?5v>}iPQlUy|yc=hx-Z zaUar_RFB$SfK&*EIJ}TIo4qC!axhr+3W0}PA}ZMdI^`dkZQyf1B1V)=masH8Hs)Z7 zU3wrW23eaD(MMRQ^^XjsT>;zJB_HTPD88B|df9ec`CbC?t^AGTZL|D`6NCnbsNLX6whS}Tym{HKG*F=oCXw-<8;h~BaqA&)tTI6zdag&T#*YA`~ zbH`F$R9_C`y+d9%eVaSI0+2F|tclGCvC&6UYgQaOqHe{x6$oshJDIolc3Ezq?;dbPW& zzBaC*MAS*qXq%l*=i_a@3r9ljnqX9VBEuoiFv zY1%Y^V#N6!dL1$;l*aLN&fpoJC@8z)2Eu{c)&I~`0{lW1jA^)c{z_3;%(8=j9Icv9 zzyap^;2slbMm)jjbKb<#LEXOHwQXL7e26K`fB!#}sEb41yLYPCSmYy9fsNI+*X1yc zpgW&+`anKz%y5I6t_11^NM{4Dm3@%YH7a> zwXm8^{n7L}58fsl)_I>RgQ-`bKUM7YT0Hoy8HU>TJ%?tpC(y3O{Rx@q6YV=ccs}F@T1e}HnXsDaI6G^E=k4w zAm#qg);nO?oy-|8iB~FPgNq|L9ggSpFSrQsTn^??W&$=^;^$lJl*CWBnULuhH2iOd zQ?deus91U9hHy#V{^l^6z*M*jwfG?GVQ0$8Z~sBa&P3~y`v_DKS_lg!Z6G>3<|J(Q zSHsTI=!7V4$r9WJ|?aM)Nx5b|k(9Ru_$<_Tu31F?ybindnu&~ya zVVPujRth?5&o|ZoBK9zCnUFz28ms)5_wJPU0G65KuijcC=-_L8bgXZAk#BE?PJsfu zd!J>*t^(q}}BdS-fCaz_tA-ia(b? zSB80o$FmE_0@Zj8c0j6%CYiZY{D9pHlXe(#6LglGvw~r`( z$BKPyfF}k`jYiI^anN_oG28rmM0!~L+^E18xwDy`KyF8kZf1GDGEWQG>J z4qMSUK3HBOCEpLtfv?MFD)&U0*wu+Bzci0 zy+-jHpltC)C{YxmD2*e*dVf_j6?EF-O)egs!kZUMX4&k#hqt9rCReW7{o30|=PaLL zzRCpnRZ4ZFo6ng|sdT{}bg|fewSi*K8)@}A`)r$DKE}#5;3)Onou)zzbln@c+hOxm zCl%hI+_(0EiC2vYd5b|0rqZl+i>IXfv#3lQ{Vmm``UT9!O2hl{6M~Q4m{O|-3Q@;_ z=#18s%Rd?^S=X7%)WY7I!%F(@YuEoP1}j*taYWcUI}`&J{rJ24jeilrHLM}YCrx^8 zkAWkI`&y8Ndk^L3CtC&&M0r9c*l{>;e?Q)jplEt~_iNF^0*<2-SGmUy523cW91M^K zLA!3Ktq}YP9nzg70T46lY6_mO+5x-jk1UE`Q7l;F7WKX1);Twr8*G9f)5(Nn!z}Z_ z-%8LS)w=&|ZDhRrYYCqHpn- znPZ=LQS)~&kd}tO^#K*15rpoFbD9*&N{|MS0AGY7Xh2#a6FNFeGCG;7d*?sq;x3Tr z4Ti5X&JG9!)ZQOZaeGa)^E^?=V%%BoIMjLfS^d*Scwp!FHzPcbuefJdGYMsa-H{k+ zRO7KfANK4Eqq-DQEG2 z=$-UdiZ!*{*7=ad1VSWWaudbjNW95ys&ZycR@9MmupWPJCfdyl9o)^mF>v18)(5ZY zn{)xIDVf+o(%B9@(8_DXMaUOsw-B)QvaUPQHm3a?;(*Hv0e&^Z4jGzJS)mnTGH*8y zWg8CpZ=6d?nbH)Wb%TS)p((Ll93|lL!+3MNl~Kw|JVHNf`F?am-=*sY<^cMBU=1W5 z%i5-fPWk8UA4*5VjG5&{K?~ zA0VXrhwQgT*6|I;7W=8M;wru<>cKHz*WkI{&qDMRLqrCs+8ex?lGLa_@uyzI3=YI# z9wMtl6{(%NHh5G5Nt=xA}Y2O^y4$;wC@bi(sM8np=1e`Z|IdwBtH z#mkXjHG^p;65}Spa`UXtjcFQBNZ;0kP0cMhUU945-v~M8bWp`Wqfl8UP=#C zr2s6PJ%KzSHO|H|aM>E{eOrXwAml&FpFaot9D1|xT`(YlKn>IO8c{UvWt+z3^eerD zLGYk?k?^p9)EguAJ17!|L%S60GViI;Jn0#iF6uM5i81YS6NV6S;Z904s4S%b3U)tC z%(o2mACv7Ui?9*^R5%D9KarSXbYAT8Kx4b2l`_0HXd7x>yqP zu+^gyZt@yGvj4x_n~*@jn_EyfLf1}LV@Jpq#=sEi0XW; z#Ll|RF-w|NcCcXQaVh#A-82prv3p01CTJX5ix^Ahh0hWtPIe%?q)^+Zt8Q}VYS#>Q zM2Y$(*^y}lD*t7meoU%tj9e7YjbLn;YGo^4j4P=h;Dz4|S^bydj|Gb@IdY&B{l+@{ zArHy$(X32#CU^utyed6h_eO4Ij5(I@;uInhD5Ob~{IOL_iiG?m3cgY`A0BDKhOp$f z{2|^(;mfyU9?MNw)r%2QE)qQ#J4k!=m)flfEA<9z#7p+MEK6^L$AJ~@-&xSm$iP^6 zY!kkzC@sO04r9K6e0J~ol*rjCxHYk&kB%t;)LodCPDX%~E}2W|d0aNjOs3A>zww&u z2`hV>O>z>62f#ML2a*K@af_5B{p$IZVDua%Mz{rR*)c9rxoy{jdYn$c&iKWnps4ne zI^+eQ0T1tAu_Y{RKArbCH=QV0kOuNP;JktPF_FjZAGyCAN(@i~^;mMUl{0=%38M+` zTlR!Kv%VsM@Gyk%h`Id>+F!=~)$@|5fy;YZN`QXq8kQ^hAb@(MRLE?DOvP_*rmfj6 zbD0yTP{=oLeomPBW+r&I=^v|48yb$B2pV_q9g_FhF(lSoeTo_L@f%kta*3Ca3U z0FsqIgtECq1N3Pgjmi#~j`>CYgQfjf$>*J=vqeTN2r6bdoYcFC!w)@9J(M;k}Hw3-uI!LjegdV+i= zug59O0L&fTHAy)cnT6UC01ix$NGxlitPeUDMqJnt1xon~fm+Bvy67c>jeS`%8O2GF z6l`ib{`81t>-frWg&T0*cl11S&z<+7bIc`yf)U`YTt_%=@QK2$XDEjTvchU>#Vujc z0)Ue4P{*TVek)}(%KY^4%g3Og z)LqJ&M-(N)O!HWWOB8kCq;2)r*#=_Z8rdj7(;NK$@};9jx0I`p#DeZmn%TGvYA_!{ zwC_4<>KUx3HqZ38Bw*7TUBX7g|M~b37`=t0`*P>Sngs8HvwsP#a1SZ?B84|17jv7 z#*1Vxga9~JZPx*{?PyOiRz-U_qHme$U3iG4-+t*w&`5yv!qC<+tfV%rd_uCfZiyo}7HME9wSd`y1UpxM6N80Mjp+dL#j$ zzv#uf0mNQABmV1$XP1XJ3*hK+&hqv)#BoRg>pBP{T=4guk>Ps9$nenb>uMJ?#pPYp z#I6sS-gOBLFv4VYD7TCbnPKhoafBF=K?KO&vvC*q{jVH8rbE6(JU0pP&sL28x6NbUj54)eqCizmJ9=|4AieiPDP;>X@p;Cc|ZnUAzY7pTU zvtPl8W+X=IwNGC@kMs07OSEqsdh?;Ar1_;I0N243Zpx2)@*QyefHBqZ!n-->5M zFMydnNAP#6rbcZVo2(;&#DZl>O|`G)H$)_+$R%V zNCNa^vQ*k;Q9Vah1TxH{+;Jbiw34AEp~!rX|42caP4rA5E%AT zo&CmUF|+APw8q>kYG1pEe>|sE`=`zKJe>?D1ev zXd7YX2o3Mn_75DZV4~x4n2e%%*bFdYj^837FBoGaf)amEGReR4rf%_y)zy7A*bqsfbaF|VkaNFtBv7AHp!r-HHWiT3OZ^ujNC}WtKiU%5q5qsC6p<+tVu6d0 zJ7!MSrJr@K-`jsLIGZ41mfG@s%FYgcXgYEyZ|sMUTHp2k6)>@o-Y$V8>}*Mu6xb!* zrM(I51gd>y4Fy?v^zI?bKo#PDIy1KSsq04t^w^B^IYmT<(LJkoafwaMW9d&qTT{N#xMR?SrDg;Hk7k)}ah6-$uCzd)z^waPDqH`*q6S5n$-uk`krbiK zvWIfHR04TI)o=Em)oK^j-V}<(r?^>72`7$Uzdf1u{l|e!$@S9*y38N~mrpp)p#XuS zz{kcGj>PQseEqa9g%ZrU`}0@4IrP>%aSR>X1qUCFjJv}(MB3X#aOjYnA~MS4bQMiv z5A*3*_9aXo*kjxXOXK570=~E5bHZf}AqseCKin1iCFoyizzW0we3Jsj0e@SD37?}% zv>=_G{P+w9ug^xnlfkmkb4~m-f3_%gmh_9qQHfbSJ{Te2I4VJNwy;b&a0lpszfWoM z5wX|Ei1cQlHPW<%<0!G>qbDxr;Nz=(?N&`tH0gw+0+N*8+JnOyTOl%lL{fDU~-LJ86=b# zL4X`Psw29Gd!{?G*!7Ab?OesR-M;p8;|034jK2bkHGd-_ z%YU<{U0GbjgD353Rp6Z|4h%{v#DN;c*j?T8Cshq=UkoG#)o!Uu~#O4nhqfgSM z#q?x8R&{YL5#b;UZR$*z;O zn?z9ikupgz#uh8z5O|TgO%^UfFo+5+>#+?qYR+2kmO`1@Hl(4pL0w+XJ@nHT@tu1a zg4c_5ij`bkT&f!zHTd}Ws+*g2sK_n z7e003E<3DpY;zDbJ;wGUs1VwZZfb=a*g8;#e)c0KPUDPnf`#aIYVVRx?r;UqpIlH?%3mHF(LRxzy{V4w_j zWzp}whqc<*ou*In_9R=wl&m~}xYe&R?77w{-3_2V3OWFqg7YrDM9`V0aC+Pdx=o-& z$PUi(#suwcNDLrFUV+eE#j)SMfUbJ7ki6!#P_g{|Qr>r&lphztK;h&C)^17LBe&;G z*C)ldN0uD?mS5$rU4F3$S)ed`{6m;AloUVuS9m03^svQv3VOD_0hy2@cv2=l$1$_E ztdxB~3a&@hh5|h6C_MycIafiN0JrAHlD5^TjsgBReK@Ton(^ zed{UQ~s_i{|bCBEWb`krv}!SAJ@A&!PQ zftLX_S5FyXJ+P}dkLk}=p#{cxCgF*q#;J@yGWQ;tzbCkb#NnX3rGKTJ4NN&2I7atE z^cQ%|xx{ejSp5^?OAq>?MmPBAjomRw9DAfk9{hvR>8o~~bNjJMD8_^7A)(==l=TQ< z6bt<2JYA$)$5@YYZ1(2=2CZzZ=xST@vD}rP$q{r-3&TG4IwOwfmALY~o} z3^Z&$CTMF*qsAXvqr*T+l~0ifYIVfG5O_3tH=ij*-q9-=e7f$2Ay(F&@&D-7(!Khh zH_+08#Ec`@c_X~P^$E_}9wHPRnv#_;wBiJSZ}&A?;}b7uF30GR)u~i+Ac! zGAJ7#0=V*C?afh9X}-PWyWwrj6{DtZn1S~-?aAk;i0vX4w{}1mITeS?tly~0%lW7L zpQ?&?seNl}Ywx(Bc%vqEt@xU$dd)`_uS{YG%z(oh|4SRr{iJ|B@|T;C5C{b+4+@gi ze=RBu?@W%~5o#$ylE1O-d<%ce{Av>#^ulf|x90Zf(zmO@?||ILU2>Bh-pHDz3h21; zLWez`!KrkQ3XZ5op4~i1h&mQr7ZT$4yleQrO8xLeICczAL@tt@C+&-ZithMTmVQVt z`iR-joL7?D_FAUK)|%wy2hG<7mkK0ofKv&OWoZ9ND_8A3T!rqP4+Q760EwZk#Ylo> z@2q!!5^$d=L9#mKwWsjU?;%TwIw3NMoCB1BzfikMCpTL)`b=7&?mpLtWAaXNA&QVE zsA8IJ@wcvZJqx3z#{^6AzsLxXv0h#7g8xyjNX$4x=xavg<^!R89J^1i8o=;)hh~&9 zBhoc!R3E09O#)B4bsui?=lm;=;6$j zri;?h$B&VE&C$b4n&eZW5_wBw_G);Z=M}p<)L}Og4(CLFY$V|P2gq`V06OCZ-{keZ zfQ33a!>tkWv1dZQu8o7?1R^7`iBAZjryI+AVvUQ{>k)Sz$&tqC9Yn7LcM2fgFnJ4S z5(y#D3T+i3LOyL(Se77d5R|vxwMumDzBJ_B>(kCF=>SgxSJue#uTINVX2{-?i~Ot8 ze$6|7ysjTnn%`{D1#M?tZ@htm03(ooDW@jce(bK-%_)hqlO5MWT0+PhY@O~FwD;K^ zm=RMZ$li~5wC#c%nM5KvGySaZ8X507N9kik4bwY3Xf$Cpv^udOLBD;F(8;d2y-j^gM7o zY1{3fL-M+E@{QZel%Ssh=DEn8`h<8}i1Cz0@VbUEe)Jvs>QVC$91aYQJ;fi18!YG2 zaTi|1is8Pc@>75foyh-6&DVl7=*1EWo>iwRR@}|Eh)@33xo$mwHeY*gx#vglyqeJo zugsfzhQj;JJMCs8Y#l-l{B+Yci@G(6?;Kat#7G&=>>?`f0v6Iu9kqo4hQLM03vQr? z3pM!o>)tDiqNss}?1Hj!mQO(|VNf_2B`w9Detx|jSoJa>6uv&;+qMo@mo67%6!7nz z=07E%7=9v<*gPJ2(cb*iT=o0ZrNJ-EBgS*@#;eG_6z>`a0%t!Nkm zwMm7_?t{>!Vdw>sO-baml0iP)IQEx*`|9KJy@(nwNaWyi`;t|*2q6?jd2JFuY!!Jv z>zq1Ksmyq*#zLfqUvq}7=vH4-z^=1#h9JWD(jutlI&S4S67=jLqz3byXGn*sE6p zpFV#s!ufqLR$u?yE92ztPvr3@Qod`NJ6kbpJiBj~%bA%#AAjX9u zR%ZxPivM!C`P=ZHwmIv3q0zU%=WebX1?F5=fQbUlGk55t%`N> zr^uJrNP1b6CQ5(WW42#;Qm%b)t`=4T5p&&9Suc=<(iK6G^qK@=wPI7k$Bmi zq~+T>L?4nIzkAg`+s2c6yO2%cb6UYN^qZCdslH=}E=N!B_7Nf-7?E!G2W*tuSQ^<>KXue=j&JF0?UW}tWxc2Nc#+}(Crq6*l@k3Uk!_L7Wf_OI1X@h znM7nQ!WitKmbPz;J6|UPul%)?K)^&T$6-_M_os|ZyuYn{Wcg*sAO5NE?XNJ^JAL^% zHsRIz%fiSp5Q8#EWe**)uUv1yY1m4&XIvDO@;=^?kwG1^?-1S!h2i(WP8cE$&&*;- z78_PSMrL-igf)m|sAoe&jPNvXzihEd^m?V1d%Iz;7)SxVT+KdnpY5QLOZI7-pK|rm z)?NZldeg5~(Vqk?YzoV1HK7oZ6PFnFbDz)Q5(HX!4XP}ON^j7<67;pk5yM~j>{9m) zVEsZ7Ug3FHXDceseHSx0>?4dI*E+p0Ie0aq&&FXD6aG7PQ9km3@uI@vkatJ%P_>nm zqkjrXyF-PZloPpy-M;7dnCA@ZY<^9PnS1Fj$zx;aIfi-iW`oWXasir0+wKoKyV6Pv zhQ6nBXmeFceRAIy$6Ps={^Wno6huM0eiS^y;toe?_Cnx+ALEUS^;yXE-^vd<>Ipb3 zaW#tJ_9irleGuPsjCKs|@p<5gdHy89^v%nc1%=O5@JSI8DC!Ls;m1VhQce*927+>0 z5$9Ug@0SMIH=-()Q`Yy9pBBFWf`B^>04_!2j0q61R*hF*^5EAvocA!jD|PAZs)bj_ zWUmoS&E&HmXrhcfPJElM>H`7Ze~cL2HvHT>3aa!Qgr5$~4fJXo%86!6Ntis_81DG+w05~xcX8#!^%r|K_6fhE6&}Y+S zoLWj@!5{8cx(@(duB-nZp{Q4Qb@liCQWox5&;9fRt-^8RyfL*Lg%)4!fUzsL#;*Qq zkMi*EoNm<%qxG;f8u~MT`=y7)Ez<%mNTaKZzYx!R#&fHUJUzMcD^!>x+=>2$zx5c$ zTxLu3_q6L|#dvh8hjlly3tgnL($yPpB;B&G&vrbl<`*HU%FS}E;deF{Fpu(lcodYi z8~8?GOQX4RnZHi`s^6RR0n2pG->E^gMS<~i`#6hs%S($~ZwL>`;cdfX2SK|sy4O4z zi{)2VZcnpLt`uwLY$bo0TE55pu~y)>-|HMB9uTOhyj%)R8KH1)ZXiw^p4Xdg8S%qIkCDdRfGoju@w4`E z9=6H<-wj1!>JzD;Y;ke%^)`H#LNK!y0SA$;!;FjP`;$i-;!19xR#@RX?GQHXPo!dn zbk^N*So*ztkK+hgM=v)^PRNSCkNLSF%aa(f?jp}7s`q)Pu_R6>h*g-!jQ3L0MXG=CiQ?}^ zjD$bb%BeKC6;0tB`M_asdGhe7oR?XUy^6tqMcAXq`}AtOc)sI!xIPMP2IDfF~qE4BM?B-c66@E#0bYPVG4Qa9r|oY5;)!#ekiv| zv5Jg9FehId!*LIDiz$^C4$Qh9K`SzN$>i4D@tYVGviCoyNW~hN*Y}!l4!_A!XZeNK z5TY8HCd(km_iWW*mXfn$Xr&JIXWei)jtmMM>92HUIbVp$+yP`$@2hrtr)Cn_U0((UI`$n=usb4xQPZlq@FyA4{QzTkP22(&(Rle8?Gc97cwSIF#=mQ#x29&6!hAx9FsoGsZOR zgfB6Rk#$ljdg&c(ICd02($4)5uSZ}$5oj@T&%Q!bw(ZKdWBMjy&4!J7R9^5yFPwOv z0Q%ebQnS&0Ru%JYy1|JVcHO!LhtaWAS&ho=JSqHA6W8x1>WT12^mKiydbOY#y=$uZ zvCw_=>_zm5fH_VDK@J+eq4K1_mh{*r8-}c4JT&vLFcZDqCSL;`EARqu`}opEjt3BJ zjkQ7ZH^0P$*ZO*~l|7t4*W8d0T+Sq^b$P`QS{{nM(SD2GgG7mzse$i5cdd~-x8ter z;@J|+an$7FdJPkBTz-)&8^p;YebArdf7@}@Yw%F})K=N4j%&cr5 z>4YKnwh?>J;U*ghFMEvGrx6VYtVJz)-l1$A*|`gViv}ba;nk6+Q;dU808dhtmJmOW z+Q*{ubfDo>kyiRjU*FRvF*F<#^!IXK)wm{EGK}c2gY3k!EpWfF;JQ1jQolMTgMVed zESjt{A+TRN-7JYKxYIkWY1H(=pv@z<`&w0RZRXD1AXoT2!0C;z;lB=3hyI85`JOZ*W2oYZG0=qCrFX0-yV!J zvbz){Nqzsmd3tPT7LB*+vf$Oi5{}NeL%G;`c@N)o6LA6WjuTm$spZ8w!pE%tx<~VG zpp3|mq=;!!O`C@V3ir>lQO@=9o-YMao8M(Wt)XFqH!*P=-s6g2Yo5T?+5{idQnQD-bY+9If~MlxHTY5Zqaif#l6c&T|k24 zt)j(kr|{wDD0@Tf1=KCr$P^ti)!k+sw>}u-Ac8W3NO~+2s1x!NAQzB08a4~LM*oHj z09L?Fl2ugo_{|RBSPXK*LrGEW77DJglzAPd(wP{7tnl49-cF?CDRc&%Ya$dEEmjYt z=6lW#41{?(Ay9iSLjF_r?^AY(Bde>IP7arQZD9l=DAlW@!X(s#(x}ZZb}MxzlzJ5p zEbmdKiI65mF$YAGGD&eb=Mo@$K&;N7S;daz9KJ}wvKkft#Pi=*6@S?-n3TyT;w;2Y z`s_a9yDPu$m;hJswlRyU@xsf=6Y?+8OBl}~JAXl*_mt(N-N955^qkfSh zz%;z<4dKYJ!k0hnl^xZr>6p7J&!-7nIlNGAFv$H3)R z*WW=yDa>RqgrbrZA-!AKek}j4N+-_;3^GhC6xJc2cTQek(SKI{>z5kzVL>aK6U0+B zuiWOc;_+00o&9PmSNkZ@woFj+x-kLL^TQ{%`m@JhmIV%^2ploTHC^j4kjjLdd~MBl z4d$XMic-B9$`Oz2tztHwOD^IA3ivM>i6+Fm5^2`AG(s|l2tcwE_4*5X?I%ud;7|e5 z=4l66SHd-pfX+XW5Rtd{pOxP{JH1RhSSq=KDiJa$*^aC`ZGRpwYw3f@*I}Y{vY;qc z6T%Yx#qOMgMd5W^AJUzInf=alI|YG##Ku|x{Tf#7#Z9hw5K}9WCKYx$Hpve4d{SY$ zwCPrrKJUj4qPbZj9wR?G_aZ#& z6&$MxsSn+aQ1=LQ1t*-6-P}8cIm92=*e8#g-s`$p!(K1%SiQ1Per`~Tz%lhwqZ(_q z2i8W^Se|W7mW}mZi%v{VjLm(iC2v|8BOf%!rOP`DUzX6h-`77~ ztMs~GH$!HUeY)ZqZ$ertTRy;+apG665wHWMWSQEZj&UrwB@SRBV4qgs zi2Xdzl*Vf35z?TIK?t23ZrkHpHwO#o_)eJS9bEXFj6IMoo&fo5$h}sGE`jt+?$cvY zQ2ecWP;w%+0Fk8m{IOkLtFlNqN`=;u)_S`#fv8UeHcln1e?L;~_^M5149%_GhI8E) z2;du}p8(mN$dW-}#-z_q0(^jcEp&3A$G|G>6K=h#*R_3k7JXX`8* z0wJK}8jdF7s3HDP-Xz>-pJGbt;-qQ!;SIg#{$gH6U~h#AW?<8xU^!0V1%Jf-!oy^? zbPl0u^nu#DUv{~_Q|g!=%bi&=ekp8FMGDwU3cizPQo6<+Dyqk zGf274B@@(vL15@q(jw5ykcgrd8T`XLFA>yutFLf@f;BnypksJp&Cv|F`mo_RyMo3DPMMs1 z8k?rLBr%Kww%W#HxhHuTO5uP3@gF!JJn7P@db{{J;;A4sB3JOJiG=whUwEjxb`%^` z99R>udXDNW5yn!5lgah7?y#!}_o@>RP7b{=G)71F&K^9ry2yCxUy#!Yv=!YK$b#^D ztgBa!1LESi*>3t_5uc1~k5-wGxbJV2s=R|=eyvm(2ictuBcsr;!40({WPS^Q^_YEO z;XBXXrCgk1=UQ$_x*qiM!9|W!2kBqZrmnpbDaZTi$N9QDdulFZAHS_O$o<@;rxQ^P zNlLJS!WX+KEfGE>XPWH?5TBlP#9rRyMf@Jn2!e}`zJ`bX{QK=??gW?LA0$@Xt+dS% zKwCyrvDO9(7pt2nt9;&yx5sdBK6K_;GG^jj;g|Bq?0z2dc@FZl$Vs37kFfWSr~3W> z$Dij|$B08l_OU{QP&UV2A+obqY1ljCi0n9I?@=hSN>)ZFGg}lTD>GY0#_#fam)HCA z`g~sB-#3Mf4hD};akcqI!nJY<(ZxMR!LZJdG2k9H+@pE(SCU+>yIK^$z-h31o9Pf-}Wd2>%< zruM*Xwxe`2x$xeY;J4%XyqJOQrICf1@Uue>RkjlUr@o>$>wcdox`PZhAw6vSY`%-JwK#rN`qTFK2$X zg$1jqSx|woG2p(`(1)U;&&Bz;mXwZj$tk>P8NF9yXk=pr56r&lm-lO!<6;{ga^Q0< zl(nZZ-?My1ytWMEWy45r^~t-<_pxaIQh*lR3Rg~qKxg-g+cZzlnq3&WftbxNd|d%c zZp7q#CH!5)vXDvrZk&)CcjC8e8?&)P97Kd!x9D3UluLfu5b`l=lAp$wyKvWw zm@S{7Gd`7KXh{(YboyF|i-b#9JgKa?8$o;g8R|V!Y5LSwuVH$_EN4ZM5+ zm8k+I(YuPO1$m#E0cHJKs`yVkg%S|K{jQ4pjL#8GnPL*uO(P!HrlY&7Lc@ddzq<0- zO6xQ&Pa~6W!h#z$Moi(6ipU#;C=>Ua#Uxmtml6?nfs`M1lkbKo4|E8Aw#fM`OfR)7 zI=u;~P6nHbep9<`ts*;LVd7WP7+WE9D$v&`!^6m6HtD@C#l*iiCQ2X_*UlKXoxz265@)LIz}zwYk6MFQBGCIU8fbwSrTq$uBm_K*K0oaQOmLd?kx}P~(&IlR zQ+yLqz>2GA?Nw{ly!FPLMRnh9{rwD5&<#*rjvVDh(P4VM6YjPv z4QrR=NV#tTOS6>L#KUxrx2D~n^xjVX7M?-~VU3gH(*Z4zy9fEm_qBOq(sM>cdPr^k zhrnHWQLmD(1}nYRXeQFZM z;Q}h_P7rv9^(RFme>n(BkKB%nSnN)6bti#V&mONPW-IaWL@k*TQJI_0(3F1UimoKG8PTKGo*j`N}cI z3SRk-_KJMu{u2TuMj_?~TK*dTa*B@~b!@nd*qhK0Jv4_gzGo1`--Y$KJCe)T|NV>TqJeNW7i~Chf)I+u9 zd}`lO#A#Btq5wGKMa)|Dg#cRvjxvAW4I2kSUTU=27tyrELKSzDYabsS4>MBa1m?`| zPoF6@IOoq?$EFHwF18%_W$8G1T-kO)lKHT*TZF}tH2j+9aTM4V&xvn80I|Kij^N6l zk{Tt@i~F8Xv6u3iASTKq$cC83c1Ngp#F{+D?^Pjo(?M0yiecUQ75ZhE0sb8BpMcUw z8R$)o_Fatqv7<5~T=OhF>q0@Y<4m+nOIpXC(kj}4(%`z8^ku+5GNSa(P_1XF;^5_b zw6tykha5arJ5*I0W@Bv~#N@wg3Dn_bTmf;gfrX(=So6tl)9`Dxt~(8ln@SdOtcZyf zz3wNMe4bg2C0Fs3i?&Hb&-c@?qWpO^gPKw>zscY@@^d$!Ni~rW?x`UAlqc%5X=QTJ zk~PNBCp}DDgqVI;ZsIU&i||t`(nkL4nT}PqrbhZAHfADjMj>-G716qb;XfD7#{M99 zL^~N?pJ7i6rL9W7}76<=lm?xU4zb^^zT*pDz8vCWwyUCe;&? zPQ?=!!;l@{c}HOwQtkvtB(+RY7&g#c>ysxe=J>TkR`4er3#n%(Bbq~HlpK@~mhVu= zQKY3CHAMPaymODRQcvvIKX*{5&Xztp5%z^fqC&ADLR91=A|>C1chrTWG0WHEdoj9L}IWW5nBHvW*Hl5Xylm$(BUM@!&7A9!7+J4D!MgskPJcp&xdXrap z-JE89qTc&*rEo5|*yVbGX8(JN_$ENe+1-4%c)H+^$j=$zb|fk*F}PT=DBQ#O8uOkG z{ezy5lc#-PR-ZeQ8@`_RvncY+Y2dawd(IrPWN9^#gtczAdo-up^g!HaADDs1FAmCE zB@^NP9lB14d#)*2w_WWlL`;Y(&>DP5Ov8>7EZ;ZA-~{A@CZwdwN%qA~9tW%EOK4yJ zDXCCp>A3l<4-`68FWcvRHtzCvPm^AXTU2xt)eDa4HzrJH)-bofdx~;c2!l2!hy>Y# za5hb<-l8&ix8Oop%!OMY1y!t(VnI9}-l|CCw{latBnK8Ib!GcH{H*UPDuh$^> zxmsY|7OmRXqkxBGy}e+0cH1`wKEbKaR&$g-SMytM(d52h;$j*q`BZn`-@JS#)%~D$ zT0GaH_D%6z=j&bfubE4tt+u5=Z{eAP`%&=dWvJ_)yt2n8pyM{W`8^okrJmH4Dzg&l z(W(cu^$cnRMS07^xtBT9$$kec0Gxn;Fpff1DOWzs=43jE_-NvtI_O|3oE;oW zj;}J^a=>q&464t$@#yyJa>wX(zehT!GdI@wT#%BXUL>}MYpqt<-$v>&e`!!}4NU?3q z0qFpg`2Q=$W#miEM0rxU0Dw9z#=gNoc}D40*V>UV^C1bgpE|J?D*8d96}foUJ7s1N zGJ1mh@fo2VCK!&0&9O(K^ZQX{TE7boD6obF`%CdnFrt|=qprS&B?u=2H_*2{*Q=vQ zO6~)@CiLq!TMi5iYM>~=ha=_!y!b-1LQgc{mN^Q9C?I#Nz(RtXq*$?-vRl$PPq?xp zSk709Dr~TqToM8v4rz^E^J?@7mVtD0#>|Vm>+84oow;3k<1|}-z2khXw&by`kye=-9r#?5f8Oko@Y3Epb&^!YenaQT$405R(leAY!aqKlyhPF&Kg zgXB7Y?FsM0I0N;5X{Wf~Ax%IFAcIjz^xYdFiwTfUD~$CoblEfTq&`b%-T3^a9ioLw zP%KnEP>okClmC2<6dcgvId?et0IQQy5b)Ef`1gC2i2`c`_m#ThF{ZDx8Fv56V zM-a6;#-!RQkjZDBMcikLt1@pxUn8OEw!MP5AlN2Z|FuoTII&BkDyAsU7t#q+>OErZ z9MT6#qbWIdN+aZDP>>K{l8e*4tK^469Y;$Go}1Y<><2w zE7j56b-x3M*~eLnEHeP_h6M#3f4BAH&(5AxpO1OdGq^i(sg0nqhYXl~_CyHjf15U7 z$U+sALE*XUi12{z7hYk8_mmLvXCWS)#)X);yF#E zD1j3h#$N=DqM`Rzzw*NV_LAF1D`Ce2yV#k5*{(Q(`u|7@UR6}c0fr`+1PlLi?`miV z3dcr?k@_L801h6Tu3W`;;x*nnl}Qrpqcdv6vgULIDy5u!Navs?;B88={Nrtc0=dDN zWbX_Oy}Ew4Y-s`6j^!sRq{E|5>EX64J^icHRsX8RgELT6cvt}(8_7_G_3gIE>_TLY z)V4oBnE=hX2T=na%X=|CzRB1F7JSddio^O+@I}+{a&7pXMP|4tbQ-#R;WeybpK)8U zzRBt@TM-Mn+0V6|%Ac}CoIQQq$iiRM4&m!slQj4`>9;Ap$RGI5woKHlDS^U>ff&Uyzc~-l4q9rr zHwj$3HGmh4Kfx?>5()9#lPSs3Kuj#(7sD*0350k#(QZoeceN@dK(-&a539WRcpNmj zmTz(3?OVqQ%|IRXJFi74LUQefyNMRF3>~@Ix!|L5d<(yh`yWL(D9SFwH+eD3wS4xx z4MTA-3L?~rjwJX~Cu{JowA6a*S+^gbFJEAIkog#ZaXfbF-_(Mfx z<)Iu8rE%dDxpk!-f-;_)i0^%yp;f<~CgF|Kb%z2O;NV7y`C8c_o3?Q9v zNyg|)Fd|Tj610iG9WY=O0*P=hs8T<^@vNEFGQkZ_WfLA(6mP;nL-Z#e0!NXOMF}3( zA*op372fUz*mZgIKVsoW0p(>;Fqee8DUzV95GPpO!ucfr->x!#l+g45$A-F&Hk$Cy z8(PE?>JA8>Q<fj?inT%posr;wA<8w+SBqP1IxFgSm{;7Q)$8`Ofu9IXIV3Uh#JgA2L0C)2s^>U zLL3H4R#ng4XQM@F6Y{>_0z@YjUmyouMS~XQ5}J^&O3O!@qSd2yg^j(-GHwN}%@`Gv z79(`0K~1?h9VYncDwe&>g*4$j> za4$!xCXAhEd(wow*PWxU&7FbB2Ug=@I1zpPQ!2*yBr^YHNadF6D{|8=#`)9EfgUj8 z7Tu<=zvxe2I8|lb!5M49KPMb)a(JLB0+nbVJ3LjceLerrv1A>rXy)#%8na2IuKava z&`tikyL^(ZS7w`@E$^^^jhMlpt>k0#>#Si>Mid>Qb_X5SS-?zo=k>j=bDW{+n?tX~ zASv<(OkI#A%q9o;-l|@PFrMFxg$Z602vNto`i-<&mdn=**|FD6g2f@2A7%7Z37tZ2 zo#}zR=X2yZ%Bpc(2xg;A^r1;>;hMZ1pQm`pl_ns>Wwmk&waaNaG3K&hgue`*st17v zg%i4D^OoOfc?;#k+gtfQ<&W#Rf;`y(h%PA8R(qozQ2acYGGy~~o;Mu9Um({P;Jfie z7rx<}#b;W}?%nr4O>fJP_iVG#>?Bunn2`tEAAO@A=C?qsl)txO$lbj{c zs@W`utc>%yV%4^JZ@|@YgH@ndad)s*HXd$_8=UOGX@#}0!3Tup`C5^```1&L<m@gt9*OJk8% zoPzo5R^@+1=Zz9AJN)*eL$~G8OXT9ZgvA8RzSx!OX;Kg_t?;Ie9S^>7M>16sqU!r# z%}<{hl1yq+OMmGru}z{vA8?ViCfs9}?|Z3c!7UO>gw&ZLF=(-331Rm2T?Y=sm%H2# zt$*`ne^T7(7J~u}KNTU~H^Fir^Vwl#35%UUdbrzTTXdUHR^-el{S0{6wAd8x%?b9)X05MH)#pr%G;7X$%D5botYou2B13zS>;Ojj#a7iW1R2pMMiaq4T zj5~)S=nJ&65-ALMfmxc@!*?w-5W%di{Qa}X3;iN$0>s>Xx2y844~=Nh)x3!amU3=2 z3ynip7{Q;lJ*{z0U))EKxXA3DKP)?-k`WzbD-O9F1P`;khNk9vlC7ja%3?`aG)q zwW^34p-xg-&qej;oAV$)huv*d&Nq_6{Sr=ydPa)mbPFznHrCsickqx**ZmfrCe7IG z4iD>jZ%lwv^VNDcth47hxO@+LjzmJP-%Skfx7yHW{uk<~1k;$5l z#}4#QsJ-A?^^d+dS%hleXxDU!ZtQlVA)=QCahpc8C=e4Hk5)VvMl(DIhyZ?fWz2vG&5#kmY0$BdZDi((zu0*8KZN z^f(0VT(Fq~u)c5!bc8tX`^ap=Rxq=`F0&niHAz}@>?lj1S14)Po(&evH$NnPf{VQ} zR_>PZ^T!y?r7EcLLZy5a}?y1j_5haQ=*ZQyyZTYZztPSn>-Cm%h{Y(kg`LVI* z98pK+cwFP@5Q_U3WBqt%EOLX8-4Dx zQGMQ^ebW97?#D8mU^ZMy?<{LP|BbBUPyB(y3&q%bo;_ma{IDEJ>$SJd@S}ws&5Jn+kIC;0~GsOex5~x{>(Wg|Iu`9l(gE3K0#Xn=t zbc|(UPP5}RukwaeHJj`7yhmNXAeFj>EJVACLwx5{CUIJ={{e!dD3ZZ7pSMFr9#_e= zsp#W}*;d;4>e*t}M36vu2R3P)0NGC=slbssvQCH_S z+nE3+1@U4`K#)#-l&d0sH8niDP~gp1FZEU60is?LSPPWCB#vuK4YLzN1NVQ%y08Qq zD7eQa_No0A2eb11F2RC(Gw@=)5c}02CPe3##6G>_w}Y4T)+PPg3y=l(S)^YhINp#D zY?aA9lj}exmq4Rpjzw6%gXY%r8EoAkz_5A(F+~hHWLI>Uw98{w2Xt8lqfn-mM6##jz` zyFEF&Vf%yQK*4J59WI<;8R120^k_%FeI=Ju?IDG(H#}_a*A%X05j8O?no%R~vpcU# z**r_-vWW*4fGJC4=NTmbupEE7tkVY=&)x_8zgD5+3`~TmjD%>LV5bB|4osMe6&-7Q zvc7ua8P~*?(ZDBAZrXXg5JitFo}SoUu{F^PGP;BTdKv}S2zg6NrfXW^h?tBDsG`17 zQRdgD-uY>Ikyco(G`yVg$4 zqOCp0SuSL`U=C(Q8e~2Fz6-rFz6NPmE>>G7lA{fm1+{;O&gcF%ezU;Bq((8a|WkUn_Mn(%@OD;fFb# zOCd!bp@8{tfJLmh08k-ki(ff_0{+{P^!-k(T3HBxF?v zSz#^dSk;0#N^&h+sV_yuH+8yTz)#QI#LVlf%k{aN7V%-6gfUAIqHp_Q(e&ZWyuPB* zQE$Qas;N;u-i$OS0dzg-(j$?t?UZObPtbBC+KpQZrjTF*aVd@UD!$WKpHri~p4O9U zFk+wmJQh1b5_!Z+4?8HxhT%04c%QhzS*uZlmR z$&~}+yIo}dl;3xf_$&oFjF&I^<5(K3`2vT;m1<4KNIG7VFg+elTq$tRzy`P~#~n0k zW7_5J|GFuYuLO$YA@w%Q_1x|GlcX^&L;+E-y}o@zNbaK9fcz%(0otJtVpo} zo}a?s8%pg@GasvHN$;`$SXmipJRWOQOX*HSolASGQP=hY@)UJJr$I_&({5 zY2U?e&mKC}&m7n=pn(2xxLv0Ak`vJ$jQ?ToX5YW3I7EORRrx)Otta8EClNsnJjJ5<#o;CUF($jea&tj>`ON*t?2B zvjMx!xpbILGpyOVfEN?yB`60?5=r>Sw4Z5yyAEdEGf9#;!sjjx0x4|?j)H^W&V2pO z*E~StZQ~(5UODl#-*Bu?NP~Sw@^Jjo0tlMD4;>(j>p#1Y!q{;&=Iz@4JV}hVu*y(8 zEDl#vvauavKI%Wtb*NJ3^(m`pgNYo>ROkaj0X9?Bc=#f|sTLf%h2Ep(UNDUPo(&;y z2Ly0{aj}+EFKrd9B|=}jty0p`K8R%vhsbAgea-oq zwe{rF5jJmr@wKD%qRnsaYyaGn|5dSoE*A7|q?~i*8icG#>l*T)Sn0lQ1<_HU4Rt@c zo3MW20!2&p1sT2k3L=?a3UnMP*7K{kvO|c$$$=^)K{j0ScX!uo#cprA*Y2K_xXm@6 zYTG;VQiKSI$S!taz1_wiJ7Y*FvHc*ssi=VL_Nu*5`4iCE5wt&|7g2yFNstbGjed*V zAYvYTn+wDt2w^}iQ`;L|ELTWLyBw2erQYQz&KqYP-nj=@2CJ-cF`m? zdaV>T5P|JNPOr3g6OW)aUj&)^7RmW{hju;#Jx74au&lgW27aTLU4 z;?J^cL6hs+;nZOasK(>N)mb~QlGmH1GW)T*g!}WKD}ey46mm%fhjD{}e%dxTzIK$j z!f6IlB}ghzW}h%eo`0&zb`d8S59u%t9%@zFpq7+kujAdmEdQS`w-QQyfokRvk!-0v zH2JuUB!h&MyA+Il^R&@NgCV0-sXfd@^0Z(&`$D(?oIvGSe(=LnNa0lQ^ZSKGK4Etr zG_ZZ&l1~8#t}+2@?uVDCJ2tGJ&k(z>^j_TvvLtA)yfYH^kle#>?a{i)B^5EWnb&t- z(;6p_H+M2uq3qGaV;mOr54{&MPwfZF%RS9(eS>?VW#228?}70F-pxXAi*Y2Ck-5|c zWd@PPMbLE!8XnBlI{eU&jAoF!UzlJBHF~#$qs$QU z-rExjLON1?H~r=27OW_E_Q`FfcHS$iU`9loCC|1^a5rmHH4Zz-OoTktXv1L%&lLu# z-~jqfj9%SHT|wPr(KLX7vE(h9)GX()H#lRiOxNU@ggjigavn9b3RgT z6K*eWS#!OSe+kyQqd?L@ftLG~sNT@V2WEi096h4`ksSA6J2!*5=M;7MuT}{>|L-YH z3b@EFO7grysJR?2X_0jv^U6T~mh1hbt8Cp6tpOP2);$~IJdsqcuLoDxC^f5B>y_M$ z8h>@v60l1^j+XX1_Vd{x3s9j!XUT8pDo9j>{q2t4D&QIK_LvW)dStPqqK@>x6# z4C1s)iRF#Y-Mn2hX`gz66PG(V@Ak2Xd##}|Bm&HEvA{anH_~{tEr8Q1@DqW$r_cF9 zV8#TrQzj<3+AQ+`&?`z>CI@FcOOb?hZQXanH)eT-Z~H;!!5J8P>BGrWyV`#Rr9biN zyg(N`Y?zRdHQzGU(&c-YX6~N@Dh13sgAI&WK`oz(j-I2pQS@+I#zH7Z~OTbk~LE-AA0t z(aqc6WDX|=`VxN0Cp1kd`OoyR7&k7Q_xbs0ZoXzND-VQvJzQ+s@r+h8a?9`6jC>Hi zjHgN_z^kVbsG%k8uD-&yDP?oWxnm(D8haNO{4n;~+h81_0F~S$g*pDJhy9xyt(%hk`~Cn zSCatn>-%-J(BWx_mJ*LXBj3!uCYMM+4r)7Em(tnvDDZ z`oZdP!j)$Ft%%hwmQ_umr z0Ooj^8qtNH1;-`qY^!_6>~f3(A@8<0PYM!!e!Um}=c%RYzgn&Toc~Q(3$T2yQc-1a z(xhX9=`0GeCU2&6Se!AuYjHx-q1|P&0=)mR-3WoB1RrKp*6=uVCr{1dnXARr1ssLh zfI{v@Fg8c>jBk(1&wa5j=bg}~93^4@$j{clIt@*YAI1_ zOOU}50o!MArH85=QdPPtd{(xEUq-UAp!d;}$_$t?z8&2k8lTPv+uyRDq_iD{1c z6ZN~fL;lkthd)+*LmiCGlQav`nJ*AOS%tzo%@z&4P{)`PP3&8u6MXS2>9XZmQpg`IY1|9sIDM`6*AmYRaY z6-$>=`2srci%&#r2$pbfK8bah7i7AS8|r|Jx&M6JI^mHCNUgTbvk`7-=bKb~?$XBsFXg2w1m!n`RQ5u-#L+VQFn`+l%uobqIjyM3d#I^shBsH)&uwF7jgWE$cFbAPm-u6*QcDG+XCO zcI76=2rs5k$B;K-?ZY?8l}=jJ&ECwXN#_1W4pGNnamd1;jbBy%{P6Y$zhAeGKYwO| z&2-{pc2LC=8byhBlDTPhWI8cavRT^%7D?60COd8IMh5e zt8r0$&{vc9CE)nbZR|x==fn%cpP??nLq_n(hvA9#YNUvpKEdt_)$$*2zd9cv9`EJKmyQV~Uq&fd0L;d22Ion^ z%k8w4I@}rrunCp8$MU%^#~G{OV|`C^9QgS0Bhz0$N10bZe$I9Ek$pAxIe&Hl zvhTFp1+we#dZz$TlUmy()Fi3E!O-|lA#S1zW~*={2okJ2zB}3eZ?O>#9Qe+7SV@Ag zampSo5637l7Z1#WBt+R2Y1}ZaGr!Fv86rTekfAk9f^EQIzYzLVFhvW9Zb}>WFMM$y zG2Kd<*bRx=Yxudpow@a1UK_c@Y^lC%wC8vcwaha zMmEu#%Ky>W?#{CaFsTUQ7i?sG2U+$3zg;mJ7&?Pm7DEg6Kbq@b8+oP|3ms)Dh+n#ZdQ! z(7Q^`(fB}?Fd+hTMV~FvweLh?LoMeHZL6m=>KO6!H#NS$5Fi7_N*>sgo5?nHW&v0A z9=!n6x7S@gRy@%I$y!3m;*x3dIsYdhV=tHbZ}T|&`W|g&6_g(DWp#%myr1cs> zlLZtq@zBQNYG#p#_M$OQFYMaZAuS;JT|s3Zb&akwwUpTBu7ptv<8AV7fBz8WdE8Dj z=`XB8hSGsm63X=!P02fNPVzVp~>i78U# z3g(6WXiCj=#l@?wWB?3e>MXge1_mak>o5XYnhp`qa&iw4qo+a?iQQAAE7&<7!HefH zWqlO2a-zXf{*oq+G3LZ@FvFxzuT1stW&6iM-U>!D!ov_q-^ZS;xEXjzTAF*6pm=$4 zC6d81U%Bo}xCv_>9@e9Pjl_c?$!+jCJBF(~sO^f;+BH3=3S49YY-8g1aAK=RVzqCi z;pgSrU+U%*1syyFrJYyecgbmF`J55^#JzT;RXlS_Y&7nwSH$CC73_18zNv~-#eq|T zy4Qcy$%wL~BFB<KdgxqW=kIXrzm zFyI-D?3lmw+JfPgYh4xW#uo87!EGjcn!ESf$b^)0pNj+EyX4o7 z>8yiq^zX!0MF(M^Sk+c&((>Op>$6(FGk>%?uI2e<+g|Lri@%=TI%|3N8XqiNnpnboyLpKRbSPB3MFyCpg=P#f5bc>gUNe_ z{6z02Dp{TPd~MSPQ426g&RMc(XAt58UY&Wg?BH3AOI;@+iu~eg%~2*ve#y&dVIDxt z8L06TF+f$mox%-;G`WReXk!2Kd`;Je8=-sQ4uu6v(eZY@3~)}CekC|gk> z9U>$F#%V3WpKrgM<23umU3KqrJvtr+$LPr(ziT;0-}$u@xfz6q%-nGGyqV-svrP?j zafOBYCm7U1Cr@c#OIyq!V_WnYLgrkj=fNFL0Mmg&Y5?iaDT2ehuZ~lJ!V1Ay7XA|>4eDvVA!NBO4>*S8wWl}1m;-a4 zoKGRzL$K^<);oKIw!Tg|s7vJAj2w91ho|l;Om16mz5^nlj$5x3gsDQ6ZiUmDWNqyo zL}6{-D+>y-OX18kH0}`?o6;jtV>fQN1%X=Fw07@%nj$o*`7_e|;6aH=jar;;AS)1P@!*O@)iKlL;#Oe_(93@J)71 z*-KUdk_uDvbec!tZp^EY1lr4FcTVQG27!{8#(Bo6}tjF*v~Lub`F^b7C)!P*IV@m(eMB4iLOR}92q%rY*UxYo0n)&vw`7=1mU#s9=241|qV2JoQXOm8_82Lgz!a8u znp7U)P8xN6FhOIe{N4@r$_qdV5<)K{dR8I79ZFFN{Vz}0Ka+VjZy+yB!E7#_>`04^ zdzOE5I=+c6f)t@{5e}k-rUska{d3w};N&UTmU|t)S$<@iCfrgu0ke{r*~^@f*{tS? ziFrOgvy?WnKy$px4@`T~xdD4g0YEOmgI^9cr!e0sDzYS1nSoE1PpbE3=8W*0@+N|L zV4vd|=|F;AY#xA=C7c0^tMYSqrpGotUu3>rfB$HoeE;zc zhlF%8|CN>HFo58YzZNUU34TWk@XL)Z{z1^;y6*#+o!|W{i2o(_H=kcawaoJu!24_1 zHYX0SWC`3PaX2x{tlqctO8a_k1(b8Xj z5!l{OgKmblcwtYzEQGB!_e8|pGkCAt<_^w8zHavdUh6tk(h@jli58DU9q7W_f2}rZ z6=-JUe)yoVKNoOx6I23GyNkm`F1#j35sE;pws!xZ`k;Go>pM@966v>4oP#_zUND`R zq%@S4&1m!T<)oC)SAoDr@b1&hC~S)OW-96T%vEVQk(W^O3YkZs0>D&Re(kIl&r_PrA74)I?7a4KqWG^4-5fKsFFM;uueX_A0*q zo;Q!a#@mdL{(>Od%+;jBQ2zwwT=Q{kJiK!Fv#RlgvQ#H5PS8|#h!6NtIRc6X-r%+Q zc@TecC(Qkx40gSzu0W)#L+`}uu#RX!`G=3V7lUlX2be&ZMYY{J3HIlv67s_r#WZLRD;40?xv38KNbU zanyt+d+h1NY3ngQ)}rV*(A28?@;#odeFj(A&6(6j1~W79g664XiTv+r6n_wh(*tT3 zi8cb6{PXvl^G)CAYq-0o!o<)sA&%6Sb9MA69GD&@=~IF4*FDlaYBjClVHX0Qf(cl6 z;~z$YEE@CZ|7briV9r{IMraz{(;Z}d zg}aYOzvgHBxcB+<*ThDNA6LFOw=uptZi0#X?F-j$4XC@#0Vx51H8`(u&MtqWFdCoq z?xZf`B|rq6TYD@~;esh7W6sP6(9B5uwf=$ds~T)0*4nJVo5on~;LUW?FZk!h1Q><5 zgk(kC>!AvDS4`oL{XqV)$qnO!s?~?lz~e;x(>`$Uw@>6RtO;l$xXdpHjF2dk{8r@& zsbo9ATFb5qvLz81G-+F@=s=U5;)(AZkk?zu&0Y_Gm$Z#OgAkGx&j7e+!kfi?sh%_K zROq2cpQ9vJAngRl3I>!t9N`#QbS(L6`K4gue$4T13`O8te<<@nnqxi3C+35A2JG;# zKYD)_>=;k8`-)#WWFB9a7O6Sv7rnpu#>R=VeXUQJR%qp>2m73|jrR;KRmIn=tN3Ay zue?bgzuDn&JIdWxe*Kt}dVsa^W5}ElB<9<8iCoI?;2PKma^f$pFp9Y(lQKxEXi05T zn%267JJkKSvj4HFK@oUYm;ujL_BKO~5S1IIWW=n-6;6y&LjWlhV5q`$k3trUJ8Nxq zP;f+}82}Gv_uZEb*~7<~p5s#EVu{XB6#X-ipSG?|>EvLH^uX;()xpc;B1!-l3VsEK z;agX7yJZ&AwVtK?r0pME(>bXzbGutNKbp<@#fc2`T_la| zM8J+`0H<|*fbXi5&uUBn<@$Lr{0k>|XLG7%VnCwQyyL^}Ig-+tp0!&RUVc5si(gw9 zT`*j;9Q3!iwn)A*XJ*}}qtg7Qb$sAoyIIn!*1*4S~v8c-Y97rwNI}r8nn9UyDkDhl&GhjV;TX0@o}2 zBq9YXLG=Gvi74`9LRfIo+&LvMtR}JBN;&M!*W3L+dq@|4P;pldKLND#b3uK&*}yf? zd2n}mD_(PR8(WMdt9AYAn?q8+-Tu%GPo0yxCvwhTKH%VRY1;HhE-6%*R6NTWzx}qq z?=Ba2!g>z10WGuq=B1VgZ)7CR`EZ`V2)U2^Zj{lT^FmucAY1i@fN&*H-!ckOfM}VDn73Iq3rQ%-j!Du1;v&nbuL7T zqj#q^+y zJu7RGU3kODBtJaH__;DemmsW@WAf$H=CegeI~Nqp+Osc3p~KlE^ThIR1WaD&=pDcV zXWtnqQSIOX?`>2HsKUXXo-6fUKSj2VBph>}T~+~?9PJTxw|~Cl>A$eFRbYNl5uEk1 zxgFemRlycaFLM(!?z8j)=oIwC1CHJxh>D?-($dfj{SgjaOLzHXbj4N8O=QYNc=8b7xrZAsaaoM$(MxfJujm^WRi*| zVevI+y;%|qpjrlUTV{ELPmmOC+HcR-|Hi!oLl99HG02WH-*`Qoa4`;aY(fB!=|3Y? zoNR3b?wGrAaXH0`)+Izu8s0WMiom139Nl@HiszaFL>ra2FG$_FDQPP;^4-Ni zP?Ij9$;HHF+-W&JG&x|e%bbEBCt&e)u5 zMr+0XCAc%Jb#=S_=Y7@)60xhf#j~t%m4gr$4v)1vl8LorXTa4u+O6eza|S8Av4i|53}wzUCue(kC;DwqPiPmILRUGKXHFu)CbG9RqmnfUMlqCF|O zP#xaxgqy5sl|hrjidl9Dm1}?Wf}WL?LE1MNOz2WL zQG{fOyGMNW+cDjLOXiOjnecU;`1BTz@Vuo*5jlKyWtq1*EUqI%{5wLs(>C*j)12z- zR0|d6(|)J+^p3hy%#!$ky|?$+N(3eroG4BMco_X*1|_=HZz`lG1{k&Kv_^B_uqQrw zEnwctA4U5=UI@s-AL3zEc2z)4*N)&BBLVkLuZ6vrQ5eT|qyLJ(3&p&Fh(N|D?BbOt5z+>B0=2?6Kc0T)QU*;H-4=dJk0I zj8m0%S=TRCuH|pY67u@TwE0i?|GKw728gF{g>OHc&I@NrBsGYa9Ya6Hs^rpP&3k=- zV3R&vS>RlFUrCl$b8GZDGWnPU%gtj_<+lMUSI46S8~^2%l?X&dW#vT>bZ_ne@_0P< z7-#^g=!ZNT4o_OrEJ7g}5_!2kBWS_%Yy09m8kKN%wHjSUJu$fr0J-6@e&p@hT$2

+>1z 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

1$h_+VT}mk*cYS++SVQdEV+?8Z#P#+s!+>D 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�Nc=-c5J@+Sk>?MKOMg;M?LJ%#%XYj(ktMg0P$ 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ºvSWCMU3$u~4?cdTYBJ=EY6r zIyT_Z_k4;TlQ_5lofMjMW6=EBQoQ9mZru!c`GhB>`ynvNrT4H!NYoCYFAx|bu)rE! z&%U!3% zLnbuFM&3qhDk^iAem&&}igLqk>es8U4K~>A?2gTt>hPi!toCf*vI1h5`hv9J$`6y{ zm;^~poF~|y9{Ty-cM$J+oTAkKUK4VKbsXk#GZ+qpdNk!j8G385>x{DO-rWrV)-hmZcgNyt8}!a`^RNWljbRa>0;iyZIrxZu9L=p;v{MjHmOyePZvl-EgjVOE+MH@1+~ zJ65RtB*@~$Q@rMG+Y+}hC!|zRlT}=i0+n@k#V_cSH%W-(-<0KqRItZEsb&r`0+!1cA7NNR3;a0tB zQoVY14<_YpX09^4H&BybeG%q3$7yAjV*9;*YlL<7iza$#uVo_Mw3^-h^Ogssra}0a zjczb+os);}_odmr&w2B1c?qa|7ArOtsKCY$ZGd?0#uUvDcCGq8S)ZxA#YS_Z*V^oH zJeWj%@&B;()_qa-|MU0K-Q5jRlG3o0Qc5G8N=Sp!u{0g5*NG;NW zba(77*Rh_T^IO;N4)_NTJec>qo-<Uo}PV>`%7WVR!R+jO-RQ=Zhq%m2CS!`ToQS(g0i&|j$7xR1z#82$@^l#R9&mr~L{=fAvcXC!@QqVMPJ^KrNbw^ekrM zybD#d+M#5P2_;7?Z*&Le;oY@fhm#=gAYQFp!F!1 z$Cuq*=F52X!lULT3hQ!57At0-gGpv$_;4VE?Il#>w^uIGKiYID zuw#tGZ}jC8yC=n0z@-`~!yMXUkPUtP63XY<#TIE4s>$<__z2xAoa1=+a%S!-1`eQjd3%`r<@A%P411FMuX#i( zc6YS_p$$|abunKk)Xbz67xlfLCQf~T^Wdi; z*e~BzrQh%3nDVPIvxq2G9c3u>y2d08yZ!IJUzP^IIuKGf8BTYq|DI41jQOxiS*uug zk0921;PpCs!jG1_`B@BBNI@k>ZGxs=eF@Xf;d(u)JMDpi$`2no3RYnJ7D zpF(M~hqj9J^-kFRiygszCnk@r!+Fenfv&cp`?S$G>-pfUF5ELNJT3`k(ZdH4+S67+ z_qJir76Q)y&~>LvTbo`cts!1>=_+CDhn!wm9564;m+P?wBI`D^&7YMa>xS|6A*UQN zi1!b0bZqXwG9x1CAx>47`$Rwouo9zwyn)*Ts&SwxW!Eq)U&NFCxz%Z*m>9FgFcs6R zEXsIC?CFi0Br1^$>_j~BkMJL7)%r-&k1;=ovzu@ceER^t0_8uMf)S~8Wr6i?(140)5f$O7qFKq#5K=BMwUwdH? z|L$bfn#koxaL%^3$cx&L0t!PEP-IJbr#ZSM@8Pq9sv!wwY?if$e)FUW#iZxF&0~aA zLRdYl@SXf$09GV9j^EOq!*ril4RNGhWg-Vryq_g#pg@P#5L1S{CVe$n6DGkxM^A!3t~%;8ZZ( zw%!LjBs4yU@gP`%gfjJOi+-I-gwMiNX7BeORqV&Imb(p9h_fnb36?)VyCpBFwibdV zpnu4F03KqFN=(l%C-rPToT02S$yT%tTJ=kCYJZCI!Krnvj^$33M_?tN`2V(pHS>;~ zYUa>3KN*fb9gjmT?&*(jr|GD&QkVpa67rg2%%`@AEjHqz@iTbOh;ZlnK-To%z* zeh1gMhuYyza=TM!NjvmuW)Fys+RJSMT9qcc83k(ybG<~FajP16Pxt0#aI#CFM_o_G znK5qmGI5FNLGinFA^^S9m`)P15em(VOz%sIO%uNoO?L&8tJ~#Ngu< zn^C!LtckQaTPGv9;7^gQbNapIR}!#|P|q2KlXWnPGt&fhaJA4eZL0h zqq=NXZ9FLjCHOs=&%5!AjG7t0587v0gr9?MOzVGh&lIDZqQ>xPRNxpu=ff5DGdsu| z0r&T#7mEB*i6d6K`bf(t3`xLrkVgj~I|P|9jl+jBZ5l@p5QH?_No2aQ1aer|e=DP2 zQ8sW-0;(4Aof_z}BpH{c!KZcjHr=Oi*%0$Y2OlkIy_eag#l0kd%ex}@aIG13I}PhK zS^!s@;f}DX_z(4!(c@Z)-d>$>b`wzg72>fg>YkCP)oC1i21Lf27XAY64fp@HUb`H6 zl8e!ExaAUfe7MjU3z$7AU#RGMK!A)D$O}GwNj`FaD-McaU{8n_`a_Y6)Tr;72vE8s z4niF{7seq|SY`#XJaYNK1zu29J(7i&CQmsB)vO$(`&@AfR`!Q=EtOF;+L z^oM77o(M`OSj7&< z+koaZ4XWQ5OR*=ftB6U1`*7B4^=CekCQUyNx(LapP|e4noY1etRO_e~y?uc9roq%2 z%YVs9!|d7YpAMgiu-6Ls8hO4BWOzZ`@nSx?5A+UfI_#j<=P_6T3q-*e`+IvKR=2gu zgsGePUY%>MW}Y9*s-P&-TPCtk#I`j9T_M1CvUF65#IxmM^c|Nxj2))S0T^%H-rYmc zFQkbA9+zl8p;+F%pb|8sV>Xz=r$6#qp!>5M6h}JYGihWA?mKnMe7DCgUtuDyKX}pz zN$#rFVv#noeo-k4`EAUTHAPiXQBeeFQ{igxr|L2Cz9$_m#ArHN4my`h@zaNOCX3(= zaoT`3`J)*Vob26lF|7B0?lk?a*{0AqH_TAK$=OE7Ad7O~esOV0^g~NDbfUB8-lFR! zap=y>H{)tzSqXkYyG_QgSl2lvtjT)q%AH=otKj%g9^jiw(MUN60zDM!!18*$+?d!F zIqt;|dGbtKR|c6EHAwruvdlkUm~L8zjCgy00l<@O=oyXTnZ=R!+13=%sDcl%yJR5# zjpFxfc4u78>8AbGvvcp^NUr-`wL|-Jy_R0L9b9#&JgR}Ne#{f_hex@%Zk~-8q33Yi4)Q{_TlA*k%)O?ZA69bg3hj-BIB1Xn7baCJ=*zN6;1lTi6;i0BhlKeXF+dJpd` ztmHVgCSJVi#XM~p;~=PF$9O*~P;?_Gv8Qquv~%Ldl;=wt`jI&ETXAodsLw`(SZX=& z?_|O17}(*h9Bdzyo+Iz!b^GA4VLXS4@o7DrZQYtAg2gd;LSq}O3l}?9hN<)-lX1x$ z6>ZPsalI5Vl@M#I-#B{nd_-Y+&%3Fb7DO;p@S!t;m+3jTM}S+_n}WCJamvC}C67JI zVvVQ#Dgs`Q88f#nejrVR=e_^{?-WErFM?V|+44 z7A4FjI^$3cy9j(05r3jM1xeSwGm;nNrXR_+_8~Vo) zzLgp_c)@p13^c&yY}6pkE9o>8f_Z@poNWhi$_xZ zK2@%0l0hje(B(wqq`%$mYwX*|Xl5pZ#Hv!#^Yx};8s3IzW-hwOvs#*mhZ{G;uilJi zb7*O#PZFoXZ(~QWx2!JS7UgrwCV}hxFt$m;ezan$Q>g*7)aN1wT$8wjv?i%mZB-^1 zX$<1df$%9{@&UG94|T&9 znqU(d?>dtHIQG+M=iv2?$+O<~nC~f4)?R%Q=zj0k8S4se0=)@x`lG&uQOVK66_`-E9G|E#jiq9UBVe2`s|^6O4r~ zz!XFUu#UhOB z%Yf+fYeV7Ml8ZG5NMz7Pe7sku&YF?*VvyVAK-xtGwAnw_wv`iU6%AJm!IeB16Z>sGT_H; zhFrIb;QPlkDTJHyxb{0WT$kB>Pi#j4B(BBB{_ANJ%{>j0}FUBIe{=rBzrN3SvHokaLs647+4 zk85;UpUGDI{-@!|B8qz~QQxEPHz>$Rf#s(dtTU-TiU z2y%-$TqbGioqs;ca~(FmPG@_wa|w9|P1qk`_QP~- zZZ)iI9g-Kch2woDEPcqOJW^xho7T6SBIs318ZF?F00Z&U_b}pM{(2np>FoXn^5vC2 zFqyW`ZopH?8J<`iN&PhT<*z#Z`rq$IL@L$TJ}PslTAfGvEZ01K2)So8Q?nohJp9_IQjVDeIr_^miRtnBX^nf80b zA~vjooXDS?+%~wcwvx!?B@~87LgJ{2n2`Q!Y9FsMBPTdRJ~5EOey`U_&y#}9?NsX7 z)C)Sd_UBgKorIV1oO={|!Hdq8+sDfj!)?D_sp%=<#wW>rxRUQ9K0jf*#oq5Yr`=p= zYq|_UBJ-P8zU)-TX`l|dLoXKIjS|wlAJCrE-qKzHX^La1A7wQVfV6x}a+26;cWk`J z_T^5@T<*}{T=t6kp85b{m8eTi^#nXU=TCKcxcd6*CASV_SpEVquB$G7Dt*>DIF`eD z|MWWZkN1T_FBgI~2Y$tD*;K~SVOI1fLv8_RXy#6|x_BpiEGW$r7#w0r$h~{g0dMIv z=a^jtpUC-j!YVYiFwxkbp@(Rad4-0Xu}`YGXhGFSlk4L@o7{)yqJLsrWHi z+404{k`lzj>ZJeNXgQJCrTte_ufT7_3Mk~kb^L#P(RK2MUT5#)Bil&(ACcp~fY-PB zsJQU5H}J~X6_!NBzd;vX>>JhWxDV}(aQVGQ8Y-K`f?2KHizdk$b6Fz z9D7r9lkaB*k=lQ51|S8sXNM!p@3>w!b>1$aj%eK*Z%7}ZM0Hs90Jpgvb$LRLw7hjx zQ_jceX$W__d<;U9&C5Dd1^w7zqjL9(p8DY?*vCG|1?L&NcvD1ipjS?*r!JMwP3|d~ z_Soa*hM~$Gpd=>M;2#f1c&jqS&(qEzWp*jWc$Q7CJA*as@s|-C|J(ko5s;jlamB^6 zj{M*h@s;^c5P&t$vIAIi1)Zxxdo9CvPuqVIc^jHJe^kiV8}H7u{wq0c^wj&4E|)oH zvQ%fR>59E9(P*@X6 zdQ=#;g#%wb#9GynKO#}Q3SI;V1mKtXK2*$Atl|2D+86^nmQxl*LCcBCQN(cUj1_4n zYP~Y*Ax*o+P>x^~uPw$KF)VB~>NsRY4`d zP2D#(kRD=RDa*OKr2(3D-F4QqgjsGAD0O!3yAV%7@3-(4twVz(z=j)s+y56KNQo1o z>opIbLmQg21bQogt46ygH6W*T+qJ7Q44-a;UI8P@p)7kJkfmzh3&R(7#Ccxm-YhR0+`O?TfhFjT?)niyCZQV|&uQI$Z9R_0+ffO%tr%g9SG}wuPtj zU!P6b9r-<0Jy@_C3-(57ITu}I&maCrW;y0%TJ$rpQ_PbtJcq!&HWnT!XI<^SbOw$E z`>k~7qu=xeYvXFh;pol5&vPDp_>M?3^J5#`yR}!a3lt91elw2^VM+txsM%{4Mt-cq z5aDUnXi+Tgv`!}pRTpf7B@+RB@=NLZ-|A=OQWxD=HY8FW{MbNunsK))B6!%X@@fz7 zAcMlJ@IXSh-I(yuyR-bd#*m;qTf&P(K>r&OsC2#P{c=|GhC?~s6S11}Kio|N8gBpm zQcdAuP`8EXEWps%78{}nsj%?0k}fSU-|)n(CmUmw{%Vswuc<3lvrP3eq+P5q7Y-Y>E)VuDLQT;Wd!S+URb$qE-usFSDyBp6+A^n zn;cfrCJE(Dp7rDp-$$TH)H8jMPSN?Gv*d@aBaq!GD3T__CRA(Z2DNH?&~<3t*yuWt z>_6t@u_r7Qqo;(d@e4E#S7X6o!QiSz@KyG9jE(OHJ;1-$T}(|tlKm#SlEaT>u()h4 zhEMnxVm!2=eW&p%Z$n?&J5}QMD2s5uK3+)Y^`1V=<;TR~m}#Um;eUCd`4>iiy-*b? zmKm{IS{QzvKYK|*h?q}Jn+=9%9g@HgCjrx-TE^ifvx34E3nx)P(>iXqx$qF9iaNW8 zkWiH*PnzH-7QI^F6$o+Z&xurlAiJJw+L z`x`-Q%C3vdS?F_E?`*;8Fqa+}1FyjQ>5UClfC+F4@9h-t05ul?7Vxlgq_I5dDTtvr7n_PYGBJt~U^6D*o?c59teY1siU?Zx@yP&C%(v9tpo&n$fM5vP73Kfe6|MclKD7Ut ziu%fXs4o}vg`DH`6-dOj3|Z=^dhsSv2)F6>I@l*-A#ldS`}`|R^Yr8cvW0}f9c7jY9$T8`8yK z?9j`hf%hpQa&U0)t(x71M*wA5fMxD$xQBe;PT_5BgLOL$^JEn7^?Yd4l;YHg5^8O! zcCj&6+1QVIve5GAcVmO?@M!XLKeSEyVf4_&siKjEal2uIfz6kOzLEHyoZ5iw!9Qg1 zi`3lyy1B`ZXw|$!NJM(3#gETI)MvUZ^zo^_v;XGEq3C8r(Y;kJN8L{n?gUpO4*Rym zVU3eS@$Ea#N$g$eq*WW+gydX@J7D)$qB%?)jvN2lr9bpAGzfpk(%kv;BBD)pTZs3S4a^u6_sG>$3hAAs~t=YzF%*Y9Qfah+SZs zcC6U-BJ8F|=Sg7x4D19hXL&6shvwN2pT?^-Nn~*mUD!xZij`5!j z?kHtiz3@4*Gm^|)DLjC>xzpZfGh8Q3b<8j$^z7KR(0XS%E1&aP;_2K7<8OQky15-m zEW~UZThwg0S;z^`*@gfxQ3}T zyg4j{bJO_0yDoa!jIq;jd{5TO7rlDN#A>Ok=q=`#${AhOsk{1|?)tlIr<;B(J2n_? zXukcy5{wvJzBYXhbmeA-ZaMBC02&xb1RfgL|NKiV+U|l(odYY*>(LsR<_^$-(ZaK3 zOkfhv-lj{PO+??)&i6*P^e+Lx!djHN2Q_2@{i)Vp0hW~H)CJZ+#f0p;p{6HZsja}g zpTY~9kj1jv@4kYsAUphYc{tJwSMnQF{ox_wHTJQ#LW0*}g~?2KZthZFub5_27rhtG zR^|=LUv>`oGJPhY;tehRlR7FtB%hD*b9x|a;NhdlJ%UmE>yD7KC_YaJXq}SY%#$k8 zbTN=6FRbsrOV_I>)pn`htl=h6L?^zhkegCf)eQ4rXcEO#f{AJo{x0- zSO)jC$stx4dqa@#zRe*8E0GQJLVi)vCM3`H?MS1_tvc1E0or1Sb4IbxKxSOxJ+VUO zpwOzJeBrxHO$_sA zSC1^yR@a^gMn~KDM9K?s-eT&yH(KCZDn=~8l7}l_SEbnNX!y#R|VE2%l z2wP69>5Q?L_r$ObKTF>)-M!h!0`6_c=XgZ{2aCIkcgQ-=*O_Xkl-j6sOo$NP$5 z<0WJcC|)Big~^s$%V|G%&uZ#Q3cP+ZF&gP-YDq$me0YLTfh0PsAZ0g^djtN=u>zioXd+R`B`@cU%2nwCWm@qYRWN7bnL2 z`7kw!^;J8_WRn8>*{d)2RsJN{=}XAvosq{5f{Ntyhn->)^xp^{AMr-uBWyoGrMA4s z_Xs;hZD`^lee#X*6MYhcBWP#)T)}KYyprfQf zfu33{?vQ-OI+27iqg7MD@o>e_CR{-eB+G?Ml09)JCRccu6?5L9J4Jqtp?-j{$B?($ zOjS3c0mY<=&_$eGO?4;Gqy5~vfBf8T{ARm+i%80oAg+@?)oz|qec_#=S_)ZhM;-V}h*?~CJ|8ve=kn}aWW%Y!u6Xeg!8e7ua()8L$3IF}(wqw(J`x_0Is^(s9NJ{=y=(JhX}H%G zIBZ@!>7a8O2n8~^47weM?qgLlyFuW4-6eFN>wXFVKKoqP_aAr3u$<^qiRuqN=*G@0 zg<|c~hltFd-P~|g!YgkD@PQKJMkN+m)CxvT9HSQlI$iiNYUL$}~IhC*wZsH&K zpJ5jad^Q3&fig=*NK;%ERZ%JMmuKg0A;kCthMFY@drP z0^E5T<)c$}wUJM@-@5%E>sdd;{iT3IDS&f=K=R(up-+<2s(L?j;QhYW ziH6RcH0;OT1m#rZU3ke5yn7Kr4_qdn(`j)Of4WGQ!SMd058L=Z46p;_-d_e-hU#S^ zZDpj{NsXV@Pd^&=zoxaUe(e_umw%U{+O^oL*GiQ5cvjYo=075O*2#!yXz6JI!Tzq= z1Qa0*nj$o_#NY$)-|_aH7|5}>_?o5ln&a>50DXN~nP}w(0U<-2a15fa7+APn69Ec$ z_78QbLL{lydOnH7%MUCPE$J3~9>Fsw0~{0(bM4$BdOk*tZKZ{+YN_yauc`KDT=eoB z|68J}WDn^D$_GSv+5dnsYpcn2vU6k*Y-yWN8blxpC+WOcLC73~5{cRt{7gKP7eesI zByaI#YooB+B9|=sS(=c&Qx1(%rucG={DHWc;}uPQ*itD>fq<_-7FX%^IMJ8~x1*$_ z7$zrwzpDB4i>U7bmi?8jP;|J4@w2)O3cS8ZW&RTfAcz-TdM=M$BK3q95^#$Or}rCW z5cT<~{??;)$Gj3bf37J_hP~~VqVxebB8F14el}-roIHD_dzB3BktfiI_mc`0S24lM zStP(f)==sm`8-(bN9nuZ$t;6JoQPV#9hAsjxj*Bkw^2|22S5C|Xew(fZMa#;{AGzs zC&n{ee2YkxbmcQ*vVmPD6b!BUA^)iUcsFvhm#_-zlAn_Q3jbv}1&eHY|G2DU(!hkI zPiRp19U2stOGpg&x{iZKET<}-$0Gd~du6@Bw+_tn7lQ{Y#pSHgE?Jk8AD5oR*KXvy zvC@k@rFVyJJr=$BTrZ1^10%L%;U|O91{&wVZl6i$3WFOr+wSkh`YXNw2LjazIiYzI zZ@J?kkn`2TE*fYnXgxQCb^OBJLn-ht^2*jUoPPOV+2rK^ULhix zb#q+R?HjpYzs9GxssiI2Q-(=Wp9@T7Ew{1ps*1;_2a6W*<#q4P4AIcD3JL+w;uFhl zLL!1M8y1@a7?m0Ljh+*}D|l-Ffh=)l``3|AV_81lNA-0r^=;S^r1-V)nab(6wCxBe zzfa>!u4PT&k=cC2b!v>UQ69mCb7V$NYZbse`=gkBua*8-G8UNH;$k-rFD8^^yD%uu zqNlN z0o(pj3)SnBu58?%C(t(2P#0f{GmY#q;3p>?c(V|&yg;@X59_>SR$`OBJDOUc;7EA7 zzd+=~EcyhI@_a;$PpEN>v-TTjV&93517C4G^obsrU_bb<^IT-(s85d$L6Fq3B6TF4 zlm`uA2@-8T?%!!ye=ERR>YBYLwLqCNe?YzoMxOpYfL~*50q67pUGVe>Gcilip6_P( zA>zkK#xq6i>M$%s#*gZ8QqIj#y~U1+509ehY>`9=+3TepNaStE4clSgWsTi-fYDrf zVj7pzb>~EH=*ch^(P$G$sP2#xJ#yJ2t|RnpvS%OpS}CULd={rfTVjEMjQ}Ev395ot zNiP!jGlxZyjJ?=By5VDsjn-56b=`Ruhe<9fnHu$3^jS7RR!c_2b_god}W z=V7%G4SP^kg2%|ErepJ0am$?B5*l8Ygid~jT0$y!>)~iy9ht@$(aM_~#i5_d>(F`66u{mc;~s^yH;A_i*P2ssDeoG?SEM5gukqGrT);rM*(A0J+Vx~S&PZp zN+E;51+KL%hANGzdw9u%+UN1Zs}kb>kR~i5#0sY@tRYe{4VK=4q|eB6^VGlKN@N`o z5*fYZ8D%4_b$$vq#8~DF?=~95Riq-KY2~joISk4RMVQ*beeJk0DsUq|&iMK^Mf3tn z>$k8W|J!Z9i0zMhg^S(;>Co8wkWOzL*#Q)vb->&s?_c*f-b`3kD_$6ydBmpgLN!NI z#zWs^+T0MeA5G`_@P+~m%Brl0!Pziy%)+Nd&Z>?92&nTxDQfJ{x+xY4J!#xL(0I;R z0KG6MKk;~_wApyGA4fIH7~S|#B8TLSeb;LxL{{_n@;TIg=%JF#`qy#ctsncT5Ol8Y z1CzTDZYRLBfkmITJ*pZ}qYN1zlj;btedP-3d>u0lMzQI`B7TZ)!9nzULYi5&kwXBQ1q~%v>8I zhvYm>FqG-9d@u$vE53dUXoD&Q0bixl&*a?#nh04tQOvt$m`4%x`gyLIHDs(j=mt=Mu}eZX_enfiI{URUXWy= zVaqT6iYHA!~l48 zInSW{)dXw-3S~=?^LuxBFbTE;VN^z{pB5SlYU3ZBsW0U6!f6|ob*2^^Bvje~tKQp< zA+m*5IjnQ$Kqiu{s{&4?Uq}-0F7`W>&#h?~sxL;_aVGf+>&*6llPV4~QuMM<-aP=W zv5c2tN2%@r5CH(+K4qH!d1btejDGJ)$tN)XAF#O#0Gp|j!n8fu;U&rpgnn-$d-9Pg zVbySzH>r9@!{ihyPl=F@N`>j@2L>ngX+ikB3;f0A15R$M^G8cJz{b(jrag97vGhX2 zf)TsS0)NuaZPyl#c#-KBVBg`F-b=k?ydh_rK_-eW+62Mu)zzSmEFUm@`sm z5*zqSC0WVdk~B-fDN;-RFYcU55iv&Z_MIzYZEVxx&VLWB0RH|pkCCA%28bT?5%iM> zX81`-UZjvdD;fE3WYEngvf&#L85CvLuOcLRao&)hEk_UDuBPgu8qsZD&(pE%bIhn$ zgVW49et*Z0wt)IljIC=mE<_sOl-_eRGbY%KPK7?xj&waH(zafWFRx#u;O+Dnjm8wa z)wo?WNe0Z_HutX7ITw73KeT79#l%wbJ7SWPRortEwu&eu8f?1#SYDPnv1<*eK^o8qVeILQQ>Vl%pG;|qy@xqC3r@0x`Kxj}P zwRG06Gu|$uujvx3{H&g93i3xR;_1RIFv()_)E~*>@jnFpzk$FHYi0V5_no(BOzkUq z63=N|xIl)m!;1S+i8X;%9=jpO;wn!007WMjJ3R7FB0XLi2x|KDd^r`_H4g-s)DX3_Q_~W zg|Jc6EnxT`EU%r6KE-+SqHiZII=-u=RQCk;*u`mBpm&DIVbBlSiCq3(e>cJ;!*ZNS z!Rq!9KEN8+t1Fj6liGRp^OJo`S%&}d_aDUOfD*`qA2tnaoJBFPjxc0<#K#4*IDU;n zswWuo$+-AwdPg1aUsHVR;`|48Idc39yNnK-5YZzCq_s6etca`?VV`KR8>`)X2Iut@ zmvV=YQ70V$bNenkPpp&{h<#TjGNd$;>hr8se}Rgd%E5n6e>aKHq>6bSL&1F7Cp>(J zEww}8KSx0bl!xSzdQTJRS-#Z-Ye$bK+f`OxNF*rIrV5YQTi&Z~+vAnl zD|z0~P^K;X9HxXfAAmftTW4_gJ)B-asY?wTa> z8i#_C|WA>;8qbEl|Q1ot-8kZWw&!-$1%4r8O0udSmbe%xIK0uz8d|CeHRo{rIUvs z-hD8h;kF%OKxVd1!TdMKXHxSo>^$kpDTG~I0ndGuMy`~;O3q_+81q>;M*5cWL%*!pb6EILHi`#jt zH2+HA zu)xDFX@0h|_gFy@9A04R^9JzTUB|NznH1AFYG^b*QZav(VXhag^JCNsn6gg5{0N{u zZ-smpjI7;lZ_52FX&=HjLh7D}S=s-TtuF=efY8Ffu06n`Cx=q6Ch^YsjO0L&^ zl4Bb7zu`Ss?75hTXJWe4mR81{)nt@kmJ6!%rl4*T}zLpM@73wRf1j$^qtJN>5BB!_+ukdaQaah&#xUzocoen zb6)o8$#}iry!aRzblrHu8~!-N;SuLz{H!DGD6)lidXI|e#c~vEl#;7_^qZ>}Q6dA* zsNA6v_EFQ%T@R4RfLSU43ILBNo_gJ$xYcy|vRf8c?4v zMz06wG)z+Ccv7rza?%Qwb1@8I_yiFqKduOyBZ`x~Pn>ie>9GN1<>xm@7C>7|EU9Zw z0`(WTAz8TXg|CuDono*bDJ^1?b41Zg-oCQC7hqKVwE{D^KH|${Nn&qJ3YZ|gwd?f) zXHRdzl6%2b(7xe2CvBOqOb# zJK9-?7}T`A6xt0FoN*vKK~y^l`NB?uSr{fy+APuxtRmCCj(Hi|J+%Rd=*bxOdrQ%o zMUP}|xBEp-Mt__qv@X6Rz^Im7E?5$@#pwBlbtNXy(~PfLSo)VnF^*ir>8NdH28L{q#b3Ymo3F%-K%~)`>t>!k2s%#v?ybcvH%ZHYLbE4Ngx?v` zHjERe8pmq?Hjp@1JKLEdJ6{UY13-!VeEGN5M+;~jGf*JKXcM|8QiX`QEI+x#Yxv}Q z`hXf+b|>boeQCFvbr|CoP~CrE4PPnhSgx}?hy>!&?25fDTfZ#5VkGX$zCO9Lll=+S zLO<;-3gbZfYNTB8sFPliVt(hLuf*`w$1IrO3>;&0>wTtCh!4K*CNr-kSt&+aO1f>E z%+1Ki5O$yC9iFG$Kc%xNj>jGa^ysbI>hDr1I4Y|(k9&P@}7QK_dL`5`|; z$9GxUvGY2}9-khVaDl#&gYwY`04ZD+8lK9iZ`JvCeiqOO&^kw~!N>u4=h-l$mcrSd zTV?#>95MNDq@j*7zpNUKsQS%}*6vG@vVU0srzdy~fIF^LHrq>!^Ig9pmza8gmu&mF zKX6}+CiEVprm z98Cjv4O{DAjE8xW2H>^s!@&D0sihZS?ks;ZwyrfFeV+9qi8Lg^Y5 z=h@F)5$nnx2Rc*Bpih7v->%VUatL+vb~$7adhQO+p&6C&r~ce> z?0O7#0+!)#P70TsC3I7^GLV^Sc4{*Kz+8Y3Ja2?Y2PAb!hX+hxVVNMPuki?3){Z4q zD+oyY(?Y=vf&7MD+@Qv4LmELxLaq_*s<+M&@Yf<6c<&|U`-}EJ@%96GKI`L3YM#q$ z`br$G=ivj?{}SAU>F-OmU2!#sVdyyJ+4qllw%oSGOMT5vFKF1|c(bFIZcGJHXm-1m zlz+IlV6wUF17VqxX#D_HG45zCwm2p^lZ^*!F|yfdk#68^zwXg5Z6!+Kch{?GbbnhM#lj zQ!>FmLC$I0kcGfSSk%dg7e2<-02l!@V6WVR#)Ys37o{EYo(3RsOuGbrRM~}H9NFHI z#qHJNg#KES-2s#Tj5EGzIUCKv^L7OdKEvd1`dxCxY+CrVbb%H#3t#-_=d2Z~?{aic z_OZ-@siCV1%ce(2NNuD&36%(b~FDB;b~jF@05fxKvYUtL}!l{!s%Z%v|!m zr-1Bv9-OkB_gMAi?5hU$otDlnz$)O^$Oj3ExGjypid^f|U60ZM^u z>uq%!$r=cLNM zkR>3RZS*+0Xi+#g`!@92wt4s3ZMhL|U)L!NfBO&HusG4Y@7BG;w>Y~EJ=gxnPvKA2 zA`i<=Z9U~K4PTOskm7pC>vVX|*-m~p{S}7J4L)l|6EPE<>sYek!ZO(MrXEy+V!!@@ zV#eQqHwO88$&UA6{WE{~L1*nE6?ce*7`CsJ$yx+ir3|x*TGZ> zOMsCXX8&4d^Sw)#CPbt9g?Hax*(Xri2eJ)(E}2bWAdvxonrk!*dDib8nxa;uts zhMI~4Pm=#MkSb&ah7l75Bm1vd{v)>qS{}a)O$OZ&9`9W&)K=UvD|{aCJ_*cdg(C5=Igln2DKoeh76!JrS^2`#*|CfWMnR{_; z-%1>^%gw731KM_QxA~&DPW0Jv6Mv_clfQZhw?q-6{CT@)5f5yBF!*)QMh;%J;sNF5 zQ7DmJ&C6zj zrMw{FHkmo~7xuvzmX!5UXYCa(Q|~ztN=`(Dazu;3!e1b6`+KLF5VXLG8*i4Rk0FeK z`O!^aV)BEoj~cf>uMf(lnV)x?AI`~pjl2hwN{j_-dCS1^wD@$r}u;D@|jQim4SBb3apUg!H9G zaF5t~py?H&-Ha7*PAEIMMPp`dk+Oxb>Tf;R9PC@qP^K4}-)T{*7TwC59sieDpKk+* z^?%Ii*JQB_bXjs0Q5Xy|ZO>cU&aAcsl`BAYCsg;MJVd!-0EWk8`y*RX<5RK2v4B9J ztFLj4tAA2iDon|WNCPM`wu9lf8iOiWnQ~c~jgET?q%rlE8AnfcR@PRbn7tWTTtm$s z?f&xX#)+I~a!=2w@7w}~5f6cVh#%d)I(JEa2(?fPb&i#Yu0~)uFf*7? zw_i6wdw|a$rbx#*A=;W24f;-9Fk4!j$=wAVrqt=H>cg$-gXJ2}=|S9A?$ ztyk4^Z@MTSTO{*5EwfrCm}rdJn)$)?44AqnGFsJ`N^hcq9Q)D$w|y*sWx0Shv(q zJ5$JGwtQ#jR=eA3#@<@Kc8}`wDvQs*Axwkf;=jEhUKT2miZue7y_6c&n0=S>(dUd9 zsUJ9&Dt9-19;NHUBO}7z{SnBsh|>DOWYr}JH@#OLC06qn1|s)!3LQMR8{2)77!_4k zOKQq|uT%|&5oO{u#?1W_-Jy^seEjjs6kD-T`cFw4;pU(2H$-*rB#P)Ppep#vT){nP zUv?uZXg!O_DcA3Dmg9#=QSbF^d%);n_wBmK_iPP}z7s(qOUkELzzm!~FSm&<-REWK zFZyIIU(Cx~E|-!m=aVf)Lpm==L+_y=Ll4Q^x2sxNYw?6!6VDR+GGch>tchu}0>aTm zynTwfb2R#*1C;D3v0^LQ|5VQs_2CS?zA8Ul#AEA%Dg~0Ej{DsXkKJmNCsBzv8hzmx z$9QyK{)FF^+qtj6jB}6cI?2U%QFbG)Wj%|F)c3rw6FfWy1wRCbL9(R$1VSZzR2AIT zcQzqF;)g91VgIo7p#6fi{RGu^bbGtsc5qDf6iXaNd%Xp-JEM6Vm!4_K5vL+4;<)@! zVnE@tFVnUM?G2z1+qTOs&~|*0j$`qMxzqS-k||C6@h_+Eb(C>uj1^TIS?agyxOo3j z22zjxhWG~L3&zFcKz=l`(uS$GRZzOff*pe&p9IVU6p;jUn!*lT??3PXdPr2E29aq& zEXTy;LjNCU@8M7NAGeR6b2!Gaj&ZDvV{^=msEk8Gh=XH_B!?~5+QrFC096ukM`^cvDdUcd(k6nYkt zJW#@XH)X#O9IjGin}^gB#ON~Wdns9ttYv#J(zeD3fLLU;0k07U7BA0x0$w8U{O|$0{{w1ccoOSKaDbX8;tF7_P z`>6OPn_j_b5)?oO=qo$`LrkgI{pNZ4=0INJf>}29mm#HY7OieOHVZ z`=W(o08y#fYaUUy&ZYb_NO(;E>7jS}C7r=bnXz}U7l7Z{E>kk;VBIT9OmGpKy_RGM zPGFnJ5ZrQr+yX~Fny`&&3P!1M5dm$#h53KJ|7+oo(G@Q7>9cqSP_Y{blG9tpM6)Li zR>43`AT?-=hzE0V`nqEbAay%-{jT?bzNKkx3~a&S%`vk2Y8Lz9r6+JN-DnR`VTK|* zJ))mjycH3$Mv3#qO}LSwCbhmDAck*We4fX^^8T0p`Ss9xmnEF<^8>yDNE(wx77?x@ zNVk}kvQ9z{@SE@QBoE!dPr_^q_)}`d6)ASKCwh_QU!)-1)*f8v|19=izMpZYTC6(U zrOj}pYlTPT%=61{4OjoGiDSL5$f{SPZ3?AXO%M@r)WwfzNC{pC4D3+09>P0VurTpk1P?4*%f7O>>C z*Q0#EV$iMB6BFXaF~TkZ=Tr$BUk%(6IS*82yewyx=`N&n*?duRh=#FzSSgkbo@P01 zyQ3%o&3s?Xap_z5QLN{SubNi};eJr<9CXuzS~0g@F87lP zq5&W#^FKT#r^=yD05e8q=f-~?+)p?&o$X>C?N>-sM5$QCR9!;#2+)5i;dgiLF&}*n z`%C}*;|<9WT*AfOuVZy3B(%<>KPJF3JKaDrAhrLGN8~M*Q+u^=gINLEsoYhZCDDsg zCN?A34Q;daD7jcK5?aTa#lAl7iwBZq5FRe{!`ihUvB7674w2I*BWbna)~~(e*_3vk z(g3Hdz_<#ECnz10j;Eu*eqTDikD?FOwRM>EvyNg4Ki!?Po}M)4iy^WuWoxq~OL$&5 zgYbruAV5P@E4SG3Z?K?9{2T0Q0ec3$ETj9vS=p@{BqIpM04t^A*H@=+&QDDidTqSp zt<3IDc~iw~)738z5~w+WN7v8LAXZxk|LC&ep16TI?MIcOaFybjcUFGe#5~ul;RpQw zl3(7`9QM^Y(5j^=iXC1y8Hs{yWMKsV(`pSRx};blFROg;8ngA=)kNrqcz@4(6g z3Cv16E5=)cA`Vt&sn`sMwWT=FJQe6Z3DfnS?JY##B?v}U6*1iH%{n|W*M zWuBka56PCY>js-fcDZ+8@N3_6sd+-Oz{Q`1ys^uLz*tDtG^9elF;>-RBR2sXqA@iGxWsO7@3w%^JFvxi7-i(RGlA_PFmCGIIeh{g7;cl3*gh>B{NzSP(;V zc&%jqa>666rG$Q z{cx{Z5L1s3X~BsTGiC_@l-Bk>Jv*;H`;`Ci)R50`8wlu$bg>~}eqifU_p@*T2E>v7 zCfB(rkiHjq*0DotA}Sb3xIZ@daj6~>^j&e8fL^M(C!<{qI8D2n?6dq;#P#Dc?N1E) ze8HcocJx`DP3AgTvI5C~MEkw_1X(}O8dHj^WKS>Sf#z^ssm@w@5Jljr+<;>* zXr<={;}5m$4R6+BLjdm2G}!asX|S8&)>L#OK(XH!(7d|PHms8%D)12KsN0&e;9~^E zM<2P{ipDEpZ|CV^PE)u`Gcnh&aN9}9DzhMmd8v$v3Jni8i+yVsw(}fQP9d)b&0N)c zBHPuElrw#J#XUw)XxXb1SV2H+GwJW|#$yo&n-R1|EE(z^Ac^t|5^~JnpFqqiKjoXv z6V>*jY&23QGKM)avt;Ks9v|Z{1r54#r|Pq(iQl(ni$R@0hV-k*w)irx*+@V z5S!P0Y$l7lVKrOlMXAi7&yvV7!Nkx^HqT3cs-D@IMonh>r+6a_da38JvRr2m58Q~wtT z2HcYp@{m#tX`)}>ZmZvPJg)gfjZ;oNYdre)W;p@D$Du&ORoUv6wq9`H=>>h_yPl1y z^bf230$bpS=b_!>r-^hC(_iZ-eYjtUoMQqmTg1p4ax=6!{s$M%WatQ!IFoka<5LBT z`eHcVm809CY)tfJZnl5x4`_tZwlcBJ5K3_?rE!>@0GOqJ+Syiuvmgu)mNmde!u}Gw zBv2Z_iez46l{$Pw{gw3HpDJcht?6(Dk;cRe zkM^$76Us(X#rlb7PZPeaDzyHzK_0zoa@C56yyn%cd+$^mZJUv#d2T5DuoQnGB?m5~ zHJ#<^cP5ujZV12?=qWe7MP17r4b*D3`|GuMWNdzQoMtPvYje6be|@{Z6lLP#7uNHs zrnA=zkIKtbc`6kYp8UT%P8WW4oPyP5doyZ=pJZ$KWyF&e4Ek3=c!Ka+t``Ee?}d7 z@%xifKj*R!gt1p*$ns*Cxt(+bC5H&14M9*7VKLh)L72|m7ko}68T%=3?%x-fC|tW< zEP)nsQ_ERoV4JHW(V>zBgganL%!MmLYsK&+HjRKD@x5SAa@E{&k=i5ypD$9EP80Z= zHd*O?K*t1;@l#73|I$bDStk1Wv7gSG)F5xko+y+L8nGswWWONo`U*siZ*+pQAhlc{MW@%Fjm zVe{lWwS!rJmas;}V0DiLiKSwAsC!wRD$zL8vN4nt@%>W|yKGrey1f!k-NOITmD-&P zr`4=nXU*RHa<@Tq9~~Dkb^Duo@!y?=w>18PKJ(WBVRt7W>^`>p;zx<+TvXB4!1o~+ zlwS~YjP5Ugo8h{-5qn=eQD&NUy&+C5J0U{sB%zKV21YYc0O^YcX(-CP;V3U-Fn!q; z6MFI_`ROQ|1%tc=6`6|fUbmCo4l6)DMTa)5Pm4gvsO4Ia4H#=Y_57208on~0CRA&O zk}(QL_7=EPp5#0(V9ICW0B?xKE3gD#H`*-#2ac3hY^+_!Lb|1>3W;jd>5NNZ4~8%+ zqgt+#IcM*e83kY1@@!FP7Z&%4F+50a!BggwO(Def%=V^tpC*XEC~JEYd@}xV!_R`v z@|0l3cC*e!y^lvc#J&nbCPyeHNXNeaUOA8*y8zUI5UHu2Cw&+*hxKPRsCcq#_}JE| z+$;mAPI6Nr(N_xBPh^x)vr-6im!%sS|~ZNaFc3Iqi$Kqx7%Uq-Y5{lvtw z4@|K|KXT`ONz>)IbK_Q$ZUml%3)v@wzB~^)*|g~%--UBr8czY8c-Leoh*|Cntn{C@z!0E~lmyzT?^afpLM}evYr~(cV()>7DiwYntk6zJZFj!@GH&6fp8E1g z{Kv`aVTtl<16554U22bK&O~Zufj{n4)co=8dVj~^-fX2`o-N?<47r2D0uxW?RvOmJ z&1c)rH%j|VKLIOpajPb0&0g>&`aD@B8x(D7>EF9*;T}VPM`8MZqwsVSR`AVP#|~BN zbQ>bI4i^VpPl-|uV5Bx?r9pLD%PWvgI^N!Y4~QTviB_)S*OsVq0xx`!i_drRo)Q=x zf2a#DYG95%pYDF6aQ^#SpL##CSKG!C@>xv!7v&xTKC9~)fu)hN3VIaikOLMW6aAU? z+CdI(2Wbw4C18^C&LoQ^A#uaCpfS%N)}E`wKQBOMR9@Ic+>hs~eM@}0AiM%0-VO;H zJ@e^+J{CQ#B&xUN1XVe`eD*n|=e5bpEeC~v6shBTg-!##LVXz*lF%Lqf$&fv{fdGb z7T6P|VEajr2~p=me(i$-;?;*oe1(LuCytygaAcl41qfc*mt?~y&BcNcoN`wA!M0z< z`4yhq`+?&dd$5*PjDGzt3tVe1H%A|^WpMHvf@o=Fl;jneN7K2F2#3)U2{EQO=Kk{d zyDf0svhjIv_8-@kmMCvMjWf=OBIc7Lh83(5=-bIWx_C6c^YWelerz3XZ5i}B>}$bV z`aO5;&Moso3Q&9twk2N|X*bb>i~(AFZYy@4QF0fSrCAqXv$6;7DnZ7h3;EE3ij7OZ zeNo*`5>%;{+0kT{`f1`EW)U#D-X_r->**MYx5_Lg{T4j^qg-{NVrv~2-J9Mh^x9}2 zNP>T`58?d8T>B>Bum`+MGtcr{P7Pq_9)2hKXT#3l0>@kiI$6b9 z_J6Pqr(hw7;w{}#b5R%5}>*f3j2l1Y$)D4JV;l+S{mKow~Av>)-K$* zTw(ueJo49_BD)$mg@>8)*ZuO)BN!H4Iv2atns%q}E+pJK%#d1G4^N{B92Z>|XuLpUO={*1MM;dAw6H4$IcYEZXxQ=nE2&jp_89}7H20r8)oJ!+o30PyB7{@8E{boY|Q7U)feFAam3|5^09Cufly zP33h4SLP00k0nJfccA(EH)RI?>qj}$ANd6i3VMjPo_9kG^qhnLgyV!a0<~r2?jBT3 z|AEC_$-Dl2ggPUig?TE2h+IpS!Q)3Qn+&d8mY{7%i zu6T?IHFFGKQv_-domKR`7JL@Z#49LKs6q8A6*nFA)87RgDV0g`FYPMVLzJ1Pu|eS+ zVaEQtF(6gM)p{818yXy4kOY_eqGz*qQR8ma+xQ5b+1;VG`q? zaXy&CqL;nkNPTfX4O@V@g1du_vmm{ug($FuY9#v&2TArVksB{1f(MJQIXz{80`{7q z^-~0d&KW~A81+|}pY&L4nW4-LNSqR7=>Ao8J!Dz%@%u9Q1;l;Dnt}dki&~8#AzNq? z)7$6Z2JG5(q6;f;S{Nrh>$`25iscM%dsSEvHJq8zu2Udr$1Vp89Rl%55p(^S(EwPHX zztrrUZXxETDY)e~C-sMyC6V$0&@0{j;!oRl6bt-kDa)NMJ;M*!)kAzKi|HTTgbx|< z-Gum->PaASJFR`=6G*m_eGC}PZ4+eJXKOY~OD_u`?#Z}NX8=`k2o5f#k2eYqzC_mZ zzMh}TBz8OwHDPjM?o+yRQ^v839l^zHZEXU5fHF#*&@ z|In(H5MuiB;~s!k4U71@R!tj7h)3tvKvT<11I$&kD-~7~qffIg7yhMJPl5Ei2@*Y6 ziSsG|8>o*lxO{ZQ8CVqukeQWwf6x1?X#{fr;yv>d+_+c*U}l{6=7B$*OwES$2Hid9 znYc;fzY*jYF6>{&f7qXwy|3DXx=jLEi@-+3(G!G3hG&A>zQoStUj}pDwLG?D{7x2@ zPjOopxkq{T(Qyf?Itc}jY(JxmoT7#UJ|*sfTJXH)yK_(+-%&R0dVXyBlU0$)L+pCj zJbNKq#5hWwRZZhC0wj0PHkuB%sS&PX%<5TcYVr-%mURW>wlNg&0w5pbhO`bL@ z5EcYz@m|U?EzaIwE;@i>4@LBX!!7safFkC$H`SKfp1A*mHhpfYbC1^zcz{Rl@1~k( zDy6PXmbU~k?dCnaGl#-TSvi2-k<6*0gJov_BgyH1GZs?=Vg^mw51!5}tx2@*)?1F8)j(V4dcIC494i6>dp`BP$=TcpyY(j>?p7%nrowpL`K3>LE$kKwf-ArOug2ly#gF=x56l@xj6M3SH=3zhj~uuNQu>~ z=viCO-}>Z&IHwDiWDAK*`zPLhtJvYGabWk^{IRONxbNVoqW03m)S6dK9LuIrA%59V zLPl0#fM(tC3RKOZGumD$4i0I7pgp}zn+R7xT{7Aa>;ei{pDEQ42ima%EpyDvmdvZa zMe49lPLmlue9i0)$L?dy4dekZcZTn`O8uhk(SJ+SC!gOg|1ZeZn>pscwnYON*3U+* z@Dil%W$6$XLkG-lsQ(PmFtuSvN?E8y2K2o>k|=tA?yKHN;XbuJ8kHD9QRG)FxGP!` zJ*OU6l%^deh|YPfaXyhTpa8)SC3ONUw)rzc(8=DN_-AHLthDFYI@-i$GKiFI0QT0#W@cn@^U?xMl%K2rq`y1^ z|1@)gDd18bG`x@jGK=VUKh1XBu-Kmah+Vf7omGR)QSivaS zC7+1zjWbP--QJrUEpV|psv$Y9d+}YkV1V_qm9AJyXGdL)<@I zqsKCIY)hL$&8v)8d-v_r$Gw2s*LLdfxbqiR#jl=>LaFuomn>OEOO?LQCVkF3)a3C1 ztpZZAE+LJyq#-vkOwu0%<^9s3DK~fS3&4s&AaWcQ^f~rQ>E-FD!A`iwKHs|$(xVw$ zo6hRmCS$7|Hw+`39|){>LP}cpO`vPtxvz|6X^P<(Thr zXktEfH&*zK$Inkm0jnWDptf4^O7gP$^YlN8$?#Hi&NLb*Cc7CvJ*a!_Fj5d4^S+Ie z;w3hCxliG|7)VV*uAAj(l9|w8N&*#JK&Sa{63moiwmVYZF_|@<{NmPo9I}N1-#LZ| zFp6Ol>5-7_M9@ECI~j0D!SdbU;}z`MyrHiCw-G@ti(5EUJp3UZWtmH(*ROa>Ze4gQ zQRYlw`muY|prMs8O~(n5(I_4Ik6EyPrG-ama@|Ivh%1jAYECW;aL*v!&O(E2j_f5+Q~9Tn?v{MrqnKCc&-b-Z^|^ z?{F`5ExguauPk4pcC6WH*Bl(uevHJ3Lu|rN^h?OLIA(^QU#)a8A-cyv7T12NS#}6m zd_2vxwx~(`75)HAicJfHi92>)M_u-2S-NuV11Z(%EdOrN_+Mibxnhzi4`tv zfp4`v{uq`;NlG5qT2&GXzHyNXY<&PJMIPC|0u>?&Wnom^+S3qTCt@|A$n^%Vw+hwy zN(lFdt;;f7$?a@g9M4B-2z4dSd}BdA6Gqii(mrP5p!3M`(f$ktk~E#OhPxXHr=DrD zPh_2)nLdMPkR83nG1m!J+H^Xd8nD!jGZYkG-WJQWYV_I-e5t>wcaa1`Dume}XHfV( z)!HwC{@6!&3-D`Y_)i;G3?Z0rh7}`D$f0CRr}JnU4FRwvT&|t>XxQ*kjJyD3qxzWi z@3Tclrxspsi88sN5+;3=unaDuF=gylb;?z=?dKyw4*>VzwqN&PTRU6?D$%!c-fXmq78t|yqZ~jh*B2|7(NbBlc?)wRFvs0 zMn*dwq(uW-v%dEk*hs~?Apw!-h2Z!-UKDtC{lqY6`+-uizGW=)AnS*&#Q_HnVjs?% zm9PiPn?ZgNCN>ZM&-dUJtLv?rp9ea2ble*Lr<1S3JmEwq z5ztGU#X=(PbOBJm@$M`%Q^9*_45?;Ey|{ypIzl0s(zRbg3C7C3qKz%+;ZGsQooOYe z)w+3jbu7{C(_8l0MI6cNZOpLf87@KsyM|Xd^eBvXXT!%1s@}~WqmI;IS)h<~F@5QM zS(Xd9;{){9lBnTrbPfmn)HZW+^uYDXI=e%&C!vzZ2j7b0wbD|Nk0%4?^s#XJ381y; za&+p-!4OwPS8Y&p$96|`#A+{yRtglv6K7o7#81wj+rnor%k&x3`J?!!fCgN$ z$Y}9?2(KfM~ z6awjoz|wJN@TfX9^%GSysAID)zFy~605(4_rA;c9RaAWCAo~wU zodx&S-y_fq!f(WjsS3BLr%nt|H_r!?pt?OAfDUuz}P8kmw1(zmmkWkFW>+?gmWk+|`p(+iuZUm|jw zw{gdgRv|#pYlRlC%sB;vY2yN!HzD=b9do zS!%35rehQ9ZBq5Dn(=HC3mgL)WW~c3%4(-Eeg{z;_$%%woP@?l_ut)=2Ee>qCg5qf z)({cRwXbmzSN(p9T<__lrGmiJVyAD@4Ej09R>vv;PK{)a9qzvxDt}b6?_|4&gc@6N z&b=DGYxs8mdS~G6Xu#Z#n%{P^!!drtC?g6)T3tVuhfEB!u^wy1K5E>ygkl}=1}7eF zeRNQi9M=NSxbpJFq=#(JEI9s+FQse8-*zhhP>L_s4KH=WWQa8}tF%8^YxI5JHL}B1 zKUSWYSFtOu(IGYRl*nMZ;t3$RUOx!o2jKM8=gv;L zRRBy20#lA_A|ttDZvPcVT~wh<{a9`cnmvtQX78ZaPc1qud8R~wxVuR)sR){ z5uq^phb%l4MJY#2^0uhuVc?JXb<$l~2Fasm!!o9K48aYuYR-Sd%!KV2;Ad&rZ=NpN zE950CAfzww2#HLO3*@#lT>0&C#$T+7>w<_3DQC`{A2Xr^+FNeIYa|Aefl9?wzgQ~a z#Gk;4UpGH)B@e=f429B@Jmpt@ny50W=4t7W?W9(3uiy%10$#9Zn}Me1TOjx9NqiN9 z%xYTN=s*G_S31gvHUpLzJvF4y*`9ldBE(ky=-fh*X4T+Ct7IW_rI&HxRu-0P!GW4bMq-67T`eZ^<)&d z(O2yNMkEYT(H2F^l;R#5{R=yRl;a9Y{SF1=vM<0>FT#rjv0{_TX<(}XE7RxMk$DOf zE)~-LID^E6SO%hxo~fW;JT)RVP5~K@eke0=;_Bzxdj~e{5=!-bg!grUR=N_$!v3%4 zXGYS%E%FvRwDshk$SfGmGn~P5tp3he&ik z-C68G?0=Ee7QKIy)B=Ox;CvVdL7yxerv3*@%zTLl=Vde8BSO=mk_ zE&NjyX7|c+)I_X?`^SBHBQuXv{KGgE!^9?gSb?w7&f&r-F85c1fD$g0pS>QzUo{(i zDDtHf-9%uoc{dlk&;};9;CZ*ZNo-wFJ+blpMf1~-{7Ng6+{uKlI~ot(#fJ&;aQb9F zIsWdE6Ep(xT`L!E{ovIK-FZubj~7FLvD^#5SZfikaDDfc;L)DGrs+4YH_}=s;L5d= zN@})@9AVI3da&jAIeNgmeATdMXG5?MwYP-et5E6RP-BxMbeoe>_RlsTZmnlxzv~mr zgk&07li#A{_n88XLro_ppGBzug!%BLWx_fujEYWw>+LU8LGh>X1 zG-I+c!48Yfg6?T&rd#ojsTH!2iVjM@v%*5x zk3z^oV9U}4`2u;aV;+OdRbtdihCET*&0BJq57ScVD4#0~ptn zkl)u&yt>7*Y$?jclUfJUznkc#Q=#(2{u)u(S*XpoYJ~SFP`&1gqib`932I@z#D2-b z-OB%vg|mOi!s(Z*vc1>@rH&p6$ccEKqj`CJnbm+*94o?(J-<2X{g3C$+ESBp3e)Wr zGi(SuX{6JxxCf0Z=_R!0%Erx6+R;X)m08rM2Yo`Owp1 z#@}abgF1s_e>0ew6a)EI>)|rnht@uimy-(>l`FQD%Dq?0=>@xj;oz7T~MC?awLzP zQ#p<&MHesTKZ0k_7-h^ciTJMD`2w47LoWZaBD10w*~B|;7nBWL4XjA@AKMpQtUg)t z{B~fhAPIMSh*#RbtjPFIV*36#IkXGUh`NuYAv1Q%I{?xqq}VG10?@PBFL=#y@{E*~ zc@h&+;BBj?^00(b*u~jGW~Iiq4`=<1CKN>OsHp>X)D}XL!696BwC!EFmoquyqlokZwoZpFG%l`5L{N$f7qvQI6vea7%fG7e~1cz$a zt3CDnIlDo9d`Tlx)2!U0Hd0a;ICkpgoSwGus^P?qrbe!$_c{X zM)m~**xp?HacG5UjBWfrwDL^X@ysOpms9TQt@NYC5MCUS_%O~({dkXv|FR(a4NRmB z6#!`=#gbR6%`;_q!{2oQE{?1QIYR{gXO>JN zeiG8cJLzrDy18BosZC0t3rNj~Uk=QBWD_`+Ma|=q0(9Zh~ zu)}MwBXAZ7hU^KmpLCl%@rVDIPF1p_qXs7VWs&xHuFXdsIVpRO3HxlxG^z`rFyYx3 zDX>TJFF~;`M+^+9m-)wv54*NreO>IO^pcQ;^?zlSkc(wX;1Sfpw!8E-z8W2@g+6>N zP4O-C7m{M-4_g6Zx_mZXDeB^x=X!L(6yASYkFj9A*^N}Z_1OI%*5i|EzZGA5Xz-j1 zks#4MAHnzWsKb5rqKHG!c2Xe+>WAv^p89*@Iv-fcH|%^Rx2{#QM1GOqJTvyvms1(2 z>xpt^6zp~bJfQ!wJ~dUQHYVjenNH?l8@Y0kST8B=Ncpu<-TrO6+l>?68+Idk)$&3-tasEL&e6HnGA%&LdR)LxF;FM!nuQ##xEe(MD3aJ_#Ad!`C%JKaJ; z=~;p!kU*BGj=InaRj^@lNP_r?@5OV~>*G$}shs@d?23oh_^yN^jsU&>L-Q~o1i#M( zgy2^SfF{?IG39>`0~NgqJu}TU0E@@%j-)8;OPi50)*nJMKA*slavL6<5omT|T%xat|uR&yUongP+LYgk@#vB61iM&xCOXaAt7o z#!*J#jC7XE8p<281=Tkcx&*tPo5v=Z@YZ>Zd(At&ppE^={pQLF-DSHAlLy#C%9Ak> zw~pi_GoqLCH!RX4kiFoRS09!Y?MKsdb|Aw60|_xjqvY+B`VHa^8I>;^r_NDPktG=D z17QdAoddT`{Gx933`&#E?L{F^Y9>to<4JAhX4D(rS(PdBnAo6f(1U4aAMgVwP}?@R z?w*bMcezs^{0i-;?zbqT@^mf4iDab+co+gED1w5Z>bfkT$`c1`r|u7Jm&;&--aaZ? zsJ_7RLD*NHL#SKtd{{mwB!p?_{>4MNy_b)|tmg@21T2^Dx16h*N&8nT8iVtMLW}ccEQu&x&F;2E?ui`3w#qSVugfx980yQoA6n`>xR(?yk&9m5 z`uc8v??$kV??IDwx*t2{{?r=rsW)7DnJoqvvWisH_pgT_m<)7g9g;hv;_Km{q-1c` z>vPol8AVaak+lqQF<}rDE+upL>!;y4Pe6G{|4SE)G;Uy^L4Bid@RJQ)qtdyc8hV$F z@u&yd>j=g?2xFZ~i@4H`&>Ir`R>Q4?RejU2m*)j1c07=aq{p0?C!9_AAXaGSiyz== z=HU6{;d4U~CSZ`+EJmO#V%dx((;Q>s*rL7unL-D!GT*2whI}Rkw;$ zgtczp%^A@9_HTtJA;;hzU96X*EPGNI5IK|dtvrXe(=~(S@QVXekdAeb8cq>iI-u5f z7(EPWql4yJ6LTd+Q0Rea07m^Ni+9IWZ^rtu!WF5bDhsJQI{J4Q2t0?8rLf9G2y%$T zutd*kT6|(xqoiw3F1uE?~m}d=?1H?1;L=P?1;CgGi-)nckP^2E1wW*E4t@4Ito9V!rruD!FLkyUw?#XDEqHj?PyxNbE)4Hk>*1oSX8=Y8lJuQBFuC=cvN=BW@SeR-`s z)<0j6Th~iL|5>3bJMa;gD+L3|qKqs;4Yf#pe|v-4nO#FowFk0V90*!q7z-R1OE}Xa zr{4tsVx9{k3OVT9m^~)SMekbapzD~cvP}E{rhnS7vkR)|MCvUVKE+LG43SSyV>cWn z_2Y=Y>Ars1qOg$F%ZRz_p4)}v+qP&X_q^4a?&Jd!DI9%#Y5&4T=+0~Yy9pu~;QOS> zy|-ALr~E-^bf847All)p$*3{!B!1X6bcO=aOCA29NkG5A!dp8H9NT*+Uf52{Y|#$^ z1Cil=g|;S>Gto*-;!rN-f|Bj81b_uy5LN_KXB52m_O{bn4>CEns%!i5s5AQZtW7QMgeFCmZRS_xvVZR3ui$B{@u zuna~9Eo;XZj|Gz`M4-Dq)Q=OS{o!0BM?kJ+lZL*9j4*`YQ@VNej<5A`7-;T^yHh?WZHwC#Jgw#1P0zF_7-Z z8AT1STAbeleRR=+w&~ZMkM3sQ3YFC2@-^X)#anFMLx}5Il*Bnz1{xn)kE`uObt`nK z7jgP{-lt7`t~lG{L?-JhopYO&oa~D4%&A(_v*k)qH7g>uB3c%h>#Byy)k+QOpvJE6 zBg~{3MmW=LZ!Tp1U+^*F6T{{bJdiGG`%;2es6B2mif?3VQfXCk>t0kel)3#i_0V-ELU>=D zWMIi40|PY`hqB1icj=b|eNn4bBe51pHc~l7aDLlUgub0i3=6&@0W6!54T)eu6{InQ zGQFQA=F)rr|8%Y>$lDJ>wH5Aum*l>GC!E zpkdN_;ibNMTXToDM|D;&lV!7@nWp44uV_?*k-CYUq*$0{2QJ?(V}>ML8lLsG zrF9V$_Gv+yi(R*`(esf8qTy7yt=_`*@gj0EA<2`Ra}4vYbRsp)8JZnQ{^Om1Q@_hH&2N4dtn z_O+kVjyE@=)-jf4ga~jo?V!*Ql&-h%8M$j0v~5VPx8Dbb0SaNXy?!rWB_!686T%O3 zd7G*zlSZP-GfDG`V8s0jLewlUupW$?i7a$+{PFJQJ6PBLp8v}igkiV{x<&HnQIxuk zbiMk1v;dN;B))p)7MWQu<4D*5gYxoC zP61(@K+e$P*oucYG6x&~dLr0y7t`aObQUi-c69OW!pLvfkx1e0oA51rz9^j`f#m)9 zPUx+ixW=a+_+90m^izu_b#uRM;UW7mF~1Np;V&j4Mn#o&`679#DZ{8my_-a8ox2T@Z`n|1?x{G_G1a+=r^5=p1_ zHcBFNmYv`=4_F>R?warCr8$co^7nxhF;UMp$AXsUJ_<1p&2>m`&idIHLl zowm`m*%c5%9f>nD5M-4w3QNFCPlVRIAI&*2kIhhYk0W#!n!5uLz^AWVz+l_$DnRT;kmrAJ~OusTWrlzbte^6t%}rYUjkjR zQ|%8dQXzOpi7n=R=->-$5%XO4Zhos!6AHTE{%HeC->yVy8!XV>$hapgQx0Q|wHF`) zSs_u ziQEx04!Ho7eEXNx`8hf8^Dk9`pP8x{DC(TKKPqf+tatcFxZf!%u_B6s0$R^Pl7s4LE-r+*~Irtf;zEFOD>zIVC;nb0ugd~&dx{ndP z7utix)cR}An(KhWUcPf#r6`YL?q{+K=N{1QgWmA_6hS{5LVfiErF~apbWPy#(?%3Q z&|jB7a|BGyMkz$2J`EAxbi1Z<{YU5pMVN^Mz&{(RABk63@>C^>wl{??+pZ}SZ( z2|Zfi0$K#CgExua9XA!ALhzpNW6R@@300czLQle?EQF=(>aB+S^aemSEGFgW`Oj4c7LJ6~wgHir#iH3v1 z>U>n}#8>cd-%~%|zR7FWrdx3Z7@ZV+QOboeOf$>9C0# zwEGMphqyYIKiOquOt;(AhJ!pM(2{nS*H_TAHKWcKFI^_0D$YO-HSzHJYic~15_p{l z!(~yR>F}?hJoTnn@ZHZn;`4F@63(dQ(dqPxDDr!~m)>{it%G3{kockswF11Oe7T;; z9cEXnuJ?4GB0=gVxWQqO*Dt$`^)bCXrCcbb#be%A#d_=^o6N{HmD>rv8wYMq1`=Zz zvk=iDIG^qJCM)gS<*svnGS28HzU}blmPd&ay*vCRPX>x(R@w!dX3S>W zqTv6Mc}iJ-H?U0Z{PZ=^PujDJ2{8^d|v@m}l_mry&aCQ#YIU zC|(hDK2`8ezsTPu5Bq@Ycy%S=mR)7Zr6&QK2S?vG4Kd%dHcCkRbaq1#$f(G4lMR-g z#&dw|6BMm*L`N@To>${rKNXxuRY_qRzLAtRuT`k5^^uU}d1+%sL`1I-RJgUxx&ZV* zB`T1)7lHglyzO#sr>-f&8*K!^LC>xxg5JoLtF+`k=S1RGr9F-wokZV*4peS<9QeEN zH+Mc(Rs+K)bm8_}*FM667RQ&W??tVC&9(C;bac|8t)NX_Q|aj}(v7E-bd+Rs zxWc1Q6wnvh8k59syKh~|9jmH1e+9?S6$Q^2`IMOtlc|a8QS&-eGNqitnf40g{_GOn zI5-m~V|y4wBH8`yX5x#sMtQ5Nd@5z1E52Tx(HMrmEu5b*S4;P+^~4Pd8pM{1>CAeM zYf>iAd6!1ObTxel*$nPao$urfF*7mFkpd?-ut%Dikm-R}U(mu5n6%JE%` zIKh>-ZNw9aeyr`Adn?aaz1%`=1rs!ai3>&Vtl!eN+>;r5#}b)lOeDSiMbRgBrYsva zfbn4o1m9C$P}zLW%A9gJfg+^Gusq1peI2ylHh_A?f}2tr61i6IP>&6|r)Imot_6&|SgEZ{j*vg%fN- zQ;b>hgPpyNf@`9@i?pZ9Bg~Qqn4TU@1dwN3W!0S|_ya`8=xx(sb!UP5;*{m3vriXtN1g^(JJ#nNkz;Woad&7boCfKPOH?CPJo=cB+26+~DPBziv-6}Q zTMNs@Af1~@g(IAL>4}4lN{fPMzz?yP5aNoiq~HlmrG9*F+PUk zNi#es_31n7q0RC3?_Z<_3P$^_BaQD$pF~k=eKe~2P~&dxn7Q*Q3gSZy^lZX?Aks>=O7|+MIekD1dPK_Ux={6w?a7F* z#OOI#w#-?$a^HfONo6MUDm*mLm=W|ke!?~8{5O@v;eEkU=V}Mc`KNtX^Rf&noQmLN zBLf!~PX%SleJ^aFTr2a+8z;_QY4}i_0nZ;pX*D-gwdW2TJWCFD%=CR1a`(hU+n$u= zbO^;5r!D!NYLa)^y~6dU9LLD{-YSqC?r&&MpTMBJJA2p{Wlp(VDH25YMkW|()U1J? zi=wBhiWhIqz2F9l*AoWdhM>AQ&T`>Ebp*2qd`@EE@evo>_10&mA~Aq5(Fk31SfAp@ z{@P0SSWMFQor(WCYM7yUv9l)6DtnZojWhN{(IC3rG3FDIVS`!ZJ_a|Mip*0WAWXi6 z{!|&gP=Q8V?TSkYNASx#M3=}2^pRwGMPTXU$Cb|SGGsfFy((4;%}D?hq(nOm)S}Ce z=V_vjGzgH8x+yQUqts#naz_|djajFzeM=5E4~~e$tjT1mBQ6}u%`^pCtbKdOy_WC! zx_ds|f9sPY=Z5Nz>&486EWmShs94`*vpQ$x`Txh(Sw2M7K;3?3=iT8Q$z5jq8@XR@Dul-wV7u*vZ&H5~!Kv=cU z4lkJm1wbVfVi5yv%x$s_@RX@>0jc zL=K?Xg?!kGs#I7(J)ixu1#bh~Gm@zbS>N~L-~8Oq$W8IdDa;nmObi!gh5>0puznhh zPJN~j^_*~LujS!Dx4_AWHVm~L8xfC2pT%?}#Oq>K_r^=zmRQ&pUeW7p1-R9_i+D?o zMAp}G>WAY+J;$;&Ks(Qh9?iwL8P&;ZttBx~6ZRcgg;_I%JKjyjnLd9DJ!|<{L`D zg1{}Y%_dmD#`BX@E>9k$4I{_m9ITS6)XUh4-VX^P-#VAo64x{9N@x(wtFa&OCIv_= z1^w>l=wY7G3#dKrFwqJ~_txbtZch`)9dR6?mmT(e6!`^%#GGs#vCrErysX1j?}CN- zVjEF;r+UqfY6cKc1{J1Zh+RAVLgWiFuYK4HD%OrN57B1eR=aluA4nA))Yg|6AskO< z>aeg5O-epQ6}q^3!b%%p7aURurSyE1Zn+~@723W8T(IDAVQ6N%KI0|z4*e9o;V{l@QY`5(tjcT92+pzxGDo^wf8 zR6waxxJAjX$tO=}0DsP%jaA`6B#J(}tf~wWXeqWO%DnGB`|zPfl4cxUZ897Uj==Ms zMJGN49|zw@o+{oHBl_+(kAZA2)RFfqn}7gl?Me*9?$%v;8IIsU(2-CE^=-M|E=G6(JE7C_0fLo z))>*J|KhI=;Vo~m&h)3j`(E_Hl#Q6*o_J5_mI!t;XIG`cIs?-PrHYtt-Py(5w--AE zE>$&0i_!Jca^CxY$v;uH4vn~$2z%{2&7^Pcjz?Sa1|g6GvRw|s;usE(v=q7dnB4KV0)2Yw66AQ zGAdP=h%IoE1onI=Jd+q(;_t)}#JN6mXh>~a{-kLFN~2fqI{%bL$Lz-uX;$^?bnSQ>9 zpo}49L=$)HmYL-}+O5novI%dj^$_HK*k}Ev!V=^kRhSbS;#mwTK83m(PT(`~X}44n zxuLcnBFemDXY^m0`J^hPG|D57pIuyOdTy^6t^6n&08)-M4uTD3S{jfV}S!h`clmKWJ>V;{_6+dAZ7g|$C> z7SWziy7*H_yjb+=v{_gVn`H4Ubd!hy(MfJ{FKY@nfG%b}aP-$@8ax1Zk;IEh%lenZ zH!|KX6A8u*=_i{bgw80(7(~e%_9q#Z zDh0D*l?hG?wOx*1!uzW1))pp&*hv4Zn@h_uGwOFGnx$?BLQqmFgySLK3PE$qwQc?{f8f9@s+l)(-DQzr4W0-tYKN z>R==8i)tXUA^!B@C6h1tw~jb3G9}S|O+!OcE#NxHyr9w4sSUlXy#TOAX$<7(loDpw z0Y6a~H!Y7|#o{L(6;Qb=b%DVRmsynRfmg-9s&$7~IQKJ8+s^$Sf*@<#oJ3mK_v#r( z3V$2;DAcYH@tRnh#TsYlQU<|X?uCm!WJ5 zgEh9hv>YUH{cO)5{GvMhA=j2Meg|ZvMke>#JQO8g9VQbMsDa~8jVx3is=~s3BaL2o zfgI`+5E*_iTwAcq(}T&J3&ci7I2MAW05+8!wlE+03F7nSEkh{3w0@mGq|reAQwsgZ zB2*sl;|RRW4bA1dm@LuBBlB^ftp)DW!VoGavK;qHHi9WqAfRKVyL5n3swyESDdZhj>vBYbQ7bv{1O_t$3KkV<- z46-nOlHw&{R6}|@4^hw*hFTFvB#LRZzcXTI_2z5<=?GQ5p zrP6{x;{)CyP%$C>K*l2cO_PfnC2V;L-JXHrL`NhbYKgIKph7Ag)$iS&!9=sWzfPck z+K(){8}nG=GU~B;%Pp>0Nc38egH6fLw}^*xwUzW_N~5k9ZY2Ohw}A5Y3ZP-jUIKEg z&(&zjs25&+-Hj3feztY>I@CC^ql0n4l<)_H>bZnyOD7rnzNa?f^XtST=XrzZ((>JO zRd{d4XS=;bQShKh=_+-w)`@{J6;>HK5l3bYklDfl%gP~JB80ObOSwmeL)o%=6$wca zLDE;3MG87nQ&vK;U0-))&UN`hltp1YK8-~QtpzW*x2LXOPfn?{j4pH)x-eMJR=)rq zN*&%tWZTBd;*&-d+mgv_F36aTwKaAX8jDp&AEVk(lT{mSm zpBeQ!W2Y_ZY8imDFWpXD06A8jFST#w*~yaA^C38SvqdoKzO;-pMpk=;JQzpoO93l^pCuhD)|y9U`;{YV3AsbERFafz4qax#)qnMiVzpQ;`_m^zV+ zPlSC;IM9Vg_M$6%8~PK>Vt0vNat{A3!HDe@wotGublbHKmylj(Uaf*#;Gc5%rb{Ta zR}Qw0b4ylxEWCAK);XPMfTO1z7sD#mErCbxA#L@uJzfc|WbuIN_U z-H|TvQ&5ChfRPuB&ykUUo(8uV_-mkZ_@2-Plui72a3s+ukB%#1k72ZJ$WwE=vRBLq z3aut2BJ13Z#ZocHE>I7WktD!<6n(sHuQ)q-!M}dul+W2Uk~cM(#0wb&?757d-5O1F z9mj-W;2&`$i;`kKJEAI^ihSd;oMZ$zm9(>?=^_ryWcTGhR^@oLZQ)z5rEYa?*@gAc zaY_L22!Gda8y<83rBW`_MVy(e?{T#VZ*;6}^6A za+tIIS<#oHb81}dA9oGjhm5Ez$%{WMWap?{*6FF1 zk^0MI)5s*QJd{SoaZCMYMLA|g?e`t+bxbt#cR+W6ThZWz2~}f7C{uNKFEihY^K-xo z{wyld{iW+S7hs%sOIabww8Tca@uOm&Q3hlav}9$QiuSAVn(CLC_~F>XmO7p!N^U)f zz)`;J_U;ZRSc%UqHkz}kHM$L~{QRWt3W))#9FTQ_H5a1YVqXFr1@n)1(X`!T26N_C zZ$>s;<`RGw14jdf1P|^H&cVIkVN+LDTj-F$@Yzl31fXstCvDklfrmYv&=*Tl-KW=*d|@=c1cu*8;<|D4;N_T#Hl3KpJPq<|v@-PO672N&P%%aq9!BZoVbUB%K)HpF&M42En>n8CNwbawG z&kmevzCtbteJ7K}%d-?;n%caRLEc^1ugz$w>}s}j4Dj_2UrcR7DU#qU6Ds!Kuw4NA z&UW#QLu{nZ0aQFLJpHOwx>1g|Xz#y5f{@~Y`bJD_=S5SjAJr0hyIH#mavIjbk6lsF z$tNCn5xcP6oRE+pg<|H%`o@(XgK_^+a3-VePlZ^+X*s zGGrT|Q_=lG#fCd%sR=Cd9TEMi78HIoDusz)1~+D5RJWvm+jh|S8?o`A3C+57!MVa| z1NbLOYyB}u`P(vl9#DLMEn&&+J^$5aquq+t))H{A#e>oZMCpCPWxZGhovcG$?Vh8; zy&POHHt_ulsVA)*31*$LJ6J`qA6#y^ew~q=bpjpf)2Iz3pA%Z^j0;vQ4qVPA>}WuS z5y}L;NMM!ZUVj-w37*eYsK0Y*{I7ZUAGs*~@PdI_h>GKTSL?5E0*%v(zs)nAR{knnT z3FRv&*;zQ+OnUYN==po_$3N@U{vb|T79Q3G6(ug%Dj=;|IFH@Cj9^I7OPAQiDi&VG zLaxV?HCYe%hz~D1dnBVQ^DA3uYpT9uS=sT=iYhKRMD85QSMSZtl&E>f4J>4GCK>D&#eFo}0Z-n$H zl?~VOqADIn#f)tE>V}b-H>-PIl~`Dk81tV^9rJrzgV-&M|?1?APpC)db|PwyIvkA_RH)u6?65 zH5Er~xm95Z{y4aAk6C=*yRt*_TbHVOK~lhT5j(KR>;Kx??0gB5~QL7NgP#C#ldt=uu9;rtq| zHLH<9gohwlnV6+n2lEy$_+xNNemb?6*m>}^GHY-doO{g0-n&i>VjnW6t{D597n93z zYsHe2#ven-YnRe*X4m%?VOO-S5AnQ5ce9DhGrS!21aR}ME6s@FYl7$BDLbnkyrLCD z0M|UkrG%GN|Kadc=dwRyRHnmk_XAU+%fmzipP=00gifjGVSnJVT?QYmwDE=Ph;6k6 zVjsYp1f7oBAe(83VO-1_E7N}xBBd>FITvKrJI80$t$}#oUCS;BW=qkCLWIiCLG6f9H9*e{Y;Qzbq1MouQ;eJ3*O^=0M7 zcGg=Co(9P%gJjJNw-0F|4>C2;8=Wk4-?bYUpCOx{B9Tz8oh&s2j4t{o|8QJYiMPF# zjB4E=uXG=oAZE8TGN{h2$=@F`H+0{L(=Zz!cw&%@rjb9wk(v#a3KRNiTg9m*K8Z47 z_%3jQ(CZKCM+2aKQsuV(vB|fuhFizgRVjc_93>hoGvWbbp}Y}VX6*oV&)r3^YXw%A zjd}3^Ec&F=O)k2nD4NeX<1>?&T0lyL+s`~-`Gjq19}VXsh!vseyL~->!iMcxmX-6m zB@+x~jUuga8yiQrQ-w?&f3O|wvi164&VKYv@o3J%<=u%UJ^@t&2nAPzLk7G zAh+U{i_U=O&7bH_rDC=d8w*pS7o*o1C-iX_nn&Ok zpCC>gM&5!`XBrel}h_W zJvOP^>9d)kX$#BW`}7tl1u!u0Mq=HIOAu#SK8^uFx!l6xNj_`X2vA@sQTlv`7(-H8&EEY1pF7ABh?Qo@Xv+q^(OX;Xijo13X zc5=_=0>lL;f7!&`12E??3|}i1kYV+nG0TX!4H9$_`WlWs%+2yx5pa;rI~NDjknd26 zn1{wLMXbxHj;G98EUfATd0MM|+2U(d-zG!@ij-rkFiXlomE^t>K!tjc!P%`?5uzW? ztYS-XZ^T$zc~YoFM1)IvDE9 zI+2+8(Qw^uJC?M!Wj!E}%AJ(1IwLA#4}&ujPo(&z?kU|qRtR!)Y&W*Q{gM+2-}(x0 zsjAx=9-lT}?=rVpDCbqeV#RG)qeDzpr4z9BPaMnj;c(q3o#IX|vt{9|X?=hXsWL7xKC3KFE=^pQV5^ zV3;sX`<&vkUU3KlrFO8RE?qq9G%j#OJc@KY5!o7f`FA!bsZ>ZLjG!s%tl z39O8J$x4d=f+OI3!y0}FP_Aji(^--nD$i8GRI?v|b^Z|IcErm@(klw)>%dbiP2kRo zIKwq69)=sKmeH!NauEcDu)h5O9G|X4jbl_`M!x z4_2CbgEVLR4P%Ewm;Gk_bTrer-1g|<3FS2n?g^%RQjawO7V$L8;#=7T1dsjN#>z`gz3uB7tK}41{tg2l z8%9PG-U`@3)Pb5c^+&{`o!j5gOMzRKpHERUUvlJv`SD)IJFL;gVc=X_y*P$YzyNd^+RBA6D~!oac4q7aVvpY*JiL19GX_vIs#&@e+T=7CBzn zu93kXX0vfTA9Y32P0*;1vA}H?>fZOU{x*H*va`VBFqWBGZ0D+o^Gl^u+<#n93efw8`s%fSOoHf{SfMTG{8w*0%G=>D`w8FHC$NL5#9(T zv}l~5oV=`_M6lMP_XJ}kyI9|qaMr0&)e^C9GO;$8{kKq6^XGw%eBMGdv4=an>(^%X zS1bye-(IWoST8_Cfzy6NLh;l|pJETN;qHLZ+rKd+(m&u*?9*LTt$w}RscHSjg*T_& zPPVXN2ZTZb{P9E-&ZN<%E&!NtIsiYnhM;@>@;#ryVY*YsVaf?1q(w}<2h?_)q6+F&)r(7ndX%sf?K!!!k%xyj$Ox< zc_9rQxBLeh!X$1qWU!`^&Vj zBmNP~IPFWxZ%#Z>s2Wk>?wROVUxO!nzW?(Y>Fu-_uy!{(KGU}5xM~%9@cm3W5eQ67 zjHxfaCkm~ObuRmH;gz5_6+y=Ow_TVe0P2dNSz1-yml4%8|HOE;f=i`&xBrLzxAS1X zoRId38SbxUeqGPtOg2tO+kn+FJkoC;-%`UsOc6mVjn~?@Mr0hpjG-s$S*m%tN!UJX zAfMy%Fm0-d)9`Tr??J&uVjNpb5f6=8F*tgDD>Y-I>W1(Xw|wz&553ZgEfH$YJ=feP}!bVFz8) zfg5wgTOpd=V>6-Q#K@IQ03EHBKuC-6BB*I=VAnQUr}0RKA6sqsH|3tCAsu((#$2!3L*w@%!Cp5k{jv`|USM zRT0Xb_NbBdTS_N37$aq901?Dsz~QkH0i35SwX@{#&`sXM5?fbE65OPiwn@~hnX0n! zYvr@{9kjDXDsQ>Zn^64pHW^GaNAuI7ohuK9U7-1`)}~;>J%dfRwk`fk8Jt;q&~9`i zMEbmWwdpV=X6J!NnVCwx{1rkb@2097aENsOSp=N5fL=W9G@JqF^5u-NvKE(B)~@Wgg}Tkjnz5Ta=b5QqjGNRsU{$SC zW8{(HLFoy4KF_Vh^3yVrS;KJT>rTbe;%!y4*$*@!iMi*A2Jm9A8?2l6WaRf%J-4!>w?&+M7`HMaq;T$dafF+aN3 z%zkmkGqr}7y5jdG|Ft2I$1{JdXABvJl!RVSF&ys>37f0uQ>21Ogq`7qIrofy+?-oZ z26Np9e=3VQ%|7=j3&Gp_vQe9seRlvAOmV8e!>Dr#dZ%1|1VuHvu(bbt98SR8*4){X z=f3<50c(U;;k7UdfEHeA&wLk`wD%WxO9Q{31^j`Bn6-uWlI|N1(_yr+yi+In49y}? zzpu^D&0&?>RfeODDFYAE7gY$!tLHOE`9(E&kt#*O=d5ZvnzqL`Jftc~enDR}yNO$2 z>^}l|c(FUg^ap7T{Ic3n(V{S)93aL!YP}%%B9B|+_<8sdl6bPdq%d@LB#60;qGRgf z&8vX_QbTb@F*Kot6hI_GjTHA%1TtI^LeD{TzN5>xnKamBR$BztgrQO0Z~Pg4&OcD*d(f)eSiIie5maafr@ zvPN?#QmhSkb#Z%0vqT&2!)`STabK8-`h9P$d)1Jx*LRAhrU!m%EQsE0#iNud3R!%& z^~IwEFjKlEC>tj&GQ*8>1XAoaQ6in+r!qIVLP6DEM!U{3Z>ekic3v7ORBg{|^g*7I&=<3SHMxTrWlt&t_oz zF<7vUHzM{%M0_W>K@r42;l@Y}hOj7hp_5Shg=l=ONA6IQL&4digU{{0W9g*qrF23U zet&sy9lQGba?vkOvyR4MC*&{%{Rx>D*<9l`>%7Z4w(Av_XT^7J%?7*oseI}~G2fKsU+^L&*x zFxP2ym{ln{YccRX<(;sbL4Ua0<0!mUAfTeCf6#&X4VXjvqFaA zbJ5oP{_l{onM>w_z#>EdFM8zRtlLUh+G70G*)?qgi1Qc~+g9rXustt{{Ts%pYRv>H zu?u5)J$-^wn;`Da;n83J$t%msxhFGDB$KZ?J=*!%KKsvGq1WOXM`2XHn(zhLQw){X z{5R^4nPsw7UCl*AeeEjL+F45$!fc0NRn1ZlL~G~B)zI*`QfAHInh4EyK3%)v6N-ii z(H2K)N6Lxbxe7Gxqc}VzN2G`k_vvq%anr&PCplkbq&9Zu620j=cmGv3=VWM?RA-HI z<6T$=ax)P`CqFC(NemSi^^zfBEj%kM&@~7{rsTZAOyawZQ7k_L%4!m@W3FK@a#24) zI2Qrcoz!ej!PTJ}h=|QB!^0syA;t?%Pyy-G#wXj(N4koU7}m{+|CFxyU6)!V_WLm# z?OxaM)!tNg=0D-+&O}&b>g`GCxH`r#z|;<|7tQP0_9+mEyE(`%wD$zt8*NR>%MqCR zhJx*)ki8_j78zpI;vI`FXIQ8U&+Pt-aoChv4mT11g>mFg{DX05r{Rz(eF3ksuN1do zM@PJFqyJNJEf||BIlAwW->xc4){htmK*)j7qPKLidS#y2KC-aAB~Fi3^;(xPy{m5= zt-cM3Z+zK*0wed>H)EnR!AZ2|N@Q4Y|PETGFh_R8T3mtuIAwMKckGfb7-q z^0YkkIQ>$WHN#u_&CgC27H8@^l*WmWhFWoQcDq{4ku^fU)k(N2Oo_w?{6QsA`-fkJ zp;uVd3gq8#SXK|_`pJa!#Rb*Q4yO4>wJH39r_0xh0yYPrAOm*-&Fql?We=c)U=NMnyh*W1c3qdcO3o zG@y$`{0C-R)5_jlCe1KBPI+SYo3yD{Y)8`Yp-iZ_7|uc%SAQGa+3<=n%4v6uR%5#V zQXbvD`n+<*lB^8Z7Ou}jUlG{%Uk^`;%hVTtXr-t)DtxEyM>2o1UuDXcIThtRnk!J_ zU~_dnWk7?bh~#pJF-(#!>l;2xuemr6PSM?1fY|>56T@Zpz0GkQE|urSJ$GJb1&Z+t6q=e50X&9z(bx*hW4+ZOh<2@I z5UEicF0niT)w|1*cI~+6V{bk zyQ70nCfN5bl{B;skj_+atWEMxQt4_W_7WnIlN2)`nv+@;&!ZKFk_F(^b0x-a4jIKI zO}*C_KA<^Q#Pg55XX^{)mV^j*)1-k{*Hi%m&}6hA2_le!8n?cZMGbjHBI%6;zhMAJ zURzQAQ$%?(L!P|xF(K}&0WN?a>vF0eCt7~;$dBl>(Ob%~7|5VJufW{`4qdVTIdt`? zn{|d-)gSu2Isov&j~mT*j~}p(IZx7sXluMlbg*h#H%zsT=Xd2ehmA_|9Q;?G@E0Jo zKD)mp@czai3hKSvV+<17sRPz?2t7&-qzuDJh^c(G1L4JnA)P4f{6(*@Zk!ts9#=Ux zQuyU*0$!+*Nv>w==-AfJFZbk_lR@2<7S z0lrf9o57npV~OXTsk$pIxo)l`rRK_`KIWcy?-zIeQ2a_;$0SvNfdhXs@sM za@lv|DVN*N^{M&*Kbopf4Hv(ZDm>4N)^*Z)iNbvh zZyp2D^NRNVr0ynVQb0ap3DLf4vr8aQD29gk^7&*Qa{+)w0xM3GdE<_0NqoxaF7QPU&^dJC@VVsWy?K{B_g4 z8D6LuI=q_llrzS43r6Sj^d%Z-BOcD=V`!Tqm0G*EFsEr)dz`9EdzsK7jZ?;c_a9Ha zxgmbv#&0TmSBRJ-ZMYtMGQ_++mh)}4k!Ce(cf7Z~)8*xBUENjoe91P8nTzeaUc=Lb z;TSiC__{K5HV$*}l%5;!es$+ZT;-V738=q*v#QP2+UWVry^BFx*Mc1fdL|tg-1?ML zYscr&5`W(EW(cMC4VUNj`P5OXn0Lsq?n5xM&;jzJbC2}9RV>YM2*v)Uf3nSjnt1E* zAsMUV_gWe%4J#O3h@-^`z-u~bzjp+3iI1eWrMxQd8v?3VI1LjR$H0R&$Unn3ft5z$1_ z_m`&+1tfeSdjFd4p6=+J7W)jOKJunLA9SxSZy0eXX!rQ+-=Uz_F%83%aO|d06I%4x$rfmFeYrP=$3kH5NRgRKcYmK5< z@_`SUHUKI!fT_B)NqsOQ+&@%3AB?go55g=hUv=koS-4?K?F2ZoqPs|>B#SY(%33|4 zj$NXRSKhLhJ+0v9=Mh^o^XXi5pfc<>S=V7* zLtVhltb;4A)OEosT4ee7meRBoIYrci;f(UPb^VIpDge?;M7>ga7ocgolArEH(txoK zx95F1`YZFjK)nF@j~pzXBE4v!>IZiHB+cFyqtNJNpX1#w_@B9?RL?Iah+sA=P{}(l z(*ST?qFi+XjV!~a{FosW)t7kx zzh(q9u1dP65(uS&Jbi80)Bv=0ehq({L|D-YuhZ^E8Bq_TbVxhxdoM%?%$VW+XOjd<&Quj{DaD?yqDm_wiga{+-v z_&3THu6*eGi)bSvd)7Hi6JLjOj{tN5sjMHPb91UXn1AUW>p=tz|I~JjT;)>nTg6dp z-;)d<#B+8lhM~JpA2>+Iro`5R-p-;SV z-{HAXU>Hk`7Q0|M6Z-ZnUOsgcO9RF(oD%P+k;63Z5&uSLNOkK*wUpId>{&SAg|g}& z(>d;Xun3giju`|(>}QWe-e1OPF-wftI#AqQ?s2@_Z%t%-^EvG}Cf}#>9j=<6e}~Qv zq$s$DjOx*1qBAwzNzW%SzY!An;dLl~aQykT6o+M(KERcCPJ^|;nH@`ro%Vl&Npn2YUWI%h zX;eI>6nVdXpqL&#j*NQeU3LM2itwqJi;vOs2wBl~DaFF|ZkmX{rWu5a7N2f^nlCaM zfB14zuOe*1jY66(IA#^g3~jUnG6BU9YnsF6`;nNVwxJMDsY8*@`xczFmFmw*To?OT zq7TTx9EkJAdnMO?593Q@=^pC@aGWEF%TCF#pc>Kp-!r||aVJ0s6pLS-F? zQjx57uJD?f!cQHFQ8pnFRevvOlX}JM3g?R=B`Z<-)3TyzH}7R?re!lXpbh!fbzOsJ z{N}mE(`H18s%=n!w16GeaWRC^R4KbZ zxBbPAIh+ZMMG+CWxb7c=Ar4DP?ycEg;aURfu=L?_7ZycNZ=Yoct?n?pypTqmU~(d7 z{FeZNqO!Js|cbx)tc!1YwMpq5rPX*qt=-PXav%9 z{8s)mF1-`*F2j0xOIP^yn;4Da{onShr1{#Io^$>?gB za`(@-OdZWCyOo8sa`KlwsNgg)z^r{NxMJp^Gxe{3@MIP0&6dPx;iV(UD=<{J8|>j0 z@R{Cf9W*xsn8FD-l|RK3GFHwd?-;uzs5IItDzg5bvt0iL>a|{q^4+?qKd^S7V#JuL z=gM$pYDW2}Qxkr^`zl?ZHX#qgpU*UYA>}g(&?ZE$Krpex)>MmUv^?Zu`(si@?QzML z=IGz|655r}(t}hA)#zQ_j^*u{(G>4R?^izvTvp}18%hDTS$|-OU9NY(l10rJ(22c5 zPOw+3*c&0v3-5?(0o7nBe0f6h`@9#kzSDK?DNLdH=GUIR1Jb=VW2L$;aXMJ-HZq0N z`^esgEQqb*;BqO`eQkDGUDE^?>HYozT0y-7NQdP`$aF?}bLdNkzs8Jx%Q^^MQ6cCO z{nH&{D4N!gVf$0Zr(2`Nx|G3}Bz?hFW-Gu74$b(9vZ2>=nutVZ9^p&PeODT?C;k}| zViWIgUwxqctKdQgrdQGzTGnPiJoZMRzap7B5L!1K`KfEwKpiG<-W2zM^$$ISl6)QG zGFm7!8!0rcg(bN!Y#iNzKLR@Ddd7!GS7oNI2qt@pAg>hk!yQvY$`JE~HaPNxgKb=4 zZ!flJiMlZ75gIZSJ)!Xv7$!RY40K#X5KV{eQC5JK)NG*`}0HJ-gSE| zzJ16&2`QRByL`Y#xy3Oi`MeMwpINB@6R}y6zK)A;J7ICG z$q0{o30g6Ll;Mevp=+>>xUDDjlRr*Z_@iZ3yB@g8^IX2ZR@Q8(1h)yeyySJcNcwt2M@~<5 zr$KgeI7q(}QB8=WPmk!8f60#!t0igiOtq+jlAh>(eZ{WN8{EqU(+c8V314#Yp3?XN zKVgwLRJ44<+s4@@O&woS2(Z`a>HW`M11Ji^8&zN^=x0)yoT2LbSof)ofuTD7*(O6R zewp$H7Q4*|@coIx(*V5(tItL<19ci&z8^Pc_BEUdQY4-n6Ot8>(rAumsN42?Wf5kmb#;swU(xu;1vU{9a6|ZSLxlu(`Vuz z=?mU&je|T0k+p;|*g`vewFS#TU>StZKC#-*7zX(NS%bvSKjaaKkAljVlQ?PV_yRaJ z$kp-XT0(6rQ$2>Yo|Qa=@qw;!yA&CZhS0HW}DI1>7^OZa>OJgk+ zV8gWHS*i=gZ;+?SY_aNtl;%W#qI~6kqUZ-OWx>Cjzr0*7Zgh+zfr|f`j4myTrsnp+ zY8XtDdz>h;BBRgsuY+%XI?o2IVPBinIFJ1fKW=;kOx zI!0OA+2)o~VNv1XE=EP7l{124IG3{oRKMUp8G`WdAEKFqh`F$VR6`I-ygw)0k`iIm z6RFORPzu&tmhP7n)gX`TyVQ~sO_|o0KYWP*QHHz0gx6KTG@{kTVhZOS^1C)9k+3u{ zoPec@v#kr@2>ZfZHg)L$uKVI367%zD<60RPr&~|8CmhZel!HwlB_7@TGxuKh%Y00P z>)8(nUl3)<9q7>J)4g_N%zs`By4cWeq73S$XjdyVl1}rBTh^G9-B-aMKP_4^G`Dxw zMsci^sw0YON zTq~6J3_9*kH3OyEWn8mesM)Nk89NEKnlRuj6vBC6&@`N$mlQ^uQwu^siowB`#;N1X zxw*u@rl3InYs~^ba&|;O#)D@39Bq&_-_H z%fjALi_vs*L5iR-lFUQ?@ut-W>M;a+xPnu%RQdVEZ1pZ}I9@YR1a8i{xu(+Wim1V3 z*NfBrhu)%(DuOwq1kO{FCZ?=*9QYEX@YQSfN}x1QR~6BKNE0En5I&;EX}3LeF+gkk zr}kN=vRE)+m-DndWFe8G1Azn1ma=b|-zr@%C;GQ%=Sfk5q{iq4hk_Y>-@IunOmkp1 zulKgQ+p;lVEUVE0r81dcfLVN_lo>$ybtQ7c<0TWD9|nq7PS7}b&!?a639wZFcdI&n8h-(2Gb?$n z=5!L+tn?Jm;4 z;jESwtIzbNC~XYlCHPAB=R z^L35=L!UD72O_LS(HCcds!Q}QQzVu~>IUF~TxDyXZ?$pkc{`Q2++M`k1EFx-!!8bw zjp3=4*u6654@UPoPKJ)HbH@mDW!t>g*h6VzQ_VRm&zy`*GTL!n2K9E)onR|l8J_N+ z$L;f8+nW4d6uF%K6T5f>yH^0$Hst1K)nY-S`W&owU5N^o_udbNop>NAKdfq>4j#gPXz7L9>PODw@+BTAU zOneQ=>o`I&9<&zteH<-fc%IhXlfK5vV>T{a?6`CQ)kSUu3Ys6k<>1jlKwtK)>*!A zpfwuE!D@fF-A5awOg1}%0&z=3Ggrs4QMZIs#aFG-Vz>Cab^nK}vwVoEVYmL!4bmV| zB7$^DcL|DgsdTHPbk5KqsURVZDBa!CT}n!K56u8G%)A@#=RW5-??1qYVbAP!UDsN_ z^$yA-auUlA>^XnG&?CRXTy(G42LL=dge&Md| zo>40(tBA$)psyA!NG}&NGwz9;Fs=MId^5~IYxJoH_f0!_CJEEip4|2!+~>}$pM-pr z3c48%DK9t?4;J*Zj4Lnv^Lv2LZnyCMU7C2(boIYN2wcoBCCl?@8aWN(>5mY^rb)6D zmPFzl({Bn9pAv<6ztS>QZRyz0GvA?xtLgNJ&^ggJinyY+@@SLd*f5lMWrM8wZZ0hs zAs@XCK51-8b;iC*jCgY1_t;r8>bdlZ;NMX3CeYu0UU^K?#4d;}#HnVp@RT-IX3y}P zQD*-Lr>xPIFi&eIM`YFbB*`>IQn6C=*|dFOr%hGHYeGjFp88ckS3p}`p?^7$Dv zP0Q0AcX#1(smn=Yf#ZPu+|8x%9~|3gp{Y-3!0BEGC(fz~TX$D3`!sVdkOI|HZ8UVy z+cdPQwTbJBqa}DnpxrbuEu z>Y!RP4C1_Rmft&8af?q5;w@#Ch0Gx*7uc)+Qv9BjXr71{UD-f3$Y>tg22qR>@;hFc zyKqJ`T8|}rjfr9&Y?%noFQkBTv2-0kMNm7sh1F@zaR5;8j}=N!yU^qrE5F)WWM2P& z-AJPU-bj+}ZX}bR(|cU1?@|YB|4AJn^{Eq?zo!!A5;7a|cYVtqat8%I*E4*i%Nz*c zl%;zRZwS})C=sLnQjR;_!Vx#XYt$OPxY>MpddDPhI!qC^SGS3O`mpR~M zhThC=uF1Oh&%D+%>di)i@}tZ4O-rU?4$Ve5dEp$T75$Y<*MjO|lz-~qVX!fpemPA0 zY$R%cmOIEOR(Ypro*1^ac(Llvxv=RBeR*a3pE zkR}N4{gq(xU*Kdb^nz}+xc+E-osXZtDQZzHvm#?6B|2>#KTZdIq?w)?oP>)FV3mS$ zI5xULcNY&qsXK}MO$)C=BVFtqJUi(dAB1De(hUafJ_bhCvs zeqiVFGW$`vEk+QN^?|npAeKn-h2>w2C2_yGBV?_{U4Uu)kFtBx(Y9Kh_OPE8W%MFT z9x<-yLyTrqgg5y*C9A!$Reo$YR+A=?E{vYgX-N0I9{9ZP-4$ zp)LKH{V1qc0;BtZXZIBP&;lLZYdn6&eoC73y;n|I;w0=f3V2Z%mbeSM<=l&Pm)P_y^VOMbG|TteD0`O;0?L4wX@4cDBX`{4}S=6w+|A z3vqDado;{pMmK>X0oF#G(ubE!T^^4Xj@Yd$65XvPNz(&uXG#770n;$Q?uAuP;5vvO zNK~WUSd8z4wafhv1(4*YCdN~=87%#jtFi0yM+&sSy<~`VkTtEOB zz{U^|efs42jFIz{H6OsZa}Il#cC+ZwC+p=VcO@Z#wo1J%gC}7yH#f<$nwo3^(HoN? z0OBsfbw9v4B+}P3_l=i=GJic1r|`yLm9+dJ--GWwrNo_MIse`~t+d|enmCQ>`xq#a zrt|(f;XR!SU)!k{nuQihch7O(+xH^ApXO4f?RhyHodKGK`~$*0q^}Vj(_=Ebv%S~$ zTA3HIU6YNEI3kmEp4RLf_cH)J4Hno*Q17C^AML^4K8|P}+#}Phyh??7zk`@I{ldhk zKk$Co_l2h#2hiSl$tFJ#|6AA~y;j)zwrk67`YUq5dw33-0wqfa(XMx%REugX-QZ-@COP8amZ zayi8u$9F&@%EO_7_TO+aml<DwF;&ZjQd$hGc)m{$C>$E6@-jzVj%O!T7}c zh~9E_cu(>$KH%1Y{-8yN(o)4^+7|Z_gr@H^fEhNbiQIn3E`8>Q=f6j=8jNAgMSHQY%J>W$|?2Y#{((Wj`bm` zxme^1OaU|CEw&2Z?2^wBySu>?b}Eh@}i1 zR9GtGM)B||NdsLKa&f!=3f%911unm?-40IoH=?Wkc_WM&QYWMDtHr#Kb0v2yyzVj=Vtj=LwF5@6N z*g$rop+AT7{%c3tixplcu5;{sbs({Fjv0;#*e675&BD=&WmTVn7SsmYOM0{4;d%os z8xb@1ii3Gq(>nsR`W*opR}(8+#qkk@YDNjM(eY(pd{aKx3*s_U6uv6!EtCaYc{z$X5?NVxm_t{oe`E%B6kc+1G{^0R=MnW`kc0GL(67~)F4Rnl2t;3H8c2#_@-C5UK zgfJ8{2-2s`7jd?QWgo76XU1o?ZEXTX=OVfFv@527qs}B0A87BEQ^K1TFqC9`!TNEn zBwn)pp~T8H{m%oUHJ67Jb})rPgn6;>Gawe#QI0SB9`qUjS@B6|OfgkFly&@wZcXI( zbub!`p6-`87t(@)@`hHQo}B+4PaQ_uY5iMt(ZhQee-*G~3d2t@_MD^4%z|Owd>1wv zFIsf%&YwLaWu&fjh=vZucEyupeLyP9uiv?FQK!ss!cmZ9~}Uyi%_EMs?^5 z$WW&hcHKn)EIQSh_zNNtQcfgG;L%WtPoXPkODp1ot@4${OI1}Ai7Wjjz@ayjDj2BB z`20|VHS(2_h&Q$kPr&DahW^_YDk}>O4Ob~?Yg?a!(W{l>hU^c{kWh9#^G}+gn7vuE z-bBX@NS&Z|i+kloE6Hf^1VM&yjLSuht)$uV#hSO|>66)yOHgMTzp9&Sn$gPI4nF*< zRW&*P2~1Nfl`PStNYV9;JwEU54AlVrwXsoPCw9$t5c*pmZSD9p5Pmp@jk=@CVZ=!f z*R@S{>Y^RdDt5fgsow7f-XfeiLS_nYRmK9wYXVrFj1L$c41*e1ijvUkQ{+LUa$nJ2 z2{O|Un+f=5a&&x-GkdP1K-!blkLC#QZ20VV|LDs8Oh;wLg8}5=Hzv!6)A$4mWlyN~#4xNy*mOT6O|4 z5)jJ`VG!U>O5+k_)&QR^*5_P5O->j|d7VE6T*sAP-M$X&id*X>Edl^ zq@tlDckWrfw;=Gp+U+}D7<#`_3PWNX`JXc*nxF=TFu2ok-_1o1#(twn!2*6f0o{8I zIW-_+4JgR2dRkwJxl$tSTjQkPJW??)D8`0wqZ968$w18NKY*s121Kt zYH=2XFTkdZ4H()AjG$k~J1On9@f)9daXyYke9Bu&iw8LEe{RHZxJYnbYf;*pJO~>` zuqT`_UlIaa;P<+QR$&W6f4=?jm^=nG0K^b!Dwiy3mjg06nir{{M4s$~rmQF>7xZj= z(K|;==0Ae+YkLXS&r=7voxd?ur!XA-z|TNtMgfWq2+CF~?`)=ms(RYRlhaAEz}L&ebVG0yPTi?j+db9!?22=vw#V_ z`)`Xujqi_f3yi-#rbDs;5DI961ifIeRp5&5%P9c6GdJHq0|2!*L}~_!E2+c(=){Jf zuatw{{;9KmEF1Y~5=aEier~ByFb|*?I8NlrduKPX`bny8E_kx`UIn}m`)dIH*Z+ka zzC*ke8>RX$O9~lJtJ?L^-W@88aX>GHeH5BKxQcADIm|OcJE4sj*vXptobGCPT zslR>{jleF)>f7XZ7_xbMS{#~B?g{RA-&6UQJ6#|s(Y-%Bd^|34MER&~aVOtF6R`9@ zVS>P@^v+M6tM7{+Qnmlbi^5$fTj+CMxiUOlaqVe&1g&t}JPk7Ft`I>5rUGIn{5AYl zdkLJfNJKoJ>FmW9(+@E2h}AR{tdDWrY4VQIL^kZPH{Qi00ABlZFu*)_F0Evc9e%%mroJ+Epgne27b zjcK%)CNT|5lWdw!1UCYJ{i{a6f+5G+1wTRWq_+~BVn8(?P?Z7x|3_6`eY>;Nr!ur5 zY#E%Xz6I-bw?u)za=?=4PPMT#mu`PR&7vsTs`{u7Ae@N}dZHi7>2Jk2SD%zwFv%Cv z;6-h)x-Xd!gaeQpJj`!cYJiHY2Iu^Uk3q*Z5ZJuN|4XMH=j5q?gaQzV1%Go|=H7=Y z*4m{Hfe;RQmvN^-ATtAXDda^G&}B6ajsKRO@~jqnZG7o2{oGQu@z(hd+uTj@M2)l` zKnT_@pJ{(V(y-I=LmSrZeF8Y+f&quzjgQO@?VtPy29!Ty()w@kM!Ayej2$7{Gi{hP zKb4(>jk=rPKzLS95S}ehfuIFAUia zg0g(}g%1_oyc99;B%G?{l%^2oPxN+OD(*(8&lhf;eu1{1O{Y?|qegPWx5bk& z69O7#1i-_99*E;wVK|gO#qAjJXmR)h&{Di6|sT1F47AO}P*0-PKXX$=- z`s9R{z()zA8ot?rx9W9F0}zS6<_G?;0$!$tx$P~e^DY+k{WrXJIRKKPqh>D7PKG53{~quU z!#(2yj%0>Wc~z7&G4E$wZEu`!rI80Z1v6EtC$vl!K^Ug;3SO83nfUPlWkbLri9_f# zTi!M#KJazZcrXOmjn?$&?5GypoP9w0D@Lgn(lVvu6PAC=KbqaT3x3i(ZPT+D>INXM zL?5TY&(O#O&RCo@bUbTlJu51ijb^WBJ%5fI#tQ<0_mhHIa(oF7C!%lcX|He-j+?fD zqSJp|?kx6h?F`MPl;V#ZvE6E2z=4y79FIlKe&X5M*A3V+1_$pOq+^zp0T(&AtbDiQg)% zBm?<71}bryA%?F6Ku#7kzYhkY<&MX`zk6c>1!cNp#3g{OYbk%!d+h$za0EG(dd7e< z2Azmwh~>L1>^F7x=Dy0>1bxC7j}(~;p!M^B>|AgHG8+RcMtZ@P_vs}}GZFqASZ3J9 zukdp$;tHfO!kr0N>ip1r1Vhs*`(|_}J`n~dWfr0RMO9n=7pnTRt7IYm)E-0FEA*cp zKP%7fyXltH1Lfy74h*cLr9d(jef&No31A~~LGs_->%M2!g%!s^U`g>d?5=C11N}kM z_Q)`9hfASz=#%sOImQ=HQOfHre_7R7P(Eh@LF@u-ahq{EzLm76_UOS)FaeO@*^6_= zrxXl62U_L|YBcCH$A&n*8e>34?yxfdI$5A%t#ErP5O~XoeaHfQ@Nj6YNQswQU|>DI zsm1v0Dxdo_Aw#_4k1~ z`t(?DZusTzwwS}3Re!gbCp{Sd=^_~ovP=elvkg(<nnYsPpaIk%Rn5R}_~Jtiz>FnC8Az**pWtOg7Aq5^@!d3Sy32NwHoKcG^EV^CY28WAWYXw10Jp8-R&&v(oG{KxzbQ;z+|Y4mH?zlMmVx^TIPw*lECU# zHzqAu`T~Gu$?A1|^vU|C>J9O-$A$r}L;-$?)1Wu;Ry!qWx(faO$DcU$e%FA~stk7? zT4IIL&XS_dF?Dug>1!G_nFJMS*w+!;Q^BqQL^v8?J!ts&g|AD&9HT!Lk_K21fN&?( zZy?;6LPmt>X6LF>;m;%_ss%;ldwm<2$$y+cOfyVZ<01xnYOL!jK{v5MOUyi=7l zxP#N+mwcBd2F45cO+Y^84&*F@IA=)@KsNSH$9+eCB!Z#XB^l)k=uDiZu0Gmoo*DV! zvzaaq4guI^+0k+>j****j%5QG_1BXt`yD}qhX`|*ZjeHT;8@s6a9Cx_Sz;1YO^0Vu*UsKr1BL^TTt2$ z=tuEN9ubrV)|QmHyo(AUkRrEnKI#E@p1ysY&cAZAdV*D7_fbJMEhm>fNj51kl^b?{ z?*}-uxSfWBkCSq$k_z`5gfRbMW8by3%cg2zLF~r9ceM#}0!6ablE%ucSOH4$hoFF18;FqwGk0pIjPUA-0kA;`?Y&obb?)*}1G z=yopIBt$r)JfUT%d0`+VOYm*e>8+6nF|7c#A4O-9_Gs|sMSevoWPvF|>(#q3v zyCgAQ<1iHT@k;?M<7ikG^-o^Tv31~Jl=s%T2RIX_o~`i%K*ry^k^~OT9zq}MZ))Fl zr63hz%63f;hMd~MUQ-JTG$Dx$OfW=h-rBm2ELeD|H&XYiXF|$Gq%f>T{}KVE+lYAV z`=v@{xd7;|H}W3FO{AM)&O5vRRL_1rT~FX8)xWb5;Nlg`-Yq!qbmxcXPmiC|9uy!n z=CS_2PP&ru(I`DGIifjcU>zsq3w#<)2v1VkKZ3xnNcJDd36{v+8u- zur%|d=A?(?Ro$`+UAr07w%-^Wh#?^^;*dqw~L{E>EP^Du`LcE zMIq_4Uj)v2+xs!*BEVaHk(LRJ;cNi;i%dRms#BIMO>Zcy>QX=M?A*Lt23uZ{Q8#Tg z760^xsj-&(fF2p(A%5;OuTGL{n0qh3aXP}OrXG1l|J93AmM=Cxix(*1V5U>N)gd>t z+}+5fu~oo0@SwBfLO71){y5MWUel`4_yi*Pd-kuU{B(VR5$8|Ifkyd+0jvl#qDYjm zJE^2D#c`dKOC4~vL~?kZITybc4g1?)N9{RDZ_g!12rM#lf;e_cewx{x=&ts+dbqU_ zKJ}YY=}U6I>$ttr++BVg$Yh~M(Dct1li8OW^!Ag%#J@$iz`<%7ZwPlaVVx9H?Y}So ztV2P7J-MLQ=Hy+%l_adB+WY__@mZ zx3fj7;2m|ahT^CI*J!#egU?N9QoIzqYu+ZwZ*ML?=ayT<7Gnwdk+_yB1+X0NpocW0 zhkX0mdA;=v+Qz6ZdSkdaM$!siJ^kMMy5X>WETH0GwXs6)`*W@%U)Atpv%|FVoXavw zVDls^nxkOh2b@FCQ-1%PcdEzGmD(}{e9^6DGsYf)^mX&ZcMDepVyOIh7c3Jjvh0ro zG>}ltol+}Re4+Kv#WI`3uwG>w&LkV#9xe}6VX!ac2Z;s(H{GgJ$B&Al9Xo}F&VXg{G-BFV~ z0JToSefFb^(m>(ak4tfmtV2RKPweJ2gc$8PaA>ywFc4I`S2!N~cmOi>C-%C|STP0wy%D_M z5mArcy|DeG7CrA&|4Yu>;9g*PV=21T|Lvj3&%1DW%hlfN6lplAVe6JRwU@AgjTv6X z8sWWz*$1_W`Cqu|WuA&`{H2SVoG<&+)8+xo*f$ni&y22P9Wv1@24#3O>}_}J{}u)> zYe7PW5&g~Elk^U~^!BI9ulkiA0BwnL7?dnrC#<6EfMRyx`7f*TeT`nir`=&+{F1*E zv7^2cC0EM60R%I>4k1Xu8q%NeU&X;27(7~NDP9{)%Kd0}nNTe>=MtR4&iAyGAw}q# z8@AZWd8gqce@v)a#)gg2eGiZ%*mi@wZ{L0UIY)yt#?1Jy7rpKx_sP&}EEa8`!SC!G?v3MP*RE+zJ3xCR@qZ+It)VbsaayzvJ$9OTpyn`nMUb zy|0{SR!CD8;e9e0d^1Yod&(e z7{4oxnR6AV*l^a&mC>W>W&cI^w>m}w1G8D?uR(c*xv*pdfUms0>;vjbCno>F9da^; z@{C3Y>guLY*z`s-N}{J{=MlAsZ%O0dJ_{vr{bAuW58#u4aKAk34=RbOil)QZK)&}K zmTjNrO{D#3+Resj7LPm%OpA#yE-PIujy0_0L#-tztdUYx*qs2 z;Eeq(r}tqj1X=R{r^~$Neg9P0XO$IpeT#h?)J(P3cV}}1wYXMhnO@=U`((F zJ*ENWr80ytL|9>sSoiUFGhSfWKa6^t=JrZKd=?^7y=g%#_s^DE!2=%mXtR*){uyi* zQ;jenqew13qMOb90yPIG-tmnsLNX8VVyY-HCEoLS#Psl(I*&l*>BA5kcDcNdIQJes z3L%qM-BG%y`B58F=_4crM8>3Y^iU~&Nr8=0r4!ZSe{={}o*o)r-Ta^55JN9QdBFpVC@m6q z8N^B4isxZ7Od3i`a>ZGH71M|mwt+mtJ&ZGVP3!J>3ZZhlD1&Y z>z~TijtXW(MfeU$Xm~K*p0j-gO*#csXPuTqHuC>q8G*M|WwpnPYa-JqSufT5n|*CfFXZAs1~pWhKv6Q{#NAnr}-tws$2@ z#56q11J>KAQt^IilTYO+Zh~0UpZ5w+R>=s?igDN-3zEzERO9P|DIW*mzXN6<4(;qjt#6bZs_2@@yuYs%YAlKN8@Py;{CTSC z;j^koTKfZW7UwmLx8sXf&SJK6rA|dMp6J(gaVS{+f)sKO$=Qh}U{`?g^WClZOiPmQ z2!Y8*M979Ol^*_>*Tk|g@zvR=RQb#wJDoB{zi~O}WqnJA#3>JW>vG<9c>~sKUy+?f zw|F9a$g)USu!;KP-756VFQ93T$ngM)d^;B7?VjYTWqXr5HLeq9Tw^>4hYgwD4&JUs z|4vJWwvV+r&XgH@gCVX^BDU5ZshjTguA>VI*(CgUC6LvHNn!U~K0~o7+J`IuBe6eK zaq>`ZCqy_jlubISDWZo6WFEzCEy|uliX|c#rP|vlMPNA{QL6ZBURcC_Si`q9}kMmXm$dP>Xmmaz*px zWRj&)^(cLm@l2u}M*4gdlqw1GWgJgoBb_12=6XiV?#t=Xidzh)*h1kZq7Ts=+hGrW zjsGQ})liyhW#gr}ktZ)*H^IxoST_sV&Yk&eSQjJB6SB&3>4i7_h(nVd1DuZr{>5o} zROZz&pM(xh%btW+}Q@jW}1^d_f{#ll)zX(@p zwOchq&d#g5Q2*JN*M9REj@Yo3ac8`q>{~QwKXHeh{nqfLUT__=$?V$Xaed`ERs3L$ z2Rz=f!SGrHfuNT0oRK=9TA#;QThD%B`ll6cx}19_ z(1ESt*o>Lhvs+oE=Y5QmYH^w15{3%%sjX5y_2*S(pR~`1y8HL8rH^koaJLbM};y=9`qFFX<%DH;)1H${C-^ zE7I%8QPA7Eom!^Ta$C-oL4kWkrd&}5&3`1K=R*|^iR-V|bNgjhug5&fuj*%_QP`hl zgA)nxVcH%aXj-oswHjX9W7oxGqPZ5`>)5zaC4!StV(JOP%I?vk7HvAJs#vT4mQb zh;M*oT>uj?@K>6WCD)y5H5U(jk=NVJ`^(5AD4PtN_QCOIF{Yl(WhIU5B0kos;w*;F z3)3eansT=~fZqO+XwxV*!uWdIvyqmW4(Kcwlxt!>&8Cm~(`*$QijqddY7$?HOA^(- z#;?vhu8GUNwq!BwutI%T%~+O#?2mzRUZ0cDV2-QZYgaIEuizKc`iRBdJS!gDq}CG_ zL(8m&uIg41ND2D_)*EMOePZx>7_C*(@B{PKkPa)Zdak^}5Crz3?1XIJcj2TA>Oumh zhJ=jL{*|v{!+pHVjywQg|5D~}7%W8z@l4l+|x~!+YzQLSY{;-X+ zFnc>xGvbyQq^a1+9m~pU_$p5h;%(BUdWsHRlj*Jlb8U(jXWCJnSQ4(6VtbyKH3LTRJ1W9%F4 z$Z}W_gL3=75XiO!gE04=Eaho1yr$(jp?nn36KSG^r&K|@Z-@0f7+@0|T)I#CGZvM+G z(9PI@W79PBw`y4ZYvHDA7Sx_j;EVl5i{7yX6!K68xhS>uRwGyDw+^V0L$=NVyMLfO zptbqeX1VM-%Sr4^`{4+5f=#-zo}g&;_N>`DhSA*gvG_`O{zDh;*o^*7i~OFXk{c_m z(SDF2M^nQ}+4i#HTW#ct6_V-J^z}BZOdcx(tx8KvVEVUf!FIm#QHO4y1m#JrF|S7K zRsh-{V^VG74=-v!d1H(d%GT#}h6peTJyFTnY~)hY$|DTj0?N!sk+N9{sie8`_}V*J z9}w=p5EAN?^AHmXe-4C#V^MUVs%}bma=}q0912Z15}i71e6K;)d0)iu;CGfA%x4E9 zX!o(vp+{&i@3Ls%i`&-i;3H2G@vYU7{aOS$tQr0ZT7V-}Gs*VV=g0!P+qS(S1&#Cy zqo?Z&FIWjyXV5k}S)U^)d$WBtzr5!GrHwn3hZ$6evQz>r+#-ZK{Z15Su0vW6c2*$cEn zOYi5rAWAj3syDg)NY)V!=W}6B-KJO$paHY>BZZrC#5I1x6=L-X|HpylcWJ%}1Zm&I z11j$&63SwbIzA6fz!zWWH2i_idcY(ifPv{w+Vfo0p8)&2=@8O@;wG3-8V58i4(TQv znq$}_9Ny$qn4w372naNN#C`WT{QSlaJ6t6!^_^t!wVQNH))r;#>7!3=83=h@#}??Us$up~5dOduR4|-Cq6(@nHSKh>Y`W z#<-l%g8%-U>AN&!vSD)dG1+~&_9~MU<4@KJzt3S(Y#@QLGcRm(rfq=U>*U3w+i5XL z#CV_7U_mpdc$cUYxx4IFRgIy$+acBfGP-Yf`K;_ z7W>OGk%T?PGbouCSp7P&Z(hF)=y*I~Zms5@oP|avjRvjkj(bd*C4Y1tT|`oa39%>G$F#+Tnr{8#Z3inJ^X` zpJ)!XDg3Bzs-N2Q;UCDR)s{l{64L*qd`MRNXru|M$M>yf1>X?fV*+7~oP&IrI`PG+ zx(Z>gMChdO(R_$i0uNtJ$K$B2f$<9dE`=VMGE4941f-)W(u2;E@&u=|$gj$#TPrYlt!cfUT6NrvnA-vb=XIOkR zmT_uc|9*z&i4YF#l`iqpIx~#Abkgo3Tt<5+Yc=NsL<{HDDz)8QR_eiTMFK3TWtgp4 z&mbIX*ie=Zvhg4sJ>S`MARI3E4(P1?cSv3(cq8B0F!I7^H{0#LwM#R{bq=0IsVL#2 z#6w)?Ytq9YLFsYa`mYeJ9y2l$9Ym!VMxM9c5}CrMgygR_*<5wA?u^OjXd&|1ta3m< z8bh8N1*lC*ga}T|0@iJN6#3CxI{LFlN?eT(WQ~$@lT(fCp~ju~9()`3Q-ShH@ROWC ziiwb{p{kVv812@w8g8^;FH&&ZegVR(Hp19hdLXD0gP>x_%=PdcJ<+SvMg;_u}0o~s~L zPMMM*n7Ty@s(a#qW^R6aniJmFa`EITu3}Ms;CkBRb%tq9(C8JrfjQYSx|fa|Ii4do zij4Hs?W9OKTwg?eJMK`bUBot1fwytY$%lyI2Q_j>OSVe}5YJi~9mupAG`>T8N+AsE zmnv^l*>{G4ZU7*+sB})k(J>>**K>7{cP;Yr!gb=kD%ZR+BXy^=0zb2KBtFI9&u!Y{ z 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%0DJ(ur0sI!V@tF3Ei?bvB=suU30F+N5eEDDRoeg+ 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

    B*);OwK6AqPZkK^8I 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 z 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�-?&ZIfpW|k_3*@{jMj^#uKnx>a(A#hw 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

    (S-^oVT&oB@gjj5SKLOhWFFJzKZp!qAag8ld| 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%0AOl3# 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

    $`oCC`PJw1I8)G(Ccb$S$%L7vx7Y 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

lg=Pt$otzbwGjABRyyFj}5h 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