• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2025 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.postcapture
17 
18 import android.content.Context
19 import android.content.Intent
20 import android.net.Uri
21 import androidx.compose.foundation.Canvas
22 import androidx.compose.foundation.layout.Arrangement
23 import androidx.compose.foundation.layout.Box
24 import androidx.compose.foundation.layout.Row
25 import androidx.compose.foundation.layout.Spacer
26 import androidx.compose.foundation.layout.fillMaxSize
27 import androidx.compose.foundation.layout.fillMaxWidth
28 import androidx.compose.foundation.layout.padding
29 import androidx.compose.foundation.layout.size
30 import androidx.compose.foundation.shape.CircleShape
31 import androidx.compose.material.icons.Icons
32 import androidx.compose.material.icons.filled.Delete
33 import androidx.compose.material.icons.filled.Share
34 import androidx.compose.material3.Icon
35 import androidx.compose.material3.IconButton
36 import androidx.compose.material3.IconButtonDefaults
37 import androidx.compose.material3.MaterialTheme
38 import androidx.compose.material3.Text
39 import androidx.compose.runtime.Composable
40 import androidx.compose.runtime.LaunchedEffect
41 import androidx.compose.runtime.collectAsState
42 import androidx.compose.runtime.getValue
43 import androidx.compose.runtime.remember
44 import androidx.compose.ui.Alignment
45 import androidx.compose.ui.Modifier
46 import androidx.compose.ui.draw.shadow
47 import androidx.compose.ui.geometry.Size
48 import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
49 import androidx.compose.ui.graphics.nativeCanvas
50 import androidx.compose.ui.platform.LocalContext
51 import androidx.compose.ui.unit.dp
52 import androidx.hilt.navigation.compose.hiltViewModel
53 import com.google.jetpackcamera.core.common.loadAndRotateBitmap
54 
55 @Composable
56 fun PostCaptureScreen(viewModel: PostCaptureViewModel = hiltViewModel(), imageUri: Uri?) {
57     val uiState: PostCaptureUiState by viewModel.uiState.collectAsState()
58     val context = LocalContext.current
59 
60     LaunchedEffect(imageUri) {
61         viewModel.setLastCapturedImageUri(imageUri)
62     }
63 
64     Box(modifier = Modifier.fillMaxSize()) {
65         uiState.imageUri?.let { uri ->
66             val bitmap = remember(uri) {
67                 // TODO(yasith): Get the image rotation from the image
68                 loadAndRotateBitmap(context, uri, 270f)
69             }
70 
71             if (bitmap != null) {
72                 Canvas(modifier = Modifier.fillMaxSize()) {
73                     drawIntoCanvas { canvas ->
74                         val scale = maxOf(
75                             size.width / bitmap.width,
76                             size.height / bitmap.height
77                         )
78                         val imageSize = Size(bitmap.width * scale, bitmap.height * scale)
79                         canvas.nativeCanvas.drawBitmap(
80                             bitmap,
81                             null,
82                             android.graphics.RectF(
83                                 0f,
84                                 0f,
85                                 imageSize.width,
86                                 imageSize.height
87                             ),
88                             null
89                         )
90                     }
91                 }
92             }
93         } ?: Text(
94             text = "No Image Captured",
95             modifier = Modifier.align(Alignment.Center)
96         )
97 
98         Row(
99             modifier = Modifier
100                 .fillMaxWidth()
101                 .align(Alignment.BottomCenter)
102                 .padding(16.dp),
103             horizontalArrangement = Arrangement.SpaceAround
104         ) {
105             // Delete Image Button
106             IconButton(
107                 onClick = { viewModel.deleteImage(context.contentResolver) },
108                 modifier = Modifier
109                     .size(56.dp)
110                     .shadow(10.dp, CircleShape),
111                 colors = IconButtonDefaults.iconButtonColors(
112                     containerColor = MaterialTheme.colorScheme.surface
113                 )
114             ) {
115                 Icon(
116                     imageVector = Icons.Default.Delete,
117                     contentDescription = "Delete",
118                     tint = MaterialTheme.colorScheme.onSurface
119                 )
120             }
121 
122             Spacer(modifier = Modifier.weight(1f))
123 
124             // Share Image Button
125             IconButton(
126                 onClick = {
127                     imageUri?.let {
128                         shareImage(context, it)
129                     }
130                 },
131                 modifier = Modifier
132                     .size(56.dp)
133                     .shadow(10.dp, CircleShape),
134                 colors = IconButtonDefaults.iconButtonColors(
135                     containerColor = MaterialTheme.colorScheme.surface
136                 )
137             ) {
138                 Icon(
139                     imageVector = Icons.Default.Share,
140                     contentDescription = "Share",
141                     tint = MaterialTheme.colorScheme.onSurface
142                 )
143             }
144         }
145     }
146 }
147 
148 /**
149  * Starts an intent to share an image
150  *
151  * @param context The application context
152  * @param imagePath The path to the image to share
153  */
shareImagenull154 private fun shareImage(context: Context, uri: Uri) {
155     val intent = Intent(Intent.ACTION_SEND).apply {
156         type = "image/jpeg"
157         putExtra(Intent.EXTRA_STREAM, uri)
158     }
159     intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
160     context.startActivity(Intent.createChooser(intent, "Share Image"))
161 }
162