diff --git a/core/model/src/main/java/com/google/jetpackcamera/model/AspectRatio.kt b/core/model/src/main/java/com/google/jetpackcamera/model/AspectRatio.kt index 8dafa4a5f..8b96e1304 100644 --- a/core/model/src/main/java/com/google/jetpackcamera/model/AspectRatio.kt +++ b/core/model/src/main/java/com/google/jetpackcamera/model/AspectRatio.kt @@ -15,8 +15,6 @@ */ package com.google.jetpackcamera.model -import com.google.jetpackcamera.model.proto.AspectRatio as AspectRatioProto - enum class AspectRatio(val numerator: Int, val denominator: Int) { THREE_FOUR(3, 4), NINE_SIXTEEN(9, 16), @@ -31,21 +29,4 @@ enum class AspectRatio(val numerator: Int, val denominator: Int) { * Returns the landscape aspect ratio as a [Float]. */ fun toLandscapeFloat(): Float = denominator.toFloat() / numerator - - companion object { - - /** returns the AspectRatio enum equivalent of a provided AspectRatioProto */ - fun fromProto(aspectRatioProto: AspectRatioProto): AspectRatio { - return when (aspectRatioProto) { - AspectRatioProto.ASPECT_RATIO_NINE_SIXTEEN -> NINE_SIXTEEN - AspectRatioProto.ASPECT_RATIO_ONE_ONE -> ONE_ONE - - // defaults to 3:4 aspect ratio - AspectRatioProto.ASPECT_RATIO_THREE_FOUR, - AspectRatioProto.ASPECT_RATIO_UNDEFINED, - AspectRatioProto.UNRECOGNIZED - -> THREE_FOUR - } - } - } } diff --git a/core/model/src/main/java/com/google/jetpackcamera/model/DebugSettings.kt b/core/model/src/main/java/com/google/jetpackcamera/model/DebugSettings.kt index 25e05698e..7b36fe7d1 100644 --- a/core/model/src/main/java/com/google/jetpackcamera/model/DebugSettings.kt +++ b/core/model/src/main/java/com/google/jetpackcamera/model/DebugSettings.kt @@ -15,12 +15,6 @@ */ package com.google.jetpackcamera.model -import android.util.Base64 -import com.google.jetpackcamera.model.LensFacing.Companion.toProto -import com.google.jetpackcamera.model.TestPattern.Companion.toProto -import com.google.jetpackcamera.model.proto.DebugSettings as DebugSettingsProto -import com.google.jetpackcamera.model.proto.debugSettings as debugSettingsProto - /** * Data class for defining settings used in debug flows within the app. * @@ -35,67 +29,4 @@ data class DebugSettings( val isDebugModeEnabled: Boolean = false, val singleLensMode: LensFacing? = null, val testPattern: TestPattern = TestPattern.Off -) { - companion object { - /** - * Creates a [DebugSettings] domain model from its protobuf representation. - * - * @param proto The [DebugSettingsProto] instance. - * @return The corresponding [DebugSettings] instance. - */ - fun fromProto(proto: DebugSettingsProto): DebugSettings { - return DebugSettings( - isDebugModeEnabled = proto.isDebugModeEnabled, - singleLensMode = if (proto.hasSingleLensMode()) { - LensFacing.fromProto(proto.singleLensMode) - } else { - null - }, - testPattern = TestPattern.fromProto(proto.testPattern) - ) - } - - /** - * Converts a [DebugSettings] domain model to its protobuf representation. - * - * @receiver The [DebugSettings] instance to convert. - * @return The corresponding [DebugSettingsProto] instance. - */ - fun DebugSettings.toProto(): DebugSettingsProto = debugSettingsProto { - isDebugModeEnabled = this@toProto.isDebugModeEnabled - this@toProto.singleLensMode?.let { lensFacing -> - singleLensMode = lensFacing.toProto() - } - testPattern = this@toProto.testPattern.toProto() - } - - /** - * Parses the encoded byte array into a [DebugSettings] instance. - */ - fun parseFromByteArray(value: ByteArray): DebugSettings { - val protoValue = DebugSettingsProto.parseFrom(value) - return fromProto(protoValue) - } - - /** - * Parses the Base64 encoded string into a [DebugSettings] instance. - */ - fun parseFromString(value: String): DebugSettings { - val decodedBytes = Base64.decode(value, Base64.NO_WRAP) - return parseFromByteArray(decodedBytes) - } - - /** - * Encodes the [DebugSettings] data class into a byte array. - */ - fun DebugSettings.encodeAsByteArray(): ByteArray = this.toProto().toByteArray() - - /** - * Encodes the [DebugSettings] data class to a Base64 string. - */ - fun DebugSettings.encodeAsString(): String { - val protoValue = this.toProto() // Data class -> Proto - return Base64.encodeToString(protoValue.toByteArray(), Base64.NO_WRAP) - } - } -} +) diff --git a/core/model/src/main/java/com/google/jetpackcamera/model/DynamicRange.kt b/core/model/src/main/java/com/google/jetpackcamera/model/DynamicRange.kt index ecc228741..8afdb1da3 100644 --- a/core/model/src/main/java/com/google/jetpackcamera/model/DynamicRange.kt +++ b/core/model/src/main/java/com/google/jetpackcamera/model/DynamicRange.kt @@ -14,33 +14,10 @@ * limitations under the License. */ package com.google.jetpackcamera.model -import com.google.jetpackcamera.model.proto.DynamicRange as DynamicRangeProto val DEFAULT_HDR_DYNAMIC_RANGE = DynamicRange.HLG10 enum class DynamicRange { SDR, - HLG10; - - companion object { - - /** returns the DynamicRangeType enum equivalent of a provided DynamicRangeTypeProto */ - fun fromProto(dynamicRangeProto: DynamicRangeProto): DynamicRange { - return when (dynamicRangeProto) { - DynamicRangeProto.DYNAMIC_RANGE_HLG10 -> HLG10 - - // Treat unrecognized and unspecified as SDR as a fallback - DynamicRangeProto.DYNAMIC_RANGE_SDR, - DynamicRangeProto.DYNAMIC_RANGE_UNSPECIFIED, - DynamicRangeProto.UNRECOGNIZED -> SDR - } - } - - fun DynamicRange.toProto(): DynamicRangeProto { - return when (this) { - SDR -> DynamicRangeProto.DYNAMIC_RANGE_SDR - HLG10 -> DynamicRangeProto.DYNAMIC_RANGE_HLG10 - } - } - } + HLG10 } diff --git a/core/model/src/main/java/com/google/jetpackcamera/model/ImageOutputFormat.kt b/core/model/src/main/java/com/google/jetpackcamera/model/ImageOutputFormat.kt index 8fa219ab8..c19a4826e 100644 --- a/core/model/src/main/java/com/google/jetpackcamera/model/ImageOutputFormat.kt +++ b/core/model/src/main/java/com/google/jetpackcamera/model/ImageOutputFormat.kt @@ -15,32 +15,9 @@ */ package com.google.jetpackcamera.model -import com.google.jetpackcamera.model.proto.ImageOutputFormat as ImageOutputFormatProto - val DEFAULT_HDR_IMAGE_OUTPUT = ImageOutputFormat.JPEG_ULTRA_HDR enum class ImageOutputFormat { JPEG, - JPEG_ULTRA_HDR; - - companion object { - - /** returns the DynamicRangeType enum equivalent of a provided DynamicRangeTypeProto */ - fun fromProto(imageOutputFormatProto: ImageOutputFormatProto): ImageOutputFormat { - return when (imageOutputFormatProto) { - ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG_ULTRA_HDR -> JPEG_ULTRA_HDR - - // Treat unrecognized as JPEG as a fallback - ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG, - ImageOutputFormatProto.UNRECOGNIZED -> JPEG - } - } - - fun ImageOutputFormat.toProto(): ImageOutputFormatProto { - return when (this) { - JPEG -> ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG - JPEG_ULTRA_HDR -> ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG_ULTRA_HDR - } - } - } + JPEG_ULTRA_HDR } diff --git a/core/model/src/main/java/com/google/jetpackcamera/model/LensFacing.kt b/core/model/src/main/java/com/google/jetpackcamera/model/LensFacing.kt index 4aa5a27b5..b72c807b0 100644 --- a/core/model/src/main/java/com/google/jetpackcamera/model/LensFacing.kt +++ b/core/model/src/main/java/com/google/jetpackcamera/model/LensFacing.kt @@ -15,8 +15,6 @@ */ package com.google.jetpackcamera.model -import com.google.jetpackcamera.model.proto.LensFacing as LensFacingProto - enum class LensFacing { BACK, FRONT; @@ -27,25 +25,4 @@ enum class LensFacing { BACK -> FRONT } } - - companion object { - - /** returns the LensFacing enum equivalent of a provided LensFacingProto */ - fun fromProto(lensFacingProto: LensFacingProto): LensFacing { - return when (lensFacingProto) { - LensFacingProto.LENS_FACING_BACK -> BACK - - // Treat unrecognized as front as a fallback - LensFacingProto.LENS_FACING_FRONT, - LensFacingProto.UNRECOGNIZED -> FRONT - } - } - - fun LensFacing.toProto(): LensFacingProto { - return when (this) { - BACK -> LensFacingProto.LENS_FACING_BACK - FRONT -> LensFacingProto.LENS_FACING_FRONT - } - } - } } diff --git a/core/model/src/main/java/com/google/jetpackcamera/model/LowLightBoostPriority.kt b/core/model/src/main/java/com/google/jetpackcamera/model/LowLightBoostPriority.kt index eb6aa7367..38da49b84 100644 --- a/core/model/src/main/java/com/google/jetpackcamera/model/LowLightBoostPriority.kt +++ b/core/model/src/main/java/com/google/jetpackcamera/model/LowLightBoostPriority.kt @@ -15,36 +15,7 @@ */ package com.google.jetpackcamera.model -import com.google.jetpackcamera.model.proto.LowLightBoostPriority as LowLightBoostPriorityProto - enum class LowLightBoostPriority { PRIORITIZE_AE_MODE, - PRIORITIZE_GOOGLE_PLAY_SERVICES; - - companion object { - /** - * Returns the [LowLightBoostPriority] enum equivalent of a provided [LowLightBoostPriorityProto]. - * - * @param lowLightBoostPriorityProto The proto to convert from. - * @return The converted [LowLightBoostPriority]. - */ - fun fromProto( - lowLightBoostPriorityProto: LowLightBoostPriorityProto - ): LowLightBoostPriority { - return when (lowLightBoostPriorityProto) { - LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_AE_MODE -> PRIORITIZE_AE_MODE - LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_GOOGLE_PLAY_SERVICES -> - PRIORITIZE_GOOGLE_PLAY_SERVICES - LowLightBoostPriorityProto.UNRECOGNIZED -> PRIORITIZE_AE_MODE // Default to AE mode - } - } - - fun LowLightBoostPriority.toProto(): LowLightBoostPriorityProto { - return when (this) { - PRIORITIZE_AE_MODE -> LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_AE_MODE - PRIORITIZE_GOOGLE_PLAY_SERVICES -> - LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_GOOGLE_PLAY_SERVICES - } - } - } + PRIORITIZE_GOOGLE_PLAY_SERVICES } diff --git a/core/model/src/main/java/com/google/jetpackcamera/model/StabilizationMode.kt b/core/model/src/main/java/com/google/jetpackcamera/model/StabilizationMode.kt index bf0dfbe1d..13aac311d 100644 --- a/core/model/src/main/java/com/google/jetpackcamera/model/StabilizationMode.kt +++ b/core/model/src/main/java/com/google/jetpackcamera/model/StabilizationMode.kt @@ -15,8 +15,6 @@ */ package com.google.jetpackcamera.model -import com.google.jetpackcamera.model.proto.StabilizationMode as StabilizationModeProto - /** Enum class representing the device's supported stabilization configurations. */ enum class StabilizationMode { /** Stabilization off */ @@ -36,22 +34,5 @@ enum class StabilizationMode { HIGH_QUALITY, /** Optical Stabilization (OIS) */ - OPTICAL; - - companion object { - /** returns the AspectRatio enum equivalent of a provided AspectRatioProto */ - fun fromProto(stabilizationModeProto: StabilizationModeProto): StabilizationMode = - when (stabilizationModeProto) { - StabilizationModeProto.STABILIZATION_MODE_OFF -> OFF - StabilizationModeProto.STABILIZATION_MODE_ON -> ON - StabilizationModeProto.STABILIZATION_MODE_HIGH_QUALITY -> HIGH_QUALITY - StabilizationModeProto.STABILIZATION_MODE_OPTICAL -> OPTICAL - - // Default to AUTO - StabilizationModeProto.STABILIZATION_MODE_UNDEFINED, - StabilizationModeProto.UNRECOGNIZED, - StabilizationModeProto.STABILIZATION_MODE_AUTO - -> AUTO - } - } + OPTICAL } diff --git a/core/model/src/main/java/com/google/jetpackcamera/model/TestPattern.kt b/core/model/src/main/java/com/google/jetpackcamera/model/TestPattern.kt index 15fc3e2f4..d758db1ee 100644 --- a/core/model/src/main/java/com/google/jetpackcamera/model/TestPattern.kt +++ b/core/model/src/main/java/com/google/jetpackcamera/model/TestPattern.kt @@ -15,16 +15,6 @@ */ package com.google.jetpackcamera.model -import com.google.jetpackcamera.model.proto.TestPattern as ProtoTestPattern -import com.google.jetpackcamera.model.proto.TestPattern.PatternCase -import com.google.jetpackcamera.model.proto.testPattern as protoTestPattern -import com.google.jetpackcamera.model.proto.testPatternColorBars -import com.google.jetpackcamera.model.proto.testPatternColorBarsFadeToGray -import com.google.jetpackcamera.model.proto.testPatternCustom1 -import com.google.jetpackcamera.model.proto.testPatternOff -import com.google.jetpackcamera.model.proto.testPatternPN9 -import com.google.jetpackcamera.model.proto.testPatternSolidColor - /** * Represents a test pattern to replace sensor pixel data. * @@ -179,56 +169,4 @@ sealed interface TestPattern { ) } } - - companion object { - /** - * Converts a [TestPattern] sealed interface instance to its Protocol Buffer representation - * ([ProtoTestPattern]). - */ - fun TestPattern.toProto(): ProtoTestPattern { - return protoTestPattern { - when (val pattern = this@toProto) { - is Off -> off = testPatternOff {} - is ColorBars -> colorBars = testPatternColorBars {} - is ColorBarsFadeToGray -> - colorBarsFadeToGray = testPatternColorBarsFadeToGray {} - is PN9 -> pn9 = testPatternPN9 {} - is Custom1 -> custom1 = testPatternCustom1 {} - is SolidColor -> solidColor = testPatternSolidColor { - red = pattern.red.toInt() - greenEven = pattern.greenEven.toInt() - greenOdd = pattern.greenOdd.toInt() - blue = pattern.blue.toInt() - } - } - } - } - - /** - * Converts a [ProtoTestPattern] Protocol Buffer message to its Kotlin [TestPattern] sealed - * interface representation. - */ - fun fromProto(proto: ProtoTestPattern): TestPattern { - return when (proto.patternCase) { - PatternCase.OFF, - PatternCase.PATTERN_NOT_SET -> { - // Default to Off if the oneof is not set - Off - } - PatternCase.COLOR_BARS -> ColorBars - PatternCase.COLOR_BARS_FADE_TO_GRAY -> ColorBarsFadeToGray - PatternCase.PN9 -> PN9 - PatternCase.CUSTOM1 -> Custom1 - PatternCase.SOLID_COLOR -> { - val protoSolidColor = proto.solidColor - SolidColor( - red = protoSolidColor.red.toUInt(), - greenEven = protoSolidColor.greenEven.toUInt(), - greenOdd = protoSolidColor.greenOdd.toUInt(), - blue = protoSolidColor.blue.toUInt() - ) - } - } - } - } } diff --git a/core/model/src/main/java/com/google/jetpackcamera/model/VideoQuality.kt b/core/model/src/main/java/com/google/jetpackcamera/model/VideoQuality.kt index 143b7d6a2..5c52aa61b 100644 --- a/core/model/src/main/java/com/google/jetpackcamera/model/VideoQuality.kt +++ b/core/model/src/main/java/com/google/jetpackcamera/model/VideoQuality.kt @@ -15,37 +15,10 @@ */ package com.google.jetpackcamera.model -import com.google.jetpackcamera.model.proto.VideoQuality as VideoQualityProto - enum class VideoQuality { UNSPECIFIED, SD, HD, FHD, - UHD; - - companion object { - /** returns the VideoQuality enum equivalent of a provided VideoQualityProto */ - fun fromProto(videoQualityProto: VideoQualityProto): VideoQuality { - return when (videoQualityProto) { - VideoQualityProto.VIDEO_QUALITY_SD -> SD - VideoQualityProto.VIDEO_QUALITY_HD -> HD - VideoQualityProto.VIDEO_QUALITY_FHD -> FHD - VideoQualityProto.VIDEO_QUALITY_UHD -> UHD - VideoQualityProto.VIDEO_QUALITY_UNSPECIFIED, - VideoQualityProto.UNRECOGNIZED - -> UNSPECIFIED - } - } - - fun VideoQuality.toProto(): VideoQualityProto { - return when (this) { - UNSPECIFIED -> VideoQualityProto.VIDEO_QUALITY_UNSPECIFIED - SD -> VideoQualityProto.VIDEO_QUALITY_SD - HD -> VideoQualityProto.VIDEO_QUALITY_HD - FHD -> VideoQualityProto.VIDEO_QUALITY_FHD - UHD -> VideoQualityProto.VIDEO_QUALITY_UHD - } - } - } + UHD } diff --git a/core/model/src/main/java/com/google/jetpackcamera/model/mappers/DebugSettingsMapper.kt b/core/model/src/main/java/com/google/jetpackcamera/model/mappers/DebugSettingsMapper.kt new file mode 100644 index 000000000..a09d887f0 --- /dev/null +++ b/core/model/src/main/java/com/google/jetpackcamera/model/mappers/DebugSettingsMapper.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.jetpackcamera.model.mappers + +import android.util.Base64 +import com.google.jetpackcamera.model.DebugSettings +import com.google.jetpackcamera.model.proto.DebugSettings as DebugSettingsProto + +object DebugSettingsMapper { + /** + * Parses the encoded byte array into a [DebugSettings] instance. + */ + fun parseFromByteArray(value: ByteArray): DebugSettings { + val protoValue = DebugSettingsProto.parseFrom(value) + return protoValue.toDomain() + } + + /** + * Parses the Base64 encoded string into a [DebugSettings] instance. + */ + fun parseFromString(value: String): DebugSettings { + val decodedBytes = Base64.decode(value, Base64.NO_WRAP) + return parseFromByteArray(decodedBytes) + } + + /** + * Encodes the [DebugSettings] data class into a byte array. + */ + fun DebugSettings.encodeAsByteArray(): ByteArray = this.toProto().toByteArray() + + /** + * Encodes the [DebugSettings] data class to a Base64 string. + */ + fun DebugSettings.encodeAsString(): String { + val protoValue = this.toProto() // Data class -> Proto + return Base64.encodeToString(protoValue.toByteArray(), Base64.NO_WRAP) + } +} diff --git a/core/model/src/main/java/com/google/jetpackcamera/model/mappers/ProtoMappers.kt b/core/model/src/main/java/com/google/jetpackcamera/model/mappers/ProtoMappers.kt new file mode 100644 index 000000000..7046f7575 --- /dev/null +++ b/core/model/src/main/java/com/google/jetpackcamera/model/mappers/ProtoMappers.kt @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.jetpackcamera.model.mappers + +import com.google.jetpackcamera.model.AspectRatio +import com.google.jetpackcamera.model.DebugSettings +import com.google.jetpackcamera.model.DynamicRange +import com.google.jetpackcamera.model.DynamicRange.HLG10 +import com.google.jetpackcamera.model.DynamicRange.SDR +import com.google.jetpackcamera.model.FlashMode +import com.google.jetpackcamera.model.ImageOutputFormat +import com.google.jetpackcamera.model.ImageOutputFormat.JPEG +import com.google.jetpackcamera.model.ImageOutputFormat.JPEG_ULTRA_HDR +import com.google.jetpackcamera.model.LensFacing +import com.google.jetpackcamera.model.LensFacing.BACK +import com.google.jetpackcamera.model.LensFacing.FRONT +import com.google.jetpackcamera.model.LowLightBoostPriority +import com.google.jetpackcamera.model.LowLightBoostPriority.PRIORITIZE_AE_MODE +import com.google.jetpackcamera.model.LowLightBoostPriority.PRIORITIZE_GOOGLE_PLAY_SERVICES +import com.google.jetpackcamera.model.StabilizationMode +import com.google.jetpackcamera.model.StabilizationMode.AUTO +import com.google.jetpackcamera.model.StabilizationMode.HIGH_QUALITY +import com.google.jetpackcamera.model.StabilizationMode.OFF +import com.google.jetpackcamera.model.StabilizationMode.ON +import com.google.jetpackcamera.model.StabilizationMode.OPTICAL +import com.google.jetpackcamera.model.StreamConfig +import com.google.jetpackcamera.model.TestPattern +import com.google.jetpackcamera.model.TestPattern.ColorBars +import com.google.jetpackcamera.model.TestPattern.ColorBarsFadeToGray +import com.google.jetpackcamera.model.TestPattern.Custom1 +import com.google.jetpackcamera.model.TestPattern.Off +import com.google.jetpackcamera.model.TestPattern.PN9 +import com.google.jetpackcamera.model.TestPattern.SolidColor +import com.google.jetpackcamera.model.VideoQuality +import com.google.jetpackcamera.model.VideoQuality.FHD +import com.google.jetpackcamera.model.VideoQuality.HD +import com.google.jetpackcamera.model.VideoQuality.SD +import com.google.jetpackcamera.model.VideoQuality.UHD +import com.google.jetpackcamera.model.VideoQuality.UNSPECIFIED +import com.google.jetpackcamera.model.proto.AspectRatio as AspectRatioProto +import com.google.jetpackcamera.model.proto.DebugSettings as DebugSettingsProto +import com.google.jetpackcamera.model.proto.DynamicRange as DynamicRangeProto +import com.google.jetpackcamera.model.proto.FlashMode as FlashModeProto +import com.google.jetpackcamera.model.proto.ImageOutputFormat as ImageOutputFormatProto +import com.google.jetpackcamera.model.proto.LensFacing as LensFacingProto +import com.google.jetpackcamera.model.proto.LowLightBoostPriority as LowLightBoostPriorityProto +import com.google.jetpackcamera.model.proto.StabilizationMode as StabilizationModeProto +import com.google.jetpackcamera.model.proto.StreamConfig as StreamConfigProto +import com.google.jetpackcamera.model.proto.TestPattern as ProtoTestPattern +import com.google.jetpackcamera.model.proto.TestPattern.PatternCase +import com.google.jetpackcamera.model.proto.VideoQuality as VideoQualityProto +import com.google.jetpackcamera.model.proto.debugSettings as debugSettingsProto +import com.google.jetpackcamera.model.proto.testPattern as protoTestPattern +import com.google.jetpackcamera.model.proto.testPatternColorBars +import com.google.jetpackcamera.model.proto.testPatternColorBarsFadeToGray +import com.google.jetpackcamera.model.proto.testPatternCustom1 +import com.google.jetpackcamera.model.proto.testPatternOff +import com.google.jetpackcamera.model.proto.testPatternPN9 +import com.google.jetpackcamera.model.proto.testPatternSolidColor + +/** + * Converts an [AspectRatio] enum to its corresponding [AspectRatioProto] representation. + */ +fun AspectRatio.toProto(): AspectRatioProto = when (this) { + AspectRatio.NINE_SIXTEEN -> AspectRatioProto.ASPECT_RATIO_NINE_SIXTEEN + AspectRatio.THREE_FOUR -> AspectRatioProto.ASPECT_RATIO_THREE_FOUR + AspectRatio.ONE_ONE -> AspectRatioProto.ASPECT_RATIO_ONE_ONE +} + +/** returns the AspectRatio enum equivalent of a provided AspectRatioProto */ +fun AspectRatioProto.toDomain(): AspectRatio { + return when (this) { + AspectRatioProto.ASPECT_RATIO_NINE_SIXTEEN -> AspectRatio.NINE_SIXTEEN + AspectRatioProto.ASPECT_RATIO_ONE_ONE -> AspectRatio.ONE_ONE + + // defaults to 3:4 aspect ratio + AspectRatioProto.ASPECT_RATIO_THREE_FOUR, + AspectRatioProto.ASPECT_RATIO_UNDEFINED, + AspectRatioProto.UNRECOGNIZED + -> AspectRatio.THREE_FOUR + } +} + +/** + * Creates a [DebugSettings] domain model from its protobuf representation. + * + * @return The corresponding [DebugSettings] instance. + */ +fun DebugSettingsProto.toDomain(): DebugSettings { + return DebugSettings( + isDebugModeEnabled = this.isDebugModeEnabled, + singleLensMode = if (this.hasSingleLensMode()) { + this.singleLensMode.toDomain() + } else { + null + }, + testPattern = this.testPattern.toDomain() + ) +} + +/** + * Converts a [DebugSettings] domain model to its protobuf representation. + * + * @receiver The [DebugSettings] instance to convert. + * @return The corresponding [DebugSettingsProto] instance. + */ +fun DebugSettings.toProto(): DebugSettingsProto = debugSettingsProto { + isDebugModeEnabled = this@toProto.isDebugModeEnabled + this@toProto.singleLensMode?.let { lensFacing -> + singleLensMode = lensFacing.toProto() + } + testPattern = this@toProto.testPattern.toProto() +} + +/** returns the DynamicRangeType enum equivalent of a provided DynamicRangeTypeProto */ +fun DynamicRangeProto.toDomain(): DynamicRange { + return when (this) { + DynamicRangeProto.DYNAMIC_RANGE_HLG10 -> HLG10 + + // Treat unrecognized and unspecified as SDR as a fallback + DynamicRangeProto.DYNAMIC_RANGE_SDR, + DynamicRangeProto.DYNAMIC_RANGE_UNSPECIFIED, + DynamicRangeProto.UNRECOGNIZED -> SDR + } +} + +/** + * Converts a [DynamicRange] enum to its corresponding [DynamicRangeProto] representation. + */ +fun DynamicRange.toProto(): DynamicRangeProto { + return when (this) { + SDR -> DynamicRangeProto.DYNAMIC_RANGE_SDR + HLG10 -> DynamicRangeProto.DYNAMIC_RANGE_HLG10 + } +} + +/** + * Converts a [FlashMode] enum to its corresponding [FlashModeProto] representation. + */ +fun FlashMode.toProto(): FlashModeProto = when (this) { + FlashMode.AUTO -> FlashModeProto.FLASH_MODE_AUTO + FlashMode.ON -> FlashModeProto.FLASH_MODE_ON + FlashMode.OFF -> FlashModeProto.FLASH_MODE_OFF + FlashMode.LOW_LIGHT_BOOST -> FlashModeProto.FLASH_MODE_LOW_LIGHT_BOOST +} + +/** + * Converts a [FlashModeProto] to its corresponding [FlashMode] enum representation. + */ +fun FlashModeProto.toDomain(): FlashMode = when (this) { + FlashModeProto.FLASH_MODE_AUTO -> FlashMode.AUTO + FlashModeProto.FLASH_MODE_ON -> FlashMode.ON + FlashModeProto.FLASH_MODE_OFF -> FlashMode.OFF + FlashModeProto.FLASH_MODE_LOW_LIGHT_BOOST -> FlashMode.LOW_LIGHT_BOOST + else -> FlashMode.OFF +} + +/** returns the DynamicRangeType enum equivalent of a provided DynamicRangeTypeProto */ +fun ImageOutputFormatProto.toDomain(): ImageOutputFormat { + return when (this) { + ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG_ULTRA_HDR -> JPEG_ULTRA_HDR + + // Treat unrecognized as JPEG as a fallback + ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG, + ImageOutputFormatProto.UNRECOGNIZED -> JPEG + } +} + +/** + * Converts an [ImageOutputFormat] enum to its corresponding [ImageOutputFormatProto] representation. + */ +fun ImageOutputFormat.toProto(): ImageOutputFormatProto { + return when (this) { + JPEG -> ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG + JPEG_ULTRA_HDR -> ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG_ULTRA_HDR + } +} + +/** returns the LensFacing enum equivalent of a provided LensFacingProto */ +fun LensFacingProto.toDomain(): LensFacing { + return when (this) { + LensFacingProto.LENS_FACING_FRONT -> FRONT + + // Treat unrecognized as back as a fallback + LensFacingProto.LENS_FACING_BACK, + LensFacingProto.UNRECOGNIZED -> BACK + } +} + +/** + * Converts a [LensFacing] enum to its corresponding [LensFacingProto] representation. + */ +fun LensFacing.toProto(): LensFacingProto { + return when (this) { + BACK -> LensFacingProto.LENS_FACING_BACK + FRONT -> LensFacingProto.LENS_FACING_FRONT + } +} + +/** + * Returns the [LowLightBoostPriority] enum equivalent of a provided [LowLightBoostPriorityProto]. + * + * @return The converted [LowLightBoostPriority]. + */ +fun LowLightBoostPriorityProto.toDomain(): LowLightBoostPriority { + return when (this) { + LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_AE_MODE -> PRIORITIZE_AE_MODE + LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_GOOGLE_PLAY_SERVICES -> + PRIORITIZE_GOOGLE_PLAY_SERVICES + + LowLightBoostPriorityProto.UNRECOGNIZED -> PRIORITIZE_AE_MODE // Default to AE mode + } +} + +/** + * Converts a [LowLightBoostPriority] enum to its corresponding [LowLightBoostPriorityProto] + * representation. + */ +fun LowLightBoostPriority.toProto(): LowLightBoostPriorityProto { + return when (this) { + PRIORITIZE_AE_MODE -> LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_AE_MODE + PRIORITIZE_GOOGLE_PLAY_SERVICES -> + LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_GOOGLE_PLAY_SERVICES + } +} + +/** + * Converts a [StabilizationMode] enum to its corresponding [StabilizationModeProto] representation. + */ +fun StabilizationMode.toProto(): StabilizationModeProto = when (this) { + StabilizationMode.OFF -> StabilizationModeProto.STABILIZATION_MODE_OFF + StabilizationMode.AUTO -> StabilizationModeProto.STABILIZATION_MODE_AUTO + StabilizationMode.ON -> StabilizationModeProto.STABILIZATION_MODE_ON + StabilizationMode.HIGH_QUALITY -> StabilizationModeProto.STABILIZATION_MODE_HIGH_QUALITY + StabilizationMode.OPTICAL -> StabilizationModeProto.STABILIZATION_MODE_OPTICAL +} + +/** returns the AspectRatio enum equivalent of a provided AspectRatioProto */ +fun StabilizationModeProto.toDomain(): StabilizationMode = when (this) { + StabilizationModeProto.STABILIZATION_MODE_OFF -> OFF + StabilizationModeProto.STABILIZATION_MODE_ON -> ON + StabilizationModeProto.STABILIZATION_MODE_HIGH_QUALITY -> HIGH_QUALITY + StabilizationModeProto.STABILIZATION_MODE_OPTICAL -> OPTICAL + + // Default to AUTO + StabilizationModeProto.STABILIZATION_MODE_UNDEFINED, + StabilizationModeProto.UNRECOGNIZED, + StabilizationModeProto.STABILIZATION_MODE_AUTO + -> AUTO +} + +/** + * Converts a [StreamConfig] enum to its corresponding [StreamConfigProto] representation. + */ +fun StreamConfig.toProto(): StreamConfigProto = when (this) { + StreamConfig.MULTI_STREAM -> StreamConfigProto.STREAM_CONFIG_MULTI_STREAM + StreamConfig.SINGLE_STREAM -> StreamConfigProto.STREAM_CONFIG_SINGLE_STREAM +} + +/** + * Converts a [StreamConfigProto] to its corresponding [StreamConfig] enum representation. + */ +fun StreamConfigProto.toDomain(): StreamConfig = when (this) { + StreamConfigProto.STREAM_CONFIG_SINGLE_STREAM -> StreamConfig.SINGLE_STREAM + StreamConfigProto.STREAM_CONFIG_MULTI_STREAM -> StreamConfig.MULTI_STREAM + else -> StreamConfig.MULTI_STREAM +} + +/** + * Converts a [TestPattern] sealed interface instance to its Protocol Buffer representation + * ([ProtoTestPattern]). + */ +fun TestPattern.toProto(): ProtoTestPattern { + return protoTestPattern { + when (val pattern = this@toProto) { + is Off -> off = testPatternOff {} + is ColorBars -> colorBars = testPatternColorBars {} + is ColorBarsFadeToGray -> + colorBarsFadeToGray = testPatternColorBarsFadeToGray {} + + is PN9 -> pn9 = testPatternPN9 {} + is Custom1 -> custom1 = testPatternCustom1 {} + is SolidColor -> solidColor = testPatternSolidColor { + red = pattern.red.toInt() + greenEven = pattern.greenEven.toInt() + greenOdd = pattern.greenOdd.toInt() + blue = pattern.blue.toInt() + } + } + } +} + +/** + * Converts a [ProtoTestPattern] Protocol Buffer message to its Kotlin [TestPattern] sealed + * interface representation. + */ +fun ProtoTestPattern.toDomain(): TestPattern { + return when (this.patternCase) { + PatternCase.OFF, + PatternCase.PATTERN_NOT_SET -> { + // Default to Off if the oneof is not set + Off + } + + PatternCase.COLOR_BARS -> ColorBars + PatternCase.COLOR_BARS_FADE_TO_GRAY -> ColorBarsFadeToGray + PatternCase.PN9 -> PN9 + PatternCase.CUSTOM1 -> Custom1 + PatternCase.SOLID_COLOR -> { + val protoSolidColor = this.solidColor + SolidColor( + red = protoSolidColor.red.toUInt(), + greenEven = protoSolidColor.greenEven.toUInt(), + greenOdd = protoSolidColor.greenOdd.toUInt(), + blue = protoSolidColor.blue.toUInt() + ) + } + } +} + +/** returns the VideoQuality enum equivalent of a provided VideoQualityProto */ +fun VideoQualityProto.toDomain(): VideoQuality { + return when (this) { + VideoQualityProto.VIDEO_QUALITY_SD -> SD + VideoQualityProto.VIDEO_QUALITY_HD -> HD + VideoQualityProto.VIDEO_QUALITY_FHD -> FHD + VideoQualityProto.VIDEO_QUALITY_UHD -> UHD + VideoQualityProto.VIDEO_QUALITY_UNSPECIFIED, + VideoQualityProto.UNRECOGNIZED + -> UNSPECIFIED + } +} + +fun VideoQuality.toProto(): VideoQualityProto { + return when (this) { + UNSPECIFIED -> VideoQualityProto.VIDEO_QUALITY_UNSPECIFIED + SD -> VideoQualityProto.VIDEO_QUALITY_SD + HD -> VideoQualityProto.VIDEO_QUALITY_HD + FHD -> VideoQualityProto.VIDEO_QUALITY_FHD + UHD -> VideoQualityProto.VIDEO_QUALITY_UHD + } +} diff --git a/core/model/src/test/java/com/google/jetpackcamera/model/ProtoConversionTest.kt b/core/model/src/test/java/com/google/jetpackcamera/model/ProtoConversionTest.kt new file mode 100644 index 000000000..b105aab2a --- /dev/null +++ b/core/model/src/test/java/com/google/jetpackcamera/model/ProtoConversionTest.kt @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.jetpackcamera.model + +import com.google.common.truth.Truth +import com.google.jetpackcamera.model.mappers.toDomain +import com.google.jetpackcamera.model.mappers.toProto +import com.google.jetpackcamera.model.proto.AspectRatio as AspectRatioProto +import com.google.jetpackcamera.model.proto.DebugSettings as DebugSettingsProto +import com.google.jetpackcamera.model.proto.DynamicRange as DynamicRangeProto +import com.google.jetpackcamera.model.proto.FlashMode as FlashModeProto +import com.google.jetpackcamera.model.proto.ImageOutputFormat as ImageOutputFormatProto +import com.google.jetpackcamera.model.proto.LensFacing as LensFacingProto +import com.google.jetpackcamera.model.proto.LowLightBoostPriority as LowLightBoostPriorityProto +import com.google.jetpackcamera.model.proto.StabilizationMode as StabilizationModeProto +import com.google.jetpackcamera.model.proto.StreamConfig as StreamConfigProto +import com.google.jetpackcamera.model.proto.VideoQuality as VideoQualityProto +import com.google.jetpackcamera.model.proto.debugSettings as debugSettingsProto +import com.google.jetpackcamera.model.proto.testPattern as protoTestPattern +import com.google.jetpackcamera.model.proto.testPatternColorBars +import com.google.jetpackcamera.model.proto.testPatternPN9 +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class ProtoConversionTest { + @Test + fun dynamicRange_convertsToCorrectProto() { + val correctConversions = { dynamicRange: DynamicRange -> + when (dynamicRange) { + DynamicRange.SDR -> DynamicRangeProto.DYNAMIC_RANGE_SDR + DynamicRange.HLG10 -> DynamicRangeProto.DYNAMIC_RANGE_HLG10 + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toProto()) + } + } + + @Test + fun dynamicRangeProto_convertsToCorrectDynamicRange() { + val correctConversions = { dynamicRangeProto: DynamicRangeProto -> + when (dynamicRangeProto) { + DynamicRangeProto.DYNAMIC_RANGE_SDR, + DynamicRangeProto.UNRECOGNIZED, + DynamicRangeProto.DYNAMIC_RANGE_UNSPECIFIED + -> DynamicRange.SDR + + DynamicRangeProto.DYNAMIC_RANGE_HLG10 -> DynamicRange.HLG10 + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toDomain()) + } + } + + @Test + fun imageOutputFormat_convertsToCorrectProto() { + val correctConversions = { imageOutputFormat: ImageOutputFormat -> + when (imageOutputFormat) { + ImageOutputFormat.JPEG -> ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG + ImageOutputFormat.JPEG_ULTRA_HDR + -> ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG_ULTRA_HDR + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toProto()) + } + } + + @Test + fun imageOutputFormatProto_convertsToCorrectImageOutputFormat() { + val correctConversions = { imageOutputFormatProto: ImageOutputFormatProto -> + when (imageOutputFormatProto) { + ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG, + ImageOutputFormatProto.UNRECOGNIZED + -> ImageOutputFormat.JPEG + + ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG_ULTRA_HDR + -> ImageOutputFormat.JPEG_ULTRA_HDR + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toDomain()) + } + } + + @Test + fun aspectRatio_convertsToCorrectProto() { + val correctConversions = { aspectRatio: AspectRatio -> + when (aspectRatio) { + AspectRatio.NINE_SIXTEEN -> AspectRatioProto.ASPECT_RATIO_NINE_SIXTEEN + AspectRatio.THREE_FOUR -> AspectRatioProto.ASPECT_RATIO_THREE_FOUR + AspectRatio.ONE_ONE -> AspectRatioProto.ASPECT_RATIO_ONE_ONE + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toProto()) + } + } + + @Test + fun aspectRatioProto_convertsToCorrectAspectRatio() { + val correctConversions = { aspectRatioProto: AspectRatioProto -> + when (aspectRatioProto) { + AspectRatioProto.ASPECT_RATIO_NINE_SIXTEEN -> AspectRatio.NINE_SIXTEEN + AspectRatioProto.ASPECT_RATIO_THREE_FOUR -> AspectRatio.THREE_FOUR + AspectRatioProto.ASPECT_RATIO_ONE_ONE -> AspectRatio.ONE_ONE + else -> AspectRatio.THREE_FOUR // Default value + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toDomain()) + } + } + + @Test + fun lensFacing_convertsToCorrectProto() { + val correctConversions = { lensFacing: LensFacing -> + when (lensFacing) { + LensFacing.FRONT -> LensFacingProto.LENS_FACING_FRONT + LensFacing.BACK -> LensFacingProto.LENS_FACING_BACK + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toProto()) + } + } + + @Test + fun lensFacingProto_convertsToCorrectLensFacing() { + val correctConversions = { lensFacingProto: LensFacingProto -> + when (lensFacingProto) { + LensFacingProto.LENS_FACING_FRONT -> LensFacing.FRONT + LensFacingProto.LENS_FACING_BACK -> LensFacing.BACK + else -> LensFacing.BACK // Default value + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toDomain()) + } + } + + @Test + fun lowLightBoostPriority_convertsToCorrectProto() { + val correctConversions = { lowLightBoostPriority: LowLightBoostPriority -> + when (lowLightBoostPriority) { + LowLightBoostPriority.PRIORITIZE_AE_MODE -> + LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_AE_MODE + LowLightBoostPriority.PRIORITIZE_GOOGLE_PLAY_SERVICES -> + LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_GOOGLE_PLAY_SERVICES + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toProto()) + } + } + + @Test + fun lowLightBoostPriorityProto_convertsToCorrectLowLightBoostPriority() { + val correctConversions = { lowLightBoostPriorityProto: LowLightBoostPriorityProto -> + when (lowLightBoostPriorityProto) { + LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_AE_MODE -> + LowLightBoostPriority.PRIORITIZE_AE_MODE + LowLightBoostPriorityProto.LOW_LIGHT_BOOST_PRIORITY_GOOGLE_PLAY_SERVICES -> + LowLightBoostPriority.PRIORITIZE_GOOGLE_PLAY_SERVICES + else -> LowLightBoostPriority.PRIORITIZE_AE_MODE // Default value + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toDomain()) + } + } + + @Test + fun stabilizationMode_convertsToCorrectProto() { + val correctConversions = { stabilizationMode: StabilizationMode -> + when (stabilizationMode) { + StabilizationMode.OFF -> StabilizationModeProto.STABILIZATION_MODE_OFF + StabilizationMode.ON -> StabilizationModeProto.STABILIZATION_MODE_ON + StabilizationMode.HIGH_QUALITY -> + StabilizationModeProto.STABILIZATION_MODE_HIGH_QUALITY + StabilizationMode.OPTICAL -> StabilizationModeProto.STABILIZATION_MODE_OPTICAL + StabilizationMode.AUTO -> StabilizationModeProto.STABILIZATION_MODE_AUTO + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toProto()) + } + } + + @Test + fun stabilizationModeProto_convertsToCorrectStabilizationMode() { + val correctConversions = { stabilizationModeProto: StabilizationModeProto -> + when (stabilizationModeProto) { + StabilizationModeProto.STABILIZATION_MODE_OFF -> StabilizationMode.OFF + StabilizationModeProto.STABILIZATION_MODE_ON -> StabilizationMode.ON + StabilizationModeProto.STABILIZATION_MODE_HIGH_QUALITY -> + StabilizationMode.HIGH_QUALITY + StabilizationModeProto.STABILIZATION_MODE_OPTICAL -> StabilizationMode.OPTICAL + else -> StabilizationMode.AUTO // Default value + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toDomain()) + } + } + + @Test + fun videoQuality_convertsToCorrectProto() { + val correctConversions = { videoQuality: VideoQuality -> + when (videoQuality) { + VideoQuality.SD -> VideoQualityProto.VIDEO_QUALITY_SD + VideoQuality.HD -> VideoQualityProto.VIDEO_QUALITY_HD + VideoQuality.FHD -> VideoQualityProto.VIDEO_QUALITY_FHD + VideoQuality.UHD -> VideoQualityProto.VIDEO_QUALITY_UHD + VideoQuality.UNSPECIFIED -> VideoQualityProto.VIDEO_QUALITY_UNSPECIFIED + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toProto()) + } + } + + @Test + fun videoQualityProto_convertsToCorrectVideoQuality() { + val correctConversions = { videoQualityProto: VideoQualityProto -> + when (videoQualityProto) { + VideoQualityProto.VIDEO_QUALITY_SD -> VideoQuality.SD + VideoQualityProto.VIDEO_QUALITY_HD -> VideoQuality.HD + VideoQualityProto.VIDEO_QUALITY_FHD -> VideoQuality.FHD + VideoQualityProto.VIDEO_QUALITY_UHD -> VideoQuality.UHD + else -> VideoQuality.UNSPECIFIED // Default value + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toDomain()) + } + } + + @Test + fun flashMode_convertsToCorrectProto() { + val correctConversions = { flashMode: FlashMode -> + when (flashMode) { + FlashMode.AUTO -> FlashModeProto.FLASH_MODE_AUTO + FlashMode.ON -> FlashModeProto.FLASH_MODE_ON + FlashMode.OFF -> FlashModeProto.FLASH_MODE_OFF + FlashMode.LOW_LIGHT_BOOST -> FlashModeProto.FLASH_MODE_LOW_LIGHT_BOOST + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toProto()) + } + } + + @Test + fun flashModeProto_convertsToCorrectFlashMode() { + val correctConversions = { flashModeProto: FlashModeProto -> + when (flashModeProto) { + FlashModeProto.FLASH_MODE_AUTO -> FlashMode.AUTO + FlashModeProto.FLASH_MODE_ON -> FlashMode.ON + FlashModeProto.FLASH_MODE_OFF -> FlashMode.OFF + FlashModeProto.FLASH_MODE_LOW_LIGHT_BOOST -> FlashMode.LOW_LIGHT_BOOST + else -> FlashMode.OFF // Default value + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toDomain()) + } + } + + @Test + fun streamConfig_convertsToCorrectProto() { + val correctConversions = { streamConfig: StreamConfig -> + when (streamConfig) { + StreamConfig.MULTI_STREAM -> StreamConfigProto.STREAM_CONFIG_MULTI_STREAM + StreamConfig.SINGLE_STREAM -> StreamConfigProto.STREAM_CONFIG_SINGLE_STREAM + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toProto()) + } + } + + @Test + fun streamConfigProto_convertsToCorrectStreamConfig() { + val correctConversions = { streamConfigProto: StreamConfigProto -> + when (streamConfigProto) { + StreamConfigProto.STREAM_CONFIG_MULTI_STREAM -> StreamConfig.MULTI_STREAM + StreamConfigProto.STREAM_CONFIG_SINGLE_STREAM -> StreamConfig.SINGLE_STREAM + else -> StreamConfig.MULTI_STREAM // Default value + } + } + + enumValues().forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toDomain()) + } + } + + @Test + fun debugSettings_convertsToCorrectProto() { + val correctConversions = { debugSettings: DebugSettings -> + debugSettingsProto { + isDebugModeEnabled = debugSettings.isDebugModeEnabled + debugSettings.singleLensMode?.let { + singleLensMode = it.toProto() + } + testPattern = debugSettings.testPattern.toProto() + } + } + + val debugSettingsToTest = listOf( + DebugSettings(), + DebugSettings(isDebugModeEnabled = true), + DebugSettings(singleLensMode = LensFacing.FRONT), + DebugSettings(testPattern = TestPattern.ColorBars), + DebugSettings( + isDebugModeEnabled = true, + singleLensMode = LensFacing.BACK, + testPattern = TestPattern.PN9 + ) + ) + + debugSettingsToTest.forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toProto()) + } + } + + @Test + fun debugSettingsProto_convertsToCorrectDebugSettings() { + val correctConversions = { debugSettingsProto: DebugSettingsProto -> + DebugSettings( + isDebugModeEnabled = debugSettingsProto.isDebugModeEnabled, + singleLensMode = if (debugSettingsProto.hasSingleLensMode()) { + debugSettingsProto.singleLensMode.toDomain() + } else { + null + }, + testPattern = debugSettingsProto.testPattern.toDomain() + ) + } + + val debugSettingsProtosToTest: List = listOf( + debugSettingsProto { }, + debugSettingsProto { isDebugModeEnabled = true }, + debugSettingsProto { singleLensMode = LensFacingProto.LENS_FACING_FRONT }, + debugSettingsProto { + testPattern = protoTestPattern { + colorBars = testPatternColorBars { } + } + }, + debugSettingsProto { + isDebugModeEnabled = true + singleLensMode = LensFacingProto.LENS_FACING_BACK + testPattern = protoTestPattern { + pn9 = testPatternPN9 { } + } + } + ) + + debugSettingsProtosToTest.forEach { + Truth.assertThat(correctConversions(it)).isEqualTo(it.toDomain()) + } + } +} diff --git a/data/settings-datastore/.gitignore b/data/settings-datastore/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/data/settings-datastore/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/data/settings-datastore/build.gradle.kts b/data/settings-datastore/build.gradle.kts new file mode 100644 index 000000000..fad531bfc --- /dev/null +++ b/data/settings-datastore/build.gradle.kts @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.google.protobuf) + alias(libs.plugins.kotlin.kapt) +} + +android { + namespace = "com.google.jetpackcamera.data.settingsdatastore" + compileSdk = 35 + + defaultConfig { + minSdk = 23 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + + // Hilt + implementation(libs.dagger.hilt.android) + kapt(libs.dagger.hilt.compiler) + + // proto datastore + implementation(libs.protobuf.kotlin.lite) + implementation(libs.androidx.datastore) + + // Access Model data + implementation(project(":core:common")) + implementation(project(":core:model")) + implementation(project(":data:settings")) + + // Testing + testImplementation(libs.junit) + testImplementation(libs.truth) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.truth) + androidTestImplementation(libs.kotlinx.coroutines.test) +} + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:3.21.12" + } + + generateProtoTasks { + all().forEach { task -> + task.builtins { + create("java") { + option("lite") + } + } + + task.builtins { + create("kotlin") { + option("lite") + } + } + } + } +} + +// Allow references to generated code +kapt { + correctErrorTypes = true +} diff --git a/data/settings-datastore/consumer-rules.pro b/data/settings-datastore/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/data/settings-datastore/proguard-rules.pro b/data/settings-datastore/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/data/settings-datastore/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/data/settings/src/androidTest/java/com/google/jetpackcamera/settings/DataStoreModuleTest.kt b/data/settings-datastore/src/androidTest/java/com/google/jetpackcamera/data/settings_datastore/DataStoreModuleTest.kt similarity index 100% rename from data/settings/src/androidTest/java/com/google/jetpackcamera/settings/DataStoreModuleTest.kt rename to data/settings-datastore/src/androidTest/java/com/google/jetpackcamera/data/settings_datastore/DataStoreModuleTest.kt diff --git a/data/settings/src/androidTest/java/com/google/jetpackcamera/settings/LocalSettingsRepositoryInstrumentedTest.kt b/data/settings-datastore/src/androidTest/java/com/google/jetpackcamera/data/settings_datastore/LocalSettingsRepositoryInstrumentedTest.kt similarity index 97% rename from data/settings/src/androidTest/java/com/google/jetpackcamera/settings/LocalSettingsRepositoryInstrumentedTest.kt rename to data/settings-datastore/src/androidTest/java/com/google/jetpackcamera/data/settings_datastore/LocalSettingsRepositoryInstrumentedTest.kt index fafb38dc3..c984ec1ac 100644 --- a/data/settings/src/androidTest/java/com/google/jetpackcamera/settings/LocalSettingsRepositoryInstrumentedTest.kt +++ b/data/settings-datastore/src/androidTest/java/com/google/jetpackcamera/data/settings_datastore/LocalSettingsRepositoryInstrumentedTest.kt @@ -22,6 +22,8 @@ import androidx.datastore.dataStoreFile import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat +import com.google.jetpackcamera.data.settingsdatastore.JcaSettingsSerializer +import com.google.jetpackcamera.data.settingsdatastore.LocalSettingsRepository import com.google.jetpackcamera.model.CaptureMode import com.google.jetpackcamera.model.DarkMode import com.google.jetpackcamera.model.DynamicRange diff --git a/data/settings-datastore/src/main/AndroidManifest.xml b/data/settings-datastore/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8322d22ca --- /dev/null +++ b/data/settings-datastore/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/data/settings/src/main/java/com/google/jetpackcamera/settings/DataStoreModule.kt b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/DataStoreModule.kt similarity index 91% rename from data/settings/src/main/java/com/google/jetpackcamera/settings/DataStoreModule.kt rename to data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/DataStoreModule.kt index 7f8862455..806eecb3d 100644 --- a/data/settings/src/main/java/com/google/jetpackcamera/settings/DataStoreModule.kt +++ b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/DataStoreModule.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2026 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.jetpackcamera.settings +package com.google.jetpackcamera.data.settingsdatastore import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.core.DataStoreFactory import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler import androidx.datastore.dataStoreFile +import com.google.jetpackcamera.settings.JcaSettings import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -30,7 +31,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob -// with hilt will ensure datastore instance access is unique per file @Module @InstallIn(SingletonComponent::class) object DataStoreModule { diff --git a/data/settings/src/main/java/com/google/jetpackcamera/settings/JcaSettingsSerializer.kt b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/JcaSettingsSerializer.kt similarity index 94% rename from data/settings/src/main/java/com/google/jetpackcamera/settings/JcaSettingsSerializer.kt rename to data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/JcaSettingsSerializer.kt index 81b6382d4..38fa5b92f 100644 --- a/data/settings/src/main/java/com/google/jetpackcamera/settings/JcaSettingsSerializer.kt +++ b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/JcaSettingsSerializer.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2026 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.jetpackcamera.settings +package com.google.jetpackcamera.data.settingsdatastore import androidx.datastore.core.CorruptionException import androidx.datastore.core.Serializer @@ -26,6 +26,7 @@ import com.google.jetpackcamera.model.proto.LensFacing import com.google.jetpackcamera.model.proto.StabilizationMode import com.google.jetpackcamera.model.proto.StreamConfig import com.google.jetpackcamera.model.proto.VideoQuality +import com.google.jetpackcamera.settings.JcaSettings import com.google.protobuf.InvalidProtocolBufferException import java.io.InputStream import java.io.OutputStream diff --git a/data/settings/src/main/java/com/google/jetpackcamera/settings/LocalSettingsRepository.kt b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/LocalSettingsRepository.kt similarity index 62% rename from data/settings/src/main/java/com/google/jetpackcamera/settings/LocalSettingsRepository.kt rename to data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/LocalSettingsRepository.kt index 7034f9eba..939068567 100644 --- a/data/settings/src/main/java/com/google/jetpackcamera/settings/LocalSettingsRepository.kt +++ b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/LocalSettingsRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2026 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.jetpackcamera.settings +package com.google.jetpackcamera.data.settingsdatastore import androidx.datastore.core.DataStore import com.google.jetpackcamera.core.common.DefaultCaptureModeOverride @@ -21,24 +21,18 @@ import com.google.jetpackcamera.model.AspectRatio import com.google.jetpackcamera.model.CaptureMode import com.google.jetpackcamera.model.DarkMode import com.google.jetpackcamera.model.DynamicRange -import com.google.jetpackcamera.model.DynamicRange.Companion.toProto import com.google.jetpackcamera.model.FlashMode import com.google.jetpackcamera.model.ImageOutputFormat -import com.google.jetpackcamera.model.ImageOutputFormat.Companion.toProto import com.google.jetpackcamera.model.LensFacing -import com.google.jetpackcamera.model.LensFacing.Companion.toProto import com.google.jetpackcamera.model.LowLightBoostPriority -import com.google.jetpackcamera.model.LowLightBoostPriority.Companion.fromProto -import com.google.jetpackcamera.model.LowLightBoostPriority.Companion.toProto import com.google.jetpackcamera.model.StabilizationMode import com.google.jetpackcamera.model.StreamConfig import com.google.jetpackcamera.model.VideoQuality -import com.google.jetpackcamera.model.VideoQuality.Companion.toProto -import com.google.jetpackcamera.model.proto.AspectRatio as AspectRatioProto +import com.google.jetpackcamera.model.mappers.toDomain +import com.google.jetpackcamera.model.mappers.toProto import com.google.jetpackcamera.model.proto.DarkMode as DarkModeProto -import com.google.jetpackcamera.model.proto.FlashMode as FlashModeProto -import com.google.jetpackcamera.model.proto.StabilizationMode as StabilizationModeProto -import com.google.jetpackcamera.model.proto.StreamConfig as StreamConfigProto +import com.google.jetpackcamera.settings.JcaSettings +import com.google.jetpackcamera.settings.SettingsRepository import com.google.jetpackcamera.settings.model.CameraAppSettings import javax.inject.Inject import kotlinx.coroutines.flow.first @@ -56,33 +50,23 @@ class LocalSettingsRepository @Inject constructor( override val defaultCameraAppSettings = jcaSettings.data .map { CameraAppSettings( - cameraLensFacing = LensFacing.fromProto(it.defaultLensFacing), + cameraLensFacing = it.defaultLensFacing.toDomain(), darkMode = when (it.darkModeStatus) { DarkModeProto.DARK_MODE_DARK -> DarkMode.DARK DarkModeProto.DARK_MODE_LIGHT -> DarkMode.LIGHT DarkModeProto.DARK_MODE_SYSTEM -> DarkMode.SYSTEM else -> DarkMode.DARK }, - flashMode = when (it.flashModeStatus) { - FlashModeProto.FLASH_MODE_AUTO -> FlashMode.AUTO - FlashModeProto.FLASH_MODE_ON -> FlashMode.ON - FlashModeProto.FLASH_MODE_OFF -> FlashMode.OFF - FlashModeProto.FLASH_MODE_LOW_LIGHT_BOOST -> FlashMode.LOW_LIGHT_BOOST - else -> FlashMode.OFF - }, - aspectRatio = AspectRatio.fromProto(it.aspectRatioStatus), - stabilizationMode = StabilizationMode.fromProto(it.stabilizationMode), + flashMode = it.flashModeStatus.toDomain(), + aspectRatio = it.aspectRatioStatus.toDomain(), + stabilizationMode = it.stabilizationMode.toDomain(), targetFrameRate = it.targetFrameRate, - streamConfig = when (it.streamConfigStatus) { - StreamConfigProto.STREAM_CONFIG_SINGLE_STREAM -> StreamConfig.SINGLE_STREAM - StreamConfigProto.STREAM_CONFIG_MULTI_STREAM -> StreamConfig.MULTI_STREAM - else -> StreamConfig.MULTI_STREAM - }, - lowLightBoostPriority = fromProto(it.lowLightBoostPriority), - dynamicRange = DynamicRange.fromProto(it.dynamicRangeStatus), - imageFormat = ImageOutputFormat.fromProto(it.imageFormatStatus), + streamConfig = it.streamConfigStatus.toDomain(), + lowLightBoostPriority = it.lowLightBoostPriority.toDomain(), + dynamicRange = it.dynamicRangeStatus.toDomain(), + imageFormat = it.imageFormatStatus.toDomain(), maxVideoDurationMillis = it.maxVideoDurationMillis, - videoQuality = VideoQuality.fromProto(it.videoQuality), + videoQuality = it.videoQuality.toDomain(), audioEnabled = it.audioEnabledStatus, captureMode = defaultCaptureModeOverride ) @@ -113,15 +97,9 @@ class LocalSettingsRepository @Inject constructor( } override suspend fun updateFlashModeStatus(flashMode: FlashMode) { - val newStatus = when (flashMode) { - FlashMode.AUTO -> FlashModeProto.FLASH_MODE_AUTO - FlashMode.ON -> FlashModeProto.FLASH_MODE_ON - FlashMode.OFF -> FlashModeProto.FLASH_MODE_OFF - FlashMode.LOW_LIGHT_BOOST -> FlashModeProto.FLASH_MODE_LOW_LIGHT_BOOST - } jcaSettings.updateData { currentSettings -> currentSettings.toBuilder() - .setFlashModeStatus(newStatus) + .setFlashModeStatus(flashMode.toProto()) .build() } } @@ -135,38 +113,23 @@ class LocalSettingsRepository @Inject constructor( } override suspend fun updateAspectRatio(aspectRatio: AspectRatio) { - val newStatus = when (aspectRatio) { - AspectRatio.NINE_SIXTEEN -> AspectRatioProto.ASPECT_RATIO_NINE_SIXTEEN - AspectRatio.THREE_FOUR -> AspectRatioProto.ASPECT_RATIO_THREE_FOUR - AspectRatio.ONE_ONE -> AspectRatioProto.ASPECT_RATIO_ONE_ONE - } jcaSettings.updateData { currentSettings -> currentSettings.toBuilder() - .setAspectRatioStatus(newStatus) + .setAspectRatioStatus(aspectRatio.toProto()) .build() } } override suspend fun updateStreamConfig(streamConfig: StreamConfig) { - val newStatus = when (streamConfig) { - StreamConfig.MULTI_STREAM -> StreamConfigProto.STREAM_CONFIG_MULTI_STREAM - StreamConfig.SINGLE_STREAM -> StreamConfigProto.STREAM_CONFIG_SINGLE_STREAM - } jcaSettings.updateData { currentSettings -> currentSettings.toBuilder() - .setStreamConfigStatus(newStatus) + .setStreamConfigStatus(streamConfig.toProto()) .build() } } override suspend fun updateStabilizationMode(stabilizationMode: StabilizationMode) { - val newStatus = when (stabilizationMode) { - StabilizationMode.OFF -> StabilizationModeProto.STABILIZATION_MODE_OFF - StabilizationMode.AUTO -> StabilizationModeProto.STABILIZATION_MODE_AUTO - StabilizationMode.ON -> StabilizationModeProto.STABILIZATION_MODE_ON - StabilizationMode.HIGH_QUALITY -> StabilizationModeProto.STABILIZATION_MODE_HIGH_QUALITY - StabilizationMode.OPTICAL -> StabilizationModeProto.STABILIZATION_MODE_OPTICAL - } + val newStatus = stabilizationMode.toProto() jcaSettings.updateData { currentSettings -> currentSettings.toBuilder() .setStabilizationMode(newStatus) @@ -189,6 +152,7 @@ class LocalSettingsRepository @Inject constructor( .build() } } + override suspend fun updateMaxVideoDuration(durationMillis: Long) { jcaSettings.updateData { currentSettings -> currentSettings.toBuilder() diff --git a/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/SettingsModule.kt b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/SettingsModule.kt new file mode 100644 index 000000000..5eef413dc --- /dev/null +++ b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/SettingsModule.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.jetpackcamera.data.settingsdatastore + +import com.google.jetpackcamera.settings.SettingsRepository +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +/** + * Dagger [Module] for settings data layer. + */ +@Module +@InstallIn(SingletonComponent::class) +interface SettingsModule { + + @Binds + fun bindsSettingsRepository( + localSettingsRepository: LocalSettingsRepository + ): SettingsRepository +} diff --git a/data/settings/src/main/java/com/google/jetpackcamera/settings/test/FakeDataStoreModule.kt b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/test/FakeDataStoreModule.kt similarity index 95% rename from data/settings/src/main/java/com/google/jetpackcamera/settings/test/FakeDataStoreModule.kt rename to data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/test/FakeDataStoreModule.kt index 1b04f53c7..ca830363e 100644 --- a/data/settings/src/main/java/com/google/jetpackcamera/settings/test/FakeDataStoreModule.kt +++ b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/test/FakeDataStoreModule.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.jetpackcamera.settings.test +package com.google.jetpackcamera.data.settingsdatastore.test import androidx.datastore.core.DataStore import androidx.datastore.core.DataStoreFactory diff --git a/data/settings/src/main/java/com/google/jetpackcamera/settings/test/FakeJcaSettingsSerializer.kt b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/test/FakeJcaSettingsSerializer.kt similarity index 93% rename from data/settings/src/main/java/com/google/jetpackcamera/settings/test/FakeJcaSettingsSerializer.kt rename to data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/test/FakeJcaSettingsSerializer.kt index 033f8ad62..6482de525 100644 --- a/data/settings/src/main/java/com/google/jetpackcamera/settings/test/FakeJcaSettingsSerializer.kt +++ b/data/settings-datastore/src/main/java/com/google/jetpackcamera/data/settingsdatastore/test/FakeJcaSettingsSerializer.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2026 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.jetpackcamera.settings.test +package com.google.jetpackcamera.data.settingsdatastore.test import androidx.datastore.core.CorruptionException import androidx.datastore.core.Serializer +import com.google.jetpackcamera.data.settingsdatastore.UNLIMITED_VIDEO_DURATION import com.google.jetpackcamera.model.proto.AspectRatio import com.google.jetpackcamera.model.proto.DarkMode import com.google.jetpackcamera.model.proto.DynamicRange @@ -27,7 +28,6 @@ import com.google.jetpackcamera.model.proto.StabilizationMode import com.google.jetpackcamera.model.proto.StreamConfig import com.google.jetpackcamera.model.proto.VideoQuality import com.google.jetpackcamera.settings.JcaSettings -import com.google.jetpackcamera.settings.UNLIMITED_VIDEO_DURATION import com.google.protobuf.InvalidProtocolBufferException import java.io.IOException import java.io.InputStream diff --git a/data/settings/src/main/proto/com/google/jetpackcamera/settings/jca_settings.proto b/data/settings-datastore/src/main/proto/com/google/jetpackcamera/settings/jca_settings.proto similarity index 100% rename from data/settings/src/main/proto/com/google/jetpackcamera/settings/jca_settings.proto rename to data/settings-datastore/src/main/proto/com/google/jetpackcamera/settings/jca_settings.proto diff --git a/data/settings/build.gradle.kts b/data/settings/build.gradle.kts index 8cb89eb9d..a37d23d7a 100644 --- a/data/settings/build.gradle.kts +++ b/data/settings/build.gradle.kts @@ -19,7 +19,6 @@ plugins { alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.kapt) alias(libs.plugins.dagger.hilt.android) - alias(libs.plugins.google.protobuf) } android { @@ -76,46 +75,7 @@ dependencies { implementation(libs.dagger.hilt.android) kapt(libs.dagger.hilt.compiler) - // proto datastore - implementation(libs.androidx.datastore) - implementation(libs.protobuf.kotlin.lite) - - // Testing - testImplementation(libs.junit) - testImplementation(libs.truth) - androidTestImplementation(libs.androidx.espresso.core) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.truth) - androidTestImplementation(libs.kotlinx.coroutines.test) - // Access Model data implementation(project(":core:model")) implementation(project(":core:common")) -} - -protobuf { - protoc { - artifact = "com.google.protobuf:protoc:3.21.12" - } - - generateProtoTasks { - all().forEach { task -> - task.builtins { - create("java") { - option("lite") - } - } - - task.builtins { - create("kotlin") { - option("lite") - } - } - } - } -} - -// Allow references to generated code -kapt { - correctErrorTypes = true -} +} \ No newline at end of file diff --git a/data/settings/src/main/java/com/google/jetpackcamera/settings/SettingsModule.kt b/data/settings/src/main/java/com/google/jetpackcamera/settings/ConstraintsModule.kt similarity index 87% rename from data/settings/src/main/java/com/google/jetpackcamera/settings/SettingsModule.kt rename to data/settings/src/main/java/com/google/jetpackcamera/settings/ConstraintsModule.kt index f523b5580..f6489a328 100644 --- a/data/settings/src/main/java/com/google/jetpackcamera/settings/SettingsModule.kt +++ b/data/settings/src/main/java/com/google/jetpackcamera/settings/ConstraintsModule.kt @@ -22,17 +22,11 @@ import dagger.hilt.components.SingletonComponent import javax.inject.Singleton /** - * Dagger [Module] for settings data layer. + * Dagger [Module] for constraints data layer. */ @Module @InstallIn(SingletonComponent::class) -interface SettingsModule { - - @Binds - fun bindsSettingsRepository( - localSettingsRepository: LocalSettingsRepository - ): SettingsRepository - +interface ConstraintsModule { @Binds @Singleton fun bindsSettableConstraintsRepository( diff --git a/data/settings/src/test/java/com/google/jetpackcamera/settings/ProtoConversionTest.kt b/data/settings/src/test/java/com/google/jetpackcamera/settings/ProtoConversionTest.kt deleted file mode 100644 index 5ce8f2643..000000000 --- a/data/settings/src/test/java/com/google/jetpackcamera/settings/ProtoConversionTest.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.jetpackcamera.settings - -import com.google.common.truth.Truth.assertThat -import com.google.jetpackcamera.model.DynamicRange -import com.google.jetpackcamera.model.DynamicRange.Companion.toProto -import com.google.jetpackcamera.model.ImageOutputFormat -import com.google.jetpackcamera.model.ImageOutputFormat.Companion.toProto -import com.google.jetpackcamera.model.proto.DynamicRange as DynamicRangeProto -import com.google.jetpackcamera.model.proto.ImageOutputFormat as ImageOutputFormatProto -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -@RunWith(JUnit4::class) -class ProtoConversionTest { - @Test - fun dynamicRange_convertsToCorrectProto() { - val correctConversions = { dynamicRange: DynamicRange -> - when (dynamicRange) { - DynamicRange.SDR -> DynamicRangeProto.DYNAMIC_RANGE_SDR - DynamicRange.HLG10 -> DynamicRangeProto.DYNAMIC_RANGE_HLG10 - else -> TODO( - "Test does not yet contain correct conversion for dynamic range " + - "type: ${dynamicRange.name}" - ) - } - } - - enumValues().forEach { - assertThat(correctConversions(it)).isEqualTo(it.toProto()) - } - } - - @Test - fun dynamicRangeProto_convertsToCorrectDynamicRange() { - val correctConversions = { dynamicRangeProto: DynamicRangeProto -> - when (dynamicRangeProto) { - DynamicRangeProto.DYNAMIC_RANGE_SDR, - DynamicRangeProto.UNRECOGNIZED, - DynamicRangeProto.DYNAMIC_RANGE_UNSPECIFIED - -> DynamicRange.SDR - - DynamicRangeProto.DYNAMIC_RANGE_HLG10 -> DynamicRange.HLG10 - else -> TODO( - "Test does not yet contain correct conversion for dynamic range " + - "proto type: ${dynamicRangeProto.name}" - ) - } - } - - enumValues().forEach { - assertThat(correctConversions(it)).isEqualTo(DynamicRange.fromProto(it)) - } - } - - @Test - fun imageOutputFormat_convertsToCorrectProto() { - val correctConversions = { imageOutputFormat: ImageOutputFormat -> - when (imageOutputFormat) { - ImageOutputFormat.JPEG -> ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG - ImageOutputFormat.JPEG_ULTRA_HDR - -> ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG_ULTRA_HDR - else -> TODO( - "Test does not yet contain correct conversion for image output format " + - "type: ${imageOutputFormat.name}" - ) - } - } - - enumValues().forEach { - assertThat(correctConversions(it)).isEqualTo(it.toProto()) - } - } - - @Test - fun imageOutputFormatProto_convertsToCorrectImageOutputFormat() { - val correctConversions = { imageOutputFormatProto: ImageOutputFormatProto -> - when (imageOutputFormatProto) { - ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG, - ImageOutputFormatProto.UNRECOGNIZED - -> ImageOutputFormat.JPEG - ImageOutputFormatProto.IMAGE_OUTPUT_FORMAT_JPEG_ULTRA_HDR - -> ImageOutputFormat.JPEG_ULTRA_HDR - else -> TODO( - "Test does not yet contain correct conversion for image output format " + - "proto type: ${imageOutputFormatProto.name}" - ) - } - } - - enumValues().forEach { - assertThat(correctConversions(it)).isEqualTo(ImageOutputFormat.fromProto(it)) - } - } -} diff --git a/feature/preview/build.gradle.kts b/feature/preview/build.gradle.kts index 60239e42d..9ddbadc6a 100644 --- a/feature/preview/build.gradle.kts +++ b/feature/preview/build.gradle.kts @@ -140,6 +140,7 @@ dependencies { implementation(project(":core:common")) implementation(project(":data:media")) implementation(project(":data:settings")) + implementation(project(":data:settings-datastore")) implementation(project(":core:model")) testImplementation(project(":core:common")) implementation(project(":ui:components:capture")) diff --git a/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/navigation/DebugSettingsNavType.kt b/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/navigation/DebugSettingsNavType.kt index d2c4c5dfa..dd83d8ea7 100644 --- a/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/navigation/DebugSettingsNavType.kt +++ b/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/navigation/DebugSettingsNavType.kt @@ -18,8 +18,10 @@ package com.google.jetpackcamera.feature.preview.navigation import android.os.Bundle import androidx.navigation.NavType import com.google.jetpackcamera.model.DebugSettings -import com.google.jetpackcamera.model.DebugSettings.Companion.encodeAsByteArray -import com.google.jetpackcamera.model.DebugSettings.Companion.encodeAsString +import com.google.jetpackcamera.model.mappers.DebugSettingsMapper.encodeAsByteArray +import com.google.jetpackcamera.model.mappers.DebugSettingsMapper.encodeAsString +import com.google.jetpackcamera.model.mappers.DebugSettingsMapper.parseFromByteArray +import com.google.jetpackcamera.model.mappers.DebugSettingsMapper.parseFromString /** * Custom NavType to handle DebugSettings data class. @@ -39,14 +41,14 @@ internal object DebugSettingsNavType : NavType(isNullableAllowed */ override fun get(bundle: Bundle, key: String): DebugSettings? { return bundle.getByteArray(key)?.let { bytes -> - DebugSettings.parseFromByteArray(bytes) + parseFromByteArray(bytes) } } /** * Parses the Base64 encoded Proto string from the navigation route. */ - override fun parseValue(value: String): DebugSettings = DebugSettings.parseFromString(value) + override fun parseValue(value: String): DebugSettings = parseFromString(value) /** * Encodes the [DebugSettings] data class to a Base64 string for navigation routes. diff --git a/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/navigation/PreviewNavigation.kt b/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/navigation/PreviewNavigation.kt index 60b91ba52..080cd6755 100644 --- a/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/navigation/PreviewNavigation.kt +++ b/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/navigation/PreviewNavigation.kt @@ -40,6 +40,7 @@ import com.google.jetpackcamera.model.CaptureEvent import com.google.jetpackcamera.model.DebugSettings import com.google.jetpackcamera.model.ExternalCaptureMode import com.google.jetpackcamera.model.SaveMode +import com.google.jetpackcamera.model.mappers.DebugSettingsMapper object PreviewRoute { internal const val ARG_EXTERNAL_CAPTURE_MODE: String = "externalCaptureMode" @@ -188,5 +189,5 @@ internal fun SavedStateHandle.getCaptureUris(defaultIfMissing: List = empty internal fun SavedStateHandle.getDebugSettings( defaultIfMissing: DebugSettings = DebugSettings() -): DebugSettings = get(ARG_DEBUG_SETTINGS)?.let(DebugSettings::parseFromByteArray) +): DebugSettings = get(ARG_DEBUG_SETTINGS)?.let(DebugSettingsMapper::parseFromByteArray) ?: defaultIfMissing diff --git a/settings.gradle.kts b/settings.gradle.kts index d20010988..c146957c0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -56,3 +56,4 @@ include(":core:model") include(":ui:uistate:postcapture") include(":ui:uistateadapter:postcapture") include(":core:camera:postprocess") +include(":data:settings-datastore")