1 /*
2 * Copyright (C) 2024 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.feature.preview.ui
17
18 import android.app.Activity
19 import android.util.Log
20 import android.view.Window
21 import android.view.WindowManager
22 import androidx.compose.animation.core.animateFloatAsState
23 import androidx.compose.animation.core.tween
24 import androidx.compose.foundation.background
25 import androidx.compose.foundation.layout.Box
26 import androidx.compose.foundation.layout.fillMaxSize
27 import androidx.compose.runtime.Composable
28 import androidx.compose.runtime.LaunchedEffect
29 import androidx.compose.runtime.getValue
30 import androidx.compose.runtime.remember
31 import androidx.compose.ui.Modifier
32 import androidx.compose.ui.graphics.Color
33 import androidx.compose.ui.platform.LocalContext
34 import androidx.compose.ui.platform.testTag
35 import com.google.jetpackcamera.feature.preview.ScreenFlash
36
37 private const val TAG = "ScreenFlashComponents"
38
39 @Composable
ScreenFlashScreennull40 fun ScreenFlashScreen(
41 screenFlashUiState: ScreenFlash.ScreenFlashUiState,
42 onInitialBrightnessCalculated: (Float) -> Unit
43 ) {
44 ScreenFlashOverlay(screenFlashUiState)
45
46 if (screenFlashUiState.enabled) {
47 BrightnessMaximization(onInitialBrightnessCalculated = onInitialBrightnessCalculated)
48 } else {
49 screenFlashUiState.screenBrightnessToRestore?.let {
50 // non-null brightness value means there is a value to restore
51 BrightnessRestoration(
52 brightness = it
53 )
54 }
55 }
56 }
57
58 @Composable
ScreenFlashOverlaynull59 fun ScreenFlashOverlay(
60 screenFlashUiState: ScreenFlash.ScreenFlashUiState,
61 modifier: Modifier = Modifier
62 ) {
63 // Update overlay transparency gradually
64 val alpha by animateFloatAsState(
65 targetValue = if (screenFlashUiState.enabled) 1f else 0f,
66 label = "screenFlashAlphaAnimation",
67 animationSpec = tween(),
68 finishedListener = { screenFlashUiState.onChangeComplete() }
69 )
70 Box(
71 modifier = modifier
72 .run {
73 if (screenFlashUiState.enabled) {
74 this.testTag(SCREEN_FLASH_OVERLAY)
75 } else {
76 this
77 }
78 }
79 .fillMaxSize()
80 .background(color = Color.White.copy(alpha = alpha))
81 )
82 }
83
84 @Composable
BrightnessMaximizationnull85 fun BrightnessMaximization(onInitialBrightnessCalculated: (Float) -> Unit) {
86 // This Composable is attached to Activity in current code, so will have Activity context.
87 // If the Composable is attached to somewhere else in future, this needs to be updated too.
88 val activity = LocalContext.current as? Activity ?: run {
89 Log.e(TAG, "ScreenBrightness: could not find Activity context")
90 return
91 }
92
93 val initialScreenBrightness = remember {
94 getScreenBrightness(activity.window)
95 }
96 LaunchedEffect(initialScreenBrightness) {
97 onInitialBrightnessCalculated(initialScreenBrightness)
98 }
99
100 LaunchedEffect(Unit) {
101 setBrightness(activity, WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL)
102 }
103 }
104
105 @Composable
BrightnessRestorationnull106 fun BrightnessRestoration(brightness: Float) {
107 // This Composable is attached to Activity right now, so will have Activity context.
108 // If the Composable is attached to somewhere else in future, this needs to be updated too.
109 val activity = LocalContext.current as? Activity ?: run {
110 Log.e(TAG, "ScreenBrightness: could not find Activity context")
111 return
112 }
113
114 LaunchedEffect(brightness) {
115 setBrightness(activity, brightness)
116 }
117 }
118
getScreenBrightnessnull119 fun getScreenBrightness(window: Window): Float = window.attributes.screenBrightness
120
121 fun setBrightness(activity: Activity, value: Float) {
122 Log.d(TAG, "setBrightness: value = $value")
123 val layoutParams: WindowManager.LayoutParams = activity.window.attributes
124 layoutParams.screenBrightness = value
125 activity.window.attributes = layoutParams
126 }
127