1 /*
2 * 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 com.google.jetpackcamera.settings.DisabledRationale.DeviceUnsupportedRationale
19 import com.google.jetpackcamera.settings.DisabledRationale.LensUnsupportedRationale
20 import com.google.jetpackcamera.settings.model.AspectRatio
21 import com.google.jetpackcamera.settings.model.DEFAULT_CAMERA_APP_SETTINGS
22 import com.google.jetpackcamera.settings.model.DarkMode
23 import com.google.jetpackcamera.settings.model.FlashMode
24 import com.google.jetpackcamera.settings.model.LensFacing
25 import com.google.jetpackcamera.settings.model.StabilizationMode
26 import com.google.jetpackcamera.settings.model.StreamConfig
27 import com.google.jetpackcamera.settings.model.VideoQuality
28 import com.google.jetpackcamera.settings.ui.DEVICE_UNSUPPORTED_TAG
29 import com.google.jetpackcamera.settings.ui.FPS_UNSUPPORTED_TAG
30 import com.google.jetpackcamera.settings.ui.LENS_UNSUPPORTED_TAG
31 import com.google.jetpackcamera.settings.ui.PERMISSION_RECORD_AUDIO_NOT_GRANTED_TAG
32 import com.google.jetpackcamera.settings.ui.STABILIZATION_UNSUPPORTED_TAG
33 import com.google.jetpackcamera.settings.ui.VIDEO_QUALITY_UNSUPPORTED_TAG
34
35 // seconds duration in millis
36 const val UNLIMITED_VIDEO_DURATION = 0L
37 const val FIVE_SECONDS_DURATION = 5_000L
38 const val TEN_SECONDS_DURATION = 10_000L
39 const val THIRTY_SECONDS_DURATION = 30_000L
40 const val SIXTY_SECONDS_DURATION = 60_000L
41
42 /**
43 * Defines the current state of the [SettingsScreen].
44 */
45 sealed interface SettingsUiState {
46 data object Disabled : SettingsUiState
47 data class Enabled(
48 val aspectRatioUiState: AspectRatioUiState,
49 val streamConfigUiState: StreamConfigUiState,
50 val darkModeUiState: DarkModeUiState,
51 val flashUiState: FlashUiState,
52 val fpsUiState: FpsUiState,
53 val lensFlipUiState: FlipLensUiState,
54 val stabilizationUiState: StabilizationUiState,
55 val maxVideoDurationUiState: MaxVideoDurationUiState.Enabled,
56 val videoQualityUiState: VideoQualityUiState,
57 val audioUiState: AudioUiState
58 ) : SettingsUiState
59 }
60
61 /** State for the individual options on Popup dialog settings */
62 sealed interface SingleSelectableState {
63 data object Selectable : SingleSelectableState
64 data class Disabled(val disabledRationale: DisabledRationale) : SingleSelectableState
65 }
66
67 /** Contains information on why a setting is disabled */
68 // TODO(b/360921588): Display information on UI regarding disabled rationale
69 sealed interface DisabledRationale {
70 val affectedSettingNameResId: Int
71 val reasonTextResId: Int
72 val testTag: String
73
74 data class PermissionRecordAudioNotGrantedRationale(
75 override val affectedSettingNameResId: Int
76 ) : DisabledRationale {
77 override val reasonTextResId: Int = R.string.permission_record_audio_unsupported
78 override val testTag = PERMISSION_RECORD_AUDIO_NOT_GRANTED_TAG
79 }
80
81 /**
82 * Text will be [affectedSettingNameResId] is [R.string.device_unsupported]
83 */
84 data class DeviceUnsupportedRationale(override val affectedSettingNameResId: Int) :
85 DisabledRationale {
86 override val reasonTextResId: Int = R.string.device_unsupported
87 override val testTag = DEVICE_UNSUPPORTED_TAG
88 }
89
90 data class FpsUnsupportedRationale(
91 override val affectedSettingNameResId: Int,
92 val currentFps: Int
93 ) : DisabledRationale {
94 override val reasonTextResId: Int = R.string.fps_unsupported
95 override val testTag = FPS_UNSUPPORTED_TAG
96 }
97
98 data class StabilizationUnsupportedRationale(override val affectedSettingNameResId: Int) :
99 DisabledRationale {
100 override val reasonTextResId = R.string.stabilization_unsupported
101 override val testTag = STABILIZATION_UNSUPPORTED_TAG
102 }
103
104 data class VideoQualityUnsupportedRationale(
105 override val affectedSettingNameResId: Int,
106 val currentDynamicRange: Int = R.string.video_quality_rationale_suffix_default
107 ) : DisabledRationale {
108 override val reasonTextResId = R.string.video_quality_unsupported
109 override val testTag = VIDEO_QUALITY_UNSUPPORTED_TAG
110 }
111
112 sealed interface LensUnsupportedRationale : DisabledRationale {
113 data class FrontLensUnsupportedRationale(override val affectedSettingNameResId: Int) :
114 LensUnsupportedRationale {
115 override val reasonTextResId: Int = R.string.front_lens_unsupported
116 override val testTag = LENS_UNSUPPORTED_TAG
117 }
118
119 data class RearLensUnsupportedRationale(override val affectedSettingNameResId: Int) :
120 LensUnsupportedRationale {
121 override val reasonTextResId: Int = R.string.rear_lens_unsupported
122 override val testTag = LENS_UNSUPPORTED_TAG
123 }
124 }
125 }
126
getLensUnsupportedRationalenull127 fun getLensUnsupportedRationale(
128 lensFacing: LensFacing,
129 affectedSettingNameResId: Int
130 ): LensUnsupportedRationale = when (lensFacing) {
131 LensFacing.BACK -> LensUnsupportedRationale.RearLensUnsupportedRationale(
132 affectedSettingNameResId
133 )
134
135 LensFacing.FRONT -> LensUnsupportedRationale.FrontLensUnsupportedRationale(
136 affectedSettingNameResId
137 )
138 }
139
140 // ////////////////////////////////////////////////////////////
141 //
142 // Settings that currently depend on constraints
143 //
144 // ////////////////////////////////////////////////////////////
145
146 sealed interface FpsUiState {
147 data class Enabled(
148 val currentSelection: Int,
149 val fpsAutoState: SingleSelectableState,
150 val fpsFifteenState: SingleSelectableState,
151 val fpsThirtyState: SingleSelectableState,
152 val fpsSixtyState: SingleSelectableState,
153 // Contains text like "Selected FPS only supported by rear lens"
154 val additionalContext: String = ""
155 ) : FpsUiState
156
157 // FPS selection completely disabled. Cannot open dialog.
158 data class Disabled(val disabledRationale: DisabledRationale) : FpsUiState
159 }
160
161 sealed interface FlipLensUiState {
162 val currentLensFacing: LensFacing
163
164 data class Enabled(override val currentLensFacing: LensFacing) : FlipLensUiState
165
166 data class Disabled(
167 override val currentLensFacing: LensFacing,
168 val disabledRationale: DisabledRationale
169 ) : FlipLensUiState
170 }
171
172 sealed interface StabilizationUiState {
173 data class Enabled(
174 val currentStabilizationMode: StabilizationMode,
175 val stabilizationAutoState: SingleSelectableState,
176 val stabilizationOnState: SingleSelectableState,
177 val stabilizationHighQualityState: SingleSelectableState,
178 val stabilizationOpticalState: SingleSelectableState,
179 // Contains text like "Selected stabilization mode only supported by rear lens"
180 val additionalContext: String = ""
181 ) : StabilizationUiState
182
183 // Stabilization selection completely disabled. Cannot open dialog.
184 data class Disabled(val disabledRationale: DisabledRationale) : StabilizationUiState
185 }
186
187 sealed interface AudioUiState {
188
189 sealed interface Enabled : AudioUiState {
190 val additionalContext: String
191
192 data class On(override val additionalContext: String = "") : Enabled
193 data class Mute(override val additionalContext: String = "") : Enabled
194 }
195
196 data class Disabled(val disabledRationale: DisabledRationale) : AudioUiState
197 }
198
199 sealed interface FlashUiState {
200 data class Enabled(
201 val currentFlashMode: FlashMode,
202 val onSelectableState: SingleSelectableState,
203 val autoSelectableState: SingleSelectableState,
204 val lowLightSelectableState: SingleSelectableState,
205 val additionalContext: String = ""
206 ) : FlashUiState
207
208 data class Disabled(val disabledRationale: DisabledRationale) : FlashUiState
209 }
210
211 // ////////////////////////////////////////////////////////////
212 //
213 // Settings that DON'T currently depend on constraints
214 //
215 // ////////////////////////////////////////////////////////////
216
217 sealed interface AspectRatioUiState {
218 data class Enabled(val currentAspectRatio: AspectRatio, val additionalContext: String = "") :
219 AspectRatioUiState
220 }
221
222 sealed interface StreamConfigUiState {
223 data class Enabled(val currentStreamConfig: StreamConfig, val additionalContext: String = "") :
224 StreamConfigUiState
225 }
226
227 sealed interface DarkModeUiState {
228 data class Enabled(val currentDarkMode: DarkMode, val additionalContext: String = "") :
229 DarkModeUiState
230 }
231
232 sealed interface MaxVideoDurationUiState {
233 data class Enabled(val currentMaxDurationMillis: Long, val additionalContext: String = "") :
234 MaxVideoDurationUiState
235 }
236
237 sealed interface VideoQualityUiState {
238 data class Enabled(
239 val currentVideoQuality: VideoQuality,
240 val videoQualityAutoState: SingleSelectableState,
241 val videoQualitySDState: SingleSelectableState,
242 val videoQualityHDState: SingleSelectableState,
243 val videoQualityFHDState: SingleSelectableState,
244 val videoQualityUHDState: SingleSelectableState
245 ) : VideoQualityUiState {
getSelectableStatenull246 fun getSelectableState(videoQuality: VideoQuality): SingleSelectableState =
247 when (videoQuality) {
248 VideoQuality.UNSPECIFIED -> this.videoQualityAutoState
249 VideoQuality.SD -> this.videoQualitySDState
250 VideoQuality.HD -> this.videoQualityHDState
251 VideoQuality.FHD -> this.videoQualityFHDState
252 VideoQuality.UHD -> this.videoQualityUHDState
253 }
254 }
255
256 data class Disabled(val disabledRationale: DisabledRationale) : VideoQualityUiState
257 }
258
259 /**
260 * Settings Ui State for testing, based on Typical System Constraints.
261 * @see[com.google.jetpackcamera.settings.model.SystemConstraints]
262 */
263 val TYPICAL_SETTINGS_UISTATE = SettingsUiState.Enabled(
264 aspectRatioUiState = AspectRatioUiState.Enabled(DEFAULT_CAMERA_APP_SETTINGS.aspectRatio),
265 streamConfigUiState = StreamConfigUiState.Enabled(DEFAULT_CAMERA_APP_SETTINGS.streamConfig),
266 darkModeUiState = DarkModeUiState.Enabled(DEFAULT_CAMERA_APP_SETTINGS.darkMode),
267 audioUiState = if (DEFAULT_CAMERA_APP_SETTINGS.audioEnabled) {
268 AudioUiState.Enabled.On()
269 } else {
270 AudioUiState.Enabled.Mute()
271 },
272 flashUiState =
273 FlashUiState.Enabled(
274 currentFlashMode = DEFAULT_CAMERA_APP_SETTINGS.flashMode,
275 autoSelectableState = SingleSelectableState.Selectable,
276 onSelectableState = SingleSelectableState.Selectable,
277 lowLightSelectableState = SingleSelectableState.Disabled(
278 DeviceUnsupportedRationale(R.string.flash_llb_rationale_prefix)
279 )
280 ),
281 fpsUiState = FpsUiState.Enabled(
282 currentSelection = DEFAULT_CAMERA_APP_SETTINGS.targetFrameRate,
283 fpsAutoState = SingleSelectableState.Selectable,
284 fpsFifteenState = SingleSelectableState.Selectable,
285 fpsThirtyState = SingleSelectableState.Selectable,
286 fpsSixtyState = SingleSelectableState.Disabled(
287 DeviceUnsupportedRationale(R.string.fps_rationale_prefix)
288 )
289 ),
290 lensFlipUiState = FlipLensUiState.Enabled(DEFAULT_CAMERA_APP_SETTINGS.cameraLensFacing),
291 maxVideoDurationUiState = MaxVideoDurationUiState.Enabled(UNLIMITED_VIDEO_DURATION),
292 stabilizationUiState =
293 StabilizationUiState.Disabled(
294 DeviceUnsupportedRationale(R.string.stabilization_rationale_prefix)
295 ),
296 videoQualityUiState = VideoQualityUiState.Disabled(
297 DisabledRationale.VideoQualityUnsupportedRationale(R.string.video_quality_rationale_prefix)
298 )
299 )
300