1 /* <lambda>null2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.jetpackcamera.settings 17 18 import android.Manifest 19 import android.util.Log 20 import androidx.lifecycle.ViewModel 21 import androidx.lifecycle.viewModelScope 22 import com.google.accompanist.permissions.ExperimentalPermissionsApi 23 import com.google.accompanist.permissions.MultiplePermissionsState 24 import com.google.accompanist.permissions.isGranted 25 import com.google.jetpackcamera.settings.DisabledRationale.DeviceUnsupportedRationale 26 import com.google.jetpackcamera.settings.DisabledRationale.FpsUnsupportedRationale 27 import com.google.jetpackcamera.settings.DisabledRationale.StabilizationUnsupportedRationale 28 import com.google.jetpackcamera.settings.model.AspectRatio 29 import com.google.jetpackcamera.settings.model.CameraAppSettings 30 import com.google.jetpackcamera.settings.model.CameraConstraints 31 import com.google.jetpackcamera.settings.model.CameraConstraints.Companion.FPS_15 32 import com.google.jetpackcamera.settings.model.CameraConstraints.Companion.FPS_30 33 import com.google.jetpackcamera.settings.model.CameraConstraints.Companion.FPS_60 34 import com.google.jetpackcamera.settings.model.CameraConstraints.Companion.FPS_AUTO 35 import com.google.jetpackcamera.settings.model.DarkMode 36 import com.google.jetpackcamera.settings.model.DynamicRange 37 import com.google.jetpackcamera.settings.model.FlashMode 38 import com.google.jetpackcamera.settings.model.LensFacing 39 import com.google.jetpackcamera.settings.model.StabilizationMode 40 import com.google.jetpackcamera.settings.model.StreamConfig 41 import com.google.jetpackcamera.settings.model.SystemConstraints 42 import com.google.jetpackcamera.settings.model.VideoQuality 43 import com.google.jetpackcamera.settings.model.forCurrentLens 44 import com.google.jetpackcamera.settings.model.forDevice 45 import dagger.hilt.android.lifecycle.HiltViewModel 46 import javax.inject.Inject 47 import kotlinx.coroutines.flow.MutableStateFlow 48 import kotlinx.coroutines.flow.SharingStarted 49 import kotlinx.coroutines.flow.StateFlow 50 import kotlinx.coroutines.flow.combine 51 import kotlinx.coroutines.flow.filterNotNull 52 import kotlinx.coroutines.flow.stateIn 53 import kotlinx.coroutines.flow.update 54 import kotlinx.coroutines.launch 55 56 private const val TAG = "SettingsViewModel" 57 private val fpsOptions = setOf(FPS_15, FPS_30, FPS_60) 58 59 /** 60 * [ViewModel] for [SettingsScreen]. 61 */ 62 @HiltViewModel 63 class SettingsViewModel @Inject constructor( 64 private val settingsRepository: SettingsRepository, 65 constraintsRepository: ConstraintsRepository 66 ) : ViewModel() { 67 private var grantedPermissions = MutableStateFlow<Set<String>>(emptySet()) 68 69 val settingsUiState: StateFlow<SettingsUiState> = 70 combine( 71 settingsRepository.defaultCameraAppSettings, 72 constraintsRepository.systemConstraints.filterNotNull(), 73 grantedPermissions 74 ) { updatedSettings, constraints, grantedPerms -> 75 updatedSettings.videoQuality 76 SettingsUiState.Enabled( 77 aspectRatioUiState = AspectRatioUiState.Enabled(updatedSettings.aspectRatio), 78 streamConfigUiState = StreamConfigUiState.Enabled(updatedSettings.streamConfig), 79 maxVideoDurationUiState = MaxVideoDurationUiState.Enabled( 80 updatedSettings.maxVideoDurationMillis 81 ), 82 flashUiState = getFlashUiState(updatedSettings, constraints), 83 darkModeUiState = DarkModeUiState.Enabled(updatedSettings.darkMode), 84 audioUiState = getAudioUiState( 85 updatedSettings.audioEnabled, 86 grantedPerms.contains(Manifest.permission.RECORD_AUDIO) 87 ), 88 fpsUiState = getFpsUiState(constraints, updatedSettings), 89 lensFlipUiState = getLensFlipUiState(constraints, updatedSettings), 90 stabilizationUiState = getStabilizationUiState(constraints, updatedSettings), 91 videoQualityUiState = getVideoQualityUiState(constraints, updatedSettings) 92 ) 93 }.stateIn( 94 scope = viewModelScope, 95 started = SharingStarted.WhileSubscribed(5_000), 96 initialValue = SettingsUiState.Disabled 97 ) 98 99 // //////////////////////////////////////////////////////////// 100 // 101 // Get UiStates for components 102 // 103 // //////////////////////////////////////////////////////////// 104 105 private fun getFlashUiState( 106 cameraAppSettings: CameraAppSettings, 107 constraints: SystemConstraints 108 ): FlashUiState { 109 val currentSupportedFlashModes = 110 constraints.forCurrentLens(cameraAppSettings)?.supportedFlashModes ?: emptySet() 111 112 check(currentSupportedFlashModes.isNotEmpty()) { 113 "No flash modes supported. Should at least support OFF." 114 } 115 val deviceSupportedFlashModes: Set<FlashMode> = constraints.forDevice( 116 CameraConstraints::supportedFlashModes 117 ) 118 // disable entire setting when:git status 119 // device only supports off... device unsupported rationale 120 // lens only supports off... lens unsupported rationale 121 if (deviceSupportedFlashModes == setOf(FlashMode.OFF)) { 122 return FlashUiState.Disabled( 123 DeviceUnsupportedRationale(R.string.flash_rationale_prefix) 124 ) 125 } else if (deviceSupportedFlashModes == setOf(FlashMode.OFF)) { 126 return FlashUiState.Disabled( 127 getLensUnsupportedRationale( 128 cameraAppSettings.cameraLensFacing, 129 R.string.flash_rationale_prefix 130 ) 131 ) 132 } 133 134 // if options besides off are available for this lens... 135 val onSelectableState = if (currentSupportedFlashModes.contains(FlashMode.ON)) { 136 SingleSelectableState.Selectable 137 } else if (deviceSupportedFlashModes.contains(FlashMode.ON)) { 138 SingleSelectableState.Disabled( 139 getLensUnsupportedRationale( 140 cameraAppSettings.cameraLensFacing, 141 affectedSettingNameResId = R.string.flash_on_rationale_prefix 142 ) 143 ) 144 } else { 145 SingleSelectableState.Disabled( 146 DeviceUnsupportedRationale(R.string.flash_on_rationale_prefix) 147 ) 148 } 149 150 val autoSelectableState = if (currentSupportedFlashModes.contains(FlashMode.AUTO)) { 151 SingleSelectableState.Selectable 152 } else if (deviceSupportedFlashModes.contains(FlashMode.AUTO)) { 153 SingleSelectableState.Disabled( 154 getLensUnsupportedRationale( 155 cameraAppSettings.cameraLensFacing, 156 affectedSettingNameResId = R.string.flash_auto_rationale_prefix 157 ) 158 ) 159 } else { 160 SingleSelectableState.Disabled( 161 DeviceUnsupportedRationale(R.string.flash_auto_rationale_prefix) 162 ) 163 } 164 165 // check if llb constraints: 166 // llb must be supported by device 167 val llbSelectableState = 168 if (!currentSupportedFlashModes.contains(FlashMode.LOW_LIGHT_BOOST)) { 169 SingleSelectableState.Disabled( 170 DeviceUnsupportedRationale(R.string.flash_llb_rationale_prefix) 171 ) 172 } // llb unsupported above 30fps 173 else if (cameraAppSettings.targetFrameRate > FPS_30) { 174 SingleSelectableState.Disabled( 175 FpsUnsupportedRationale( 176 R.string.flash_llb_rationale_prefix, 177 cameraAppSettings.targetFrameRate 178 ) 179 ) 180 } else { 181 SingleSelectableState.Selectable 182 } 183 184 return FlashUiState.Enabled( 185 currentFlashMode = cameraAppSettings.flashMode, 186 onSelectableState = onSelectableState, 187 autoSelectableState = autoSelectableState, 188 lowLightSelectableState = llbSelectableState 189 ) 190 } 191 192 private fun getAudioUiState(isAudioEnabled: Boolean, permissionGranted: Boolean): AudioUiState = 193 if (permissionGranted) { 194 if (isAudioEnabled) { 195 AudioUiState.Enabled.On() 196 } else { 197 AudioUiState.Enabled.Mute() 198 } 199 } else { 200 AudioUiState.Disabled( 201 DisabledRationale 202 .PermissionRecordAudioNotGrantedRationale( 203 R.string.mute_audio_rationale_prefix 204 ) 205 ) 206 } 207 208 @OptIn(ExperimentalPermissionsApi::class) 209 fun setGrantedPermissions(multiplePermissionsState: MultiplePermissionsState) { 210 val permissions = mutableSetOf<String>() 211 for (permissionState in multiplePermissionsState.permissions) { 212 if (permissionState.status.isGranted) { 213 permissions.add(permissionState.permission) 214 } 215 } 216 grantedPermissions.update { 217 permissions 218 } 219 } 220 221 fun setGrantedPermissions(permissions: MutableSet<String>) { 222 grantedPermissions.update { permissions } 223 } 224 225 private fun getStabilizationUiState( 226 systemConstraints: SystemConstraints, 227 cameraAppSettings: CameraAppSettings 228 ): StabilizationUiState { 229 val deviceStabilizations: Set<StabilizationMode> = 230 systemConstraints 231 .perLensConstraints.values 232 .asSequence() 233 .flatMap { it.supportedStabilizationModes } 234 .toSet() 235 236 fun supportsStabilization(stabilizationModes: Collection<StabilizationMode>): Boolean = 237 stabilizationModes.isNotEmpty() && 238 stabilizationModes.toSet() != setOf(StabilizationMode.OFF) 239 240 // if no lens supports stabilization 241 if (!supportsStabilization(deviceStabilizations)) { 242 return StabilizationUiState.Disabled( 243 DeviceUnsupportedRationale( 244 R.string.stabilization_rationale_prefix 245 ) 246 ) 247 } 248 249 // if a lens supports any stabilization but it isn't the current 250 val currentLensConstraints = checkNotNull( 251 systemConstraints.forCurrentLens(cameraAppSettings) 252 ) { 253 "Lens constraints for ${cameraAppSettings.cameraLensFacing} not available." 254 } 255 256 with(currentLensConstraints) { 257 supportedStabilizationModes.let { 258 if (!supportsStabilization(it)) { 259 return StabilizationUiState.Disabled( 260 getLensUnsupportedRationale( 261 cameraAppSettings.cameraLensFacing, 262 R.string.stabilization_rationale_prefix 263 ) 264 ) 265 } 266 } 267 268 // if fps is too high for any stabilization 269 val maxCommonUnsupportedFps = currentLensConstraints.unsupportedStabilizationFpsMap 270 .asSequence() 271 .filter { 272 it.key != StabilizationMode.AUTO && 273 it.key != StabilizationMode.OFF && 274 it.key in currentLensConstraints.supportedStabilizationModes 275 } 276 .map { it.value } 277 .reduceOrNull { acc, additionalUnsupported -> additionalUnsupported intersect acc } 278 ?.maxOrNull() 279 280 if (maxCommonUnsupportedFps != null && 281 maxCommonUnsupportedFps <= cameraAppSettings.targetFrameRate 282 ) { 283 return StabilizationUiState.Disabled( 284 FpsUnsupportedRationale( 285 R.string.stabilization_rationale_prefix, 286 maxCommonUnsupportedFps 287 ) 288 ) 289 } 290 291 return StabilizationUiState.Enabled( 292 currentStabilizationMode = cameraAppSettings.stabilizationMode, 293 stabilizationAutoState = getSingleStabilizationState( 294 stabilizationMode = StabilizationMode.AUTO, 295 currentFrameRate = cameraAppSettings.targetFrameRate, 296 defaultLensFacing = cameraAppSettings.cameraLensFacing, 297 deviceStabilizations = deviceStabilizations, 298 currentLensStabilizations = supportedStabilizationModes, 299 unsupportedFrameRates = StabilizationMode.AUTO.unsupportedFpsSet 300 ), 301 stabilizationOnState = getSingleStabilizationState( 302 stabilizationMode = StabilizationMode.ON, 303 currentFrameRate = cameraAppSettings.targetFrameRate, 304 defaultLensFacing = cameraAppSettings.cameraLensFacing, 305 deviceStabilizations = deviceStabilizations, 306 currentLensStabilizations = supportedStabilizationModes, 307 unsupportedFrameRates = StabilizationMode.ON.unsupportedFpsSet 308 ), 309 stabilizationHighQualityState = getSingleStabilizationState( 310 stabilizationMode = StabilizationMode.HIGH_QUALITY, 311 currentFrameRate = cameraAppSettings.targetFrameRate, 312 defaultLensFacing = cameraAppSettings.cameraLensFacing, 313 deviceStabilizations = deviceStabilizations, 314 currentLensStabilizations = supportedStabilizationModes, 315 unsupportedFrameRates = StabilizationMode.HIGH_QUALITY.unsupportedFpsSet 316 ), 317 stabilizationOpticalState = getSingleStabilizationState( 318 stabilizationMode = StabilizationMode.OPTICAL, 319 currentFrameRate = cameraAppSettings.targetFrameRate, 320 defaultLensFacing = cameraAppSettings.cameraLensFacing, 321 deviceStabilizations = deviceStabilizations, 322 currentLensStabilizations = supportedStabilizationModes, 323 unsupportedFrameRates = StabilizationMode.OPTICAL.unsupportedFpsSet 324 ) 325 ) 326 } 327 } 328 329 private fun getSingleStabilizationState( 330 stabilizationMode: StabilizationMode, 331 currentFrameRate: Int, 332 defaultLensFacing: LensFacing, 333 deviceStabilizations: Set<StabilizationMode>, 334 currentLensStabilizations: Set<StabilizationMode>?, 335 unsupportedFrameRates: Set<Int> 336 ): SingleSelectableState { 337 // if unsupported by device 338 if (!deviceStabilizations.contains(stabilizationMode)) { 339 return SingleSelectableState.Disabled( 340 disabledRationale = 341 DeviceUnsupportedRationale(R.string.stabilization_rationale_prefix) 342 ) 343 } 344 345 // if unsupported by by current lens 346 if (currentLensStabilizations?.contains(stabilizationMode) == false) { 347 return SingleSelectableState.Disabled( 348 getLensUnsupportedRationale( 349 defaultLensFacing, 350 R.string.stabilization_rationale_prefix 351 ) 352 ) 353 } 354 355 // if fps is unsupported by preview stabilization 356 if (currentFrameRate in unsupportedFrameRates) { 357 return SingleSelectableState.Disabled( 358 FpsUnsupportedRationale( 359 R.string.stabilization_rationale_prefix, 360 currentFrameRate 361 ) 362 ) 363 } 364 365 return SingleSelectableState.Selectable 366 } 367 368 private fun getVideoQualityUiState( 369 systemConstraints: SystemConstraints, 370 cameraAppSettings: CameraAppSettings 371 ): VideoQualityUiState { 372 val cameraConstraints = systemConstraints.forCurrentLens(cameraAppSettings) 373 val supportedVideoQualities: List<VideoQuality> = 374 cameraConstraints?.supportedVideoQualitiesMap?.get( 375 cameraAppSettings.dynamicRange 376 ) ?: listOf(VideoQuality.UNSPECIFIED) 377 378 return if (supportedVideoQualities != listOf(VideoQuality.UNSPECIFIED)) { 379 VideoQualityUiState.Enabled( 380 currentVideoQuality = cameraAppSettings.videoQuality, 381 videoQualityAutoState = SingleSelectableState.Selectable, 382 videoQualitySDState = getSingleVideoQualityState( 383 VideoQuality.SD, 384 supportedVideoQualities 385 ), 386 videoQualityHDState = getSingleVideoQualityState( 387 VideoQuality.HD, 388 supportedVideoQualities 389 ), 390 videoQualityFHDState = getSingleVideoQualityState( 391 VideoQuality.FHD, 392 supportedVideoQualities 393 ), 394 videoQualityUHDState = getSingleVideoQualityState( 395 VideoQuality.UHD, 396 supportedVideoQualities 397 ) 398 ) 399 } else { 400 VideoQualityUiState.Disabled( 401 DisabledRationale.VideoQualityUnsupportedRationale( 402 R.string.video_quality_rationale_prefix 403 ) 404 ) 405 } 406 } 407 408 private fun getSingleVideoQualityState( 409 videoQuality: VideoQuality, 410 supportedVideQualities: List<VideoQuality> 411 ): SingleSelectableState = if (supportedVideQualities.contains(videoQuality)) { 412 SingleSelectableState.Selectable 413 } else { 414 SingleSelectableState.Disabled( 415 DisabledRationale.VideoQualityUnsupportedRationale( 416 R.string.video_quality_rationale_prefix 417 ) 418 ) 419 } 420 421 /** 422 * Enables or disables default camera switch based on: 423 * - number of cameras available 424 * - if there is a front and rear camera, the camera that the setting would switch to must also 425 * support the other settings 426 * */ 427 private fun getLensFlipUiState( 428 systemConstraints: SystemConstraints, 429 currentSettings: CameraAppSettings 430 ): FlipLensUiState { 431 // if there is only one lens, stop here 432 if (!with(systemConstraints.availableLenses) { 433 size > 1 && contains(com.google.jetpackcamera.settings.model.LensFacing.FRONT) 434 } 435 ) { 436 return FlipLensUiState.Disabled( 437 currentLensFacing = currentSettings.cameraLensFacing, 438 disabledRationale = 439 DeviceUnsupportedRationale( 440 // display the lens that isnt supported 441 when (currentSettings.cameraLensFacing) { 442 LensFacing.BACK -> R.string.front_lens_rationale_prefix 443 LensFacing.FRONT -> R.string.rear_lens_rationale_prefix 444 } 445 ) 446 ) 447 } 448 449 // If multiple lens available, continue 450 val newLensFacing = if (currentSettings.cameraLensFacing == LensFacing.FRONT) { 451 LensFacing.BACK 452 } else { 453 LensFacing.FRONT 454 } 455 val newLensConstraints = systemConstraints.perLensConstraints[newLensFacing]!! 456 // make sure all current settings wont break constraint when changing new default lens 457 458 // if new lens won't support current fps 459 if (currentSettings.targetFrameRate != FPS_AUTO && 460 !newLensConstraints.supportedFixedFrameRates 461 .contains(currentSettings.targetFrameRate) 462 ) { 463 return FlipLensUiState.Disabled( 464 currentLensFacing = currentSettings.cameraLensFacing, 465 disabledRationale = FpsUnsupportedRationale( 466 when (currentSettings.cameraLensFacing) { 467 LensFacing.BACK -> R.string.front_lens_rationale_prefix 468 LensFacing.FRONT -> R.string.rear_lens_rationale_prefix 469 }, 470 currentSettings.targetFrameRate 471 ) 472 ) 473 } 474 475 // If a non-AUTO stabilization is currently on and the other lens won't support it 476 if (currentSettings.stabilizationMode != StabilizationMode.AUTO && 477 currentSettings.stabilizationMode !in newLensConstraints.supportedStabilizationModes 478 ) { 479 return FlipLensUiState.Disabled( 480 currentLensFacing = currentSettings.cameraLensFacing, 481 disabledRationale = StabilizationUnsupportedRationale( 482 when (currentSettings.cameraLensFacing) { 483 LensFacing.BACK -> R.string.front_lens_rationale_prefix 484 LensFacing.FRONT -> R.string.rear_lens_rationale_prefix 485 } 486 ) 487 ) 488 } 489 490 // if other lens doesnt support the video quality 491 if (currentSettings.videoQuality != VideoQuality.UNSPECIFIED && 492 newLensConstraints.supportedVideoQualitiesMap[DynamicRange.SDR]?.contains( 493 currentSettings.videoQuality 494 ) != true 495 ) { 496 return FlipLensUiState.Disabled( 497 currentLensFacing = currentSettings.cameraLensFacing, 498 disabledRationale = DisabledRationale.VideoQualityUnsupportedRationale( 499 when (currentSettings.cameraLensFacing) { 500 LensFacing.BACK -> R.string.front_lens_rationale_prefix 501 LensFacing.FRONT -> R.string.rear_lens_rationale_prefix 502 }, 503 R.string.video_quality_rationale_suffix_sdr 504 ) 505 ) 506 } 507 508 return FlipLensUiState.Enabled(currentLensFacing = currentSettings.cameraLensFacing) 509 } 510 511 private fun getFpsUiState( 512 systemConstraints: SystemConstraints, 513 cameraAppSettings: CameraAppSettings 514 ): FpsUiState { 515 val optionConstraintRationale: MutableMap<Int, SingleSelectableState> = mutableMapOf() 516 517 val deviceSupportedFrameRates = systemConstraints.perLensConstraints 518 .asSequence() 519 .flatMap { it.value.supportedFixedFrameRates } 520 .toSet() 521 522 // if device supports no fixed frame rates, disable 523 if (deviceSupportedFrameRates.isEmpty()) { 524 return FpsUiState.Disabled( 525 DeviceUnsupportedRationale(R.string.no_fixed_fps_rationale_prefix) 526 ) 527 } 528 529 val currentLensConstraints = checkNotNull( 530 systemConstraints.forCurrentLens(cameraAppSettings) 531 ) { 532 "Lens constraints for ${cameraAppSettings.cameraLensFacing} not available." 533 } 534 535 with(currentLensConstraints) { 536 // provide selectable states for each of the fps options 537 fpsOptions.forEach { fpsOption -> 538 val fpsUiState = isFpsOptionEnabled( 539 fpsOption = fpsOption, 540 defaultLensFacing = cameraAppSettings.cameraLensFacing, 541 deviceSupportedFrameRates = deviceSupportedFrameRates, 542 stabilizationMode = cameraAppSettings.stabilizationMode 543 ) 544 if (fpsUiState is SingleSelectableState.Disabled) { 545 Log.d( 546 TAG, 547 "fps option $fpsOption disabled. ${fpsUiState.disabledRationale::class}" 548 ) 549 } 550 optionConstraintRationale[fpsOption] = fpsUiState 551 } 552 } 553 return FpsUiState.Enabled( 554 currentSelection = cameraAppSettings.targetFrameRate, 555 fpsAutoState = SingleSelectableState.Selectable, 556 fpsFifteenState = optionConstraintRationale[FPS_15]!!, 557 fpsThirtyState = optionConstraintRationale[FPS_30]!!, 558 fpsSixtyState = optionConstraintRationale[FPS_60]!! 559 ) 560 } 561 562 /** 563 * Auxiliary function to determine if an FPS option should be disabled or not 564 */ 565 private fun CameraConstraints.isFpsOptionEnabled( 566 fpsOption: Int, 567 defaultLensFacing: LensFacing, 568 deviceSupportedFrameRates: Set<Int>, 569 stabilizationMode: StabilizationMode 570 ): SingleSelectableState { 571 // if device doesn't support the fps option, disable 572 if (!deviceSupportedFrameRates.contains(fpsOption)) { 573 return SingleSelectableState.Disabled( 574 disabledRationale = DeviceUnsupportedRationale(R.string.fps_rationale_prefix) 575 ) 576 } 577 // if the current lens doesn't support the fps, disable 578 if (!supportedFixedFrameRates.contains(fpsOption)) { 579 Log.d(TAG, "FPS disabled for current lens") 580 581 return SingleSelectableState.Disabled( 582 getLensUnsupportedRationale(defaultLensFacing, R.string.fps_rationale_prefix) 583 ) 584 } 585 586 // if stabilization is on and the option is incompatible, disable 587 if (fpsOption in stabilizationMode.unsupportedFpsSet) { 588 return SingleSelectableState.Disabled( 589 StabilizationUnsupportedRationale(R.string.fps_rationale_prefix) 590 ) 591 } 592 593 return SingleSelectableState.Selectable 594 } 595 596 // //////////////////////////////////////////////////////////// 597 // 598 // Settings Repository functions 599 // 600 // //////////////////////////////////////////////////////////// 601 602 fun setDefaultLensFacing(lensFacing: LensFacing) { 603 viewModelScope.launch { 604 settingsRepository.updateDefaultLensFacing(lensFacing) 605 Log.d(TAG, "set camera default facing: $lensFacing") 606 } 607 } 608 609 fun setDarkMode(darkMode: DarkMode) { 610 viewModelScope.launch { 611 settingsRepository.updateDarkModeStatus(darkMode) 612 Log.d(TAG, "set dark mode theme: $darkMode") 613 } 614 } 615 616 fun setFlashMode(flashMode: FlashMode) { 617 viewModelScope.launch { 618 settingsRepository.updateFlashModeStatus(flashMode) 619 Log.d(TAG, "set flash mode: $flashMode") 620 } 621 } 622 623 fun setTargetFrameRate(targetFrameRate: Int) { 624 viewModelScope.launch { 625 settingsRepository.updateTargetFrameRate(targetFrameRate) 626 Log.d(TAG, "set target frame rate: $targetFrameRate") 627 } 628 } 629 630 fun setAspectRatio(aspectRatio: AspectRatio) { 631 viewModelScope.launch { 632 settingsRepository.updateAspectRatio(aspectRatio) 633 Log.d(TAG, "set aspect ratio: $aspectRatio") 634 } 635 } 636 637 fun setStreamConfig(streamConfig: StreamConfig) { 638 viewModelScope.launch { 639 settingsRepository.updateStreamConfig(streamConfig) 640 Log.d(TAG, "set default capture mode: $streamConfig") 641 } 642 } 643 644 fun setStabilizationMode(stabilizationMode: StabilizationMode) { 645 viewModelScope.launch { 646 settingsRepository.updateStabilizationMode(stabilizationMode) 647 Log.d(TAG, "set stabilization mode: $stabilizationMode") 648 } 649 } 650 651 fun setMaxVideoDuration(durationMillis: Long) { 652 viewModelScope.launch { 653 settingsRepository.updateMaxVideoDuration(durationMillis) 654 Log.d(TAG, "set video duration: $durationMillis ms") 655 } 656 } 657 658 fun setVideoQuality(videoQuality: VideoQuality) { 659 viewModelScope.launch { 660 settingsRepository.updateVideoQuality(videoQuality) 661 Log.d(TAG, "set video quality: $videoQuality ms") 662 } 663 } 664 665 fun setVideoAudio(isAudioEnabled: Boolean) { 666 viewModelScope.launch { 667 settingsRepository.updateAudioEnabled(isAudioEnabled) 668 Log.d(TAG, "recording audio muted: $isAudioEnabled") 669 } 670 } 671 } 672