@@ -19,6 +19,10 @@ import android.graphics.BitmapFactory
1919import android.util.Log
2020import androidx.camera.core.ImageProxy
2121import androidx.camera.view.PreviewView
22+ import androidx.compose.animation.core.animateDp
23+ import androidx.compose.animation.core.animateFloat
24+ import androidx.compose.animation.core.tween
25+ import androidx.compose.animation.core.updateTransition
2226import androidx.compose.foundation.Image
2327import androidx.compose.foundation.LocalIndication
2428import androidx.compose.foundation.background
@@ -32,6 +36,7 @@ import androidx.compose.foundation.layout.Row
3236import androidx.compose.foundation.layout.fillMaxSize
3337import androidx.compose.foundation.layout.fillMaxWidth
3438import androidx.compose.foundation.layout.height
39+ import androidx.compose.foundation.layout.offset
3540import androidx.compose.foundation.layout.padding
3641import androidx.compose.foundation.layout.size
3742import androidx.compose.foundation.layout.width
@@ -53,6 +58,7 @@ import androidx.compose.runtime.remember
5358import androidx.compose.runtime.setValue
5459import androidx.compose.ui.Alignment
5560import androidx.compose.ui.Modifier
61+ import androidx.compose.ui.draw.scale
5662import androidx.compose.ui.graphics.Color
5763import androidx.compose.ui.graphics.asImageBitmap
5864import androidx.compose.ui.platform.LocalConfiguration
@@ -73,6 +79,9 @@ data class CameraUiState(
7379 val captureState : CaptureState
7480)
7581
82+ const val CAPTURED_IMAGE_DISPLAY_DURATION = 1500L
83+ const val ANIMATION_DURATION = 200
84+
7685@Composable
7786fun CameraScreen (
7887 viewModel : MainViewModel ,
@@ -91,7 +100,7 @@ fun CameraScreen(
91100 val captureState by viewModel.captureState.collectAsStateWithLifecycle()
92101 if (captureState.isProcessed()) {
93102 LaunchedEffect (captureState) {
94- delay(1500 )
103+ delay(CAPTURED_IMAGE_DISPLAY_DURATION )
95104 viewModel.addProcessedImage()
96105 }
97106 }
@@ -138,37 +147,74 @@ private fun CameraScreenScaffold(
138147 onCapture : () -> Unit ,
139148 onFinalizePressed : () -> Unit ,
140149) {
141- Scaffold (
142- bottomBar = {
143- CameraScreenFooter (
144- pageList = pageList,
145- pageCount = cameraUiState.pageCount,
146- onFinalizePressed = onFinalizePressed,
147- )
148- }
149- ) { innerPadding ->
150- Box (modifier = Modifier .padding(bottom = innerPadding.calculateBottomPadding()).fillMaxSize()) {
151- CameraPreviewWithOverlay (cameraPreview, cameraUiState)
152- MessageBox (cameraUiState.liveAnalysisState.inferenceTime)
153- CaptureButton (
154- onClick = onCapture,
155- modifier = Modifier
156- .align(Alignment .BottomCenter )
157- .padding(16 .dp)
158- )
159- cameraUiState.captureState.processedImage?.let {
160- Surface (
161- color = Color .Black .copy(alpha = 0.3f ),
162- modifier = Modifier .fillMaxSize()
150+ Box {
151+ Scaffold (
152+ bottomBar = {
153+ CameraScreenFooter (
154+ pageList = pageList,
155+ pageCount = cameraUiState.pageCount,
156+ onFinalizePressed = onFinalizePressed,
163157 )
164- {}
165- Image (
166- bitmap = it.asImageBitmap(),
167- contentDescription = null ,
168- modifier = Modifier .fillMaxSize().padding(24 .dp)
158+ }
159+ ) { innerPadding ->
160+ Box (
161+ modifier = Modifier
162+ .padding(bottom = innerPadding.calculateBottomPadding())
163+ .fillMaxSize()
164+ ) {
165+ CameraPreviewWithOverlay (cameraPreview, cameraUiState)
166+ MessageBox (cameraUiState.liveAnalysisState.inferenceTime)
167+ CaptureButton (
168+ onClick = onCapture,
169+ modifier = Modifier
170+ .align(Alignment .BottomCenter )
171+ .padding(16 .dp)
169172 )
170173 }
171174 }
175+ CapturedImage (cameraUiState)
176+ }
177+ }
178+
179+ @Composable
180+ private fun CapturedImage (cameraUiState : CameraUiState ) {
181+ cameraUiState.captureState.processedImage?.let { image ->
182+ Surface (
183+ color = Color .Black .copy(alpha = 0.3f ),
184+ modifier = Modifier .fillMaxSize(),
185+ ) {}
186+
187+ var isAnimating by remember { mutableStateOf(false ) }
188+ LaunchedEffect (image) {
189+ delay(CAPTURED_IMAGE_DISPLAY_DURATION - ANIMATION_DURATION )
190+ isAnimating = true
191+ }
192+ val transition = updateTransition(targetState = isAnimating, label = " captureAnimation" )
193+ val targetOffsetX = 0 .dp // TODO real value
194+ val targetOffsetY = 200 .dp // TODO real value
195+
196+ val offsetX by transition.animateDp(
197+ transitionSpec = { tween(durationMillis = ANIMATION_DURATION ) },
198+ label = " offsetX"
199+ ) { if (it) targetOffsetX else 0 .dp }
200+ val offsetY by transition.animateDp(
201+ transitionSpec = { tween(durationMillis = ANIMATION_DURATION ) },
202+ label = " offsetY"
203+ ) { if (it) targetOffsetY else 0 .dp }
204+ val scale by transition.animateFloat(
205+ transitionSpec = { tween(durationMillis = ANIMATION_DURATION ) },
206+ label = " scale"
207+ ) { if (it) 0.3f else 1f }
208+
209+ Image (
210+ bitmap = image.asImageBitmap(),
211+ contentDescription = null ,
212+ modifier = Modifier
213+ .fillMaxSize()
214+ .padding(24 .dp)
215+ .offset(x = offsetX, y = offsetY - 100 .dp)
216+ .scale(scale)
217+ )
172218 }
173219}
174220
0 commit comments