Skip to content

Commit e5601c2

Browse files
Add screen for PhotoTaskFragment
This uses UriImage compose component for loading images asynchronously
1 parent d63f5c9 commit e5601c2

2 files changed

Lines changed: 165 additions & 0 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright 2026 Google LLC
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+
* https://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 org.groundplatform.android.ui.datacollection.tasks.photo
17+
18+
import android.net.Uri
19+
import androidx.compose.foundation.layout.Box
20+
import androidx.compose.foundation.layout.Spacer
21+
import androidx.compose.foundation.layout.fillMaxWidth
22+
import androidx.compose.foundation.layout.padding
23+
import androidx.compose.foundation.layout.size
24+
import androidx.compose.material3.ButtonDefaults
25+
import androidx.compose.material3.FilledTonalButton
26+
import androidx.compose.material3.Icon
27+
import androidx.compose.material3.Text
28+
import androidx.compose.runtime.Composable
29+
import androidx.compose.ui.Modifier
30+
import androidx.compose.ui.graphics.vector.ImageVector
31+
import androidx.compose.ui.res.stringResource
32+
import androidx.compose.ui.res.vectorResource
33+
import androidx.compose.ui.tooling.preview.Preview
34+
import androidx.compose.ui.unit.dp
35+
import androidx.core.net.toUri
36+
import org.groundplatform.android.R
37+
import org.groundplatform.android.ui.common.ExcludeFromJacocoGeneratedReport
38+
import org.groundplatform.android.ui.datacollection.components.UriImage
39+
import org.groundplatform.android.ui.theme.AppTheme
40+
41+
@Composable
42+
fun PhotoTaskScreen(
43+
isPhotoPresent: Boolean,
44+
uri: Uri?,
45+
onTakePhoto: () -> Unit,
46+
modifier: Modifier = Modifier,
47+
) {
48+
Box(modifier = modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
49+
if (!isPhotoPresent) {
50+
CaptureButton(onTakePhoto)
51+
} else {
52+
UriImage(uri = uri, modifier = Modifier.fillMaxWidth().padding(top = 4.dp))
53+
}
54+
}
55+
}
56+
57+
@Composable
58+
private fun CaptureButton(onTakePhoto: () -> Unit) {
59+
FilledTonalButton(
60+
onClick = onTakePhoto,
61+
contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
62+
) {
63+
Icon(
64+
imageVector = ImageVector.vectorResource(id = R.drawable.outline_photo_camera),
65+
contentDescription = stringResource(id = R.string.camera),
66+
modifier = Modifier.size(ButtonDefaults.IconSize),
67+
)
68+
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
69+
Text(text = stringResource(id = R.string.camera))
70+
}
71+
}
72+
73+
@Preview(showBackground = true)
74+
@Composable
75+
@ExcludeFromJacocoGeneratedReport
76+
private fun PhotoTaskScreenPreviewEmpty() {
77+
AppTheme { PhotoTaskScreen(isPhotoPresent = false, uri = null, onTakePhoto = {}) }
78+
}
79+
80+
@Preview(showBackground = true)
81+
@Composable
82+
@ExcludeFromJacocoGeneratedReport
83+
private fun PhotoTaskScreenPreviewWithPhoto() {
84+
AppTheme {
85+
PhotoTaskScreen(
86+
isPhotoPresent = true,
87+
uri = "content://media/external/images/media/1".toUri(),
88+
onTakePhoto = {},
89+
)
90+
}
91+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2026 Google LLC
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+
* https://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 org.groundplatform.android.ui.datacollection.tasks.photo
17+
18+
import androidx.compose.ui.test.assertIsDisplayed
19+
import androidx.compose.ui.test.onNodeWithContentDescription
20+
import androidx.compose.ui.test.onNodeWithText
21+
import androidx.compose.ui.test.performClick
22+
import androidx.core.net.toUri
23+
import com.google.common.truth.Truth.assertThat
24+
import dagger.hilt.android.testing.HiltAndroidTest
25+
import org.groundplatform.android.BaseHiltTest
26+
import org.junit.Test
27+
import org.junit.runner.RunWith
28+
import org.robolectric.RobolectricTestRunner
29+
import org.robolectric.annotation.Config
30+
31+
@HiltAndroidTest
32+
@RunWith(RobolectricTestRunner::class)
33+
@Config(qualifiers = "w600dp-h1024dp")
34+
class PhotoTaskScreenTest : BaseHiltTest() {
35+
36+
@Test
37+
fun `shows capture button when photo is not present`() {
38+
composeTestRule.setContent {
39+
PhotoTaskScreen(isPhotoPresent = false, uri = null, onTakePhoto = {})
40+
}
41+
42+
composeTestRule.onNodeWithText("Camera").assertIsDisplayed()
43+
}
44+
45+
@Test
46+
fun `shows photo preview when photo is present`() {
47+
composeTestRule.setContent {
48+
PhotoTaskScreen(
49+
isPhotoPresent = true,
50+
uri = "content://media/external/images/media/1".toUri(),
51+
onTakePhoto = {},
52+
)
53+
}
54+
55+
composeTestRule.onNodeWithContentDescription("Preview").assertIsDisplayed()
56+
}
57+
58+
@Test
59+
fun `invokes onTakePhoto callback when capture button is clicked`() {
60+
var onTakePhotoCalled = false
61+
62+
composeTestRule.setContent {
63+
PhotoTaskScreen(
64+
isPhotoPresent = false,
65+
uri = null,
66+
onTakePhoto = { onTakePhotoCalled = true },
67+
)
68+
}
69+
70+
composeTestRule.onNodeWithText("Camera").performClick()
71+
72+
assertThat(onTakePhotoCalled).isTrue()
73+
}
74+
}

0 commit comments

Comments
 (0)