Skip to content

Commit 0116abd

Browse files
committed
Squashed commit of the following:
commit 2a1413c Author: Erick Lopez Solis <[email protected]> Date: Tue Jun 18 10:08:20 2024 -0700 `Popup:` Adds a file viewer for image attachments (#455) commit 2223e8e Author: Erick Lopez Solis <[email protected]> Date: Fri Jun 14 10:11:06 2024 -0700 `Popup:` Fixes TextPopupElement composable shifting in size when coming back into visibility (#461)
1 parent 326b238 commit 0116abd

File tree

7 files changed

+74
-67
lines changed

7 files changed

+74
-67
lines changed

toolkit/popup/src/main/AndroidManifest.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<application>
2323
<provider
2424
android:name="androidx.core.content.FileProvider"
25-
android:authorities="${applicationId}.fileprovider"
25+
android:authorities="${applicationId}.arcgis.popup.fileprovider"
2626
android:exported="false"
2727
android:grantUriPermissions="true">
2828
<meta-data

toolkit/popup/src/main/java/com/arcgismaps/toolkit/popup/Popup.kt

+16-9
Original file line numberDiff line numberDiff line change
@@ -161,36 +161,43 @@ private fun PopupBody(popupState: PopupState, onFileClicked: (ViewableFile?) ->
161161
) {
162162
states.forEach { entry ->
163163
val element = entry.popupElement
164-
item {
165-
when (element) {
166-
is TextPopupElement -> {
164+
when (element) {
165+
is TextPopupElement -> {
166+
// a contentType is needed to reuse the TextPopupElement composable inside a LazyColumn
167+
item(contentType = TextPopupElement::class.java) {
167168
TextPopupElement(
168169
entry.state as TextElementState
169170
)
170171
}
172+
}
171173

172-
is AttachmentsPopupElement -> {
174+
is AttachmentsPopupElement -> {
175+
item(contentType = AttachmentsPopupElement::class.java) {
173176
AttachmentsPopupElement(
174177
state = entry.state as AttachmentsElementState,
175178
onFileClicked
176179
)
177180
}
181+
}
178182

179-
is FieldsPopupElement -> {
183+
is FieldsPopupElement -> {
184+
item(contentType = FieldsPopupElement::class.java) {
180185
FieldsPopupElement(
181186
entry.state as FieldsElementState,
182187
)
183188
}
189+
}
184190

185-
is MediaPopupElement -> {
191+
is MediaPopupElement -> {
192+
item(contentType = MediaPopupElement::class.java) {
186193
MediaPopupElement(
187194
entry.state as MediaElementState
188195
)
189196
}
197+
}
190198

191-
else -> {
192-
// other popup elements are not created
193-
}
199+
else -> {
200+
// other popup elements are not created
194201
}
195202
}
196203
}

toolkit/popup/src/main/java/com/arcgismaps/toolkit/popup/internal/element/attachment/AttachmentTile.kt

-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ internal fun AttachmentTile(
9898
)
9999
)
100100
}
101-
// TODO open attachment viewer in `else` here
102101
}
103102
) {
104103
when (loadStatus) {

toolkit/popup/src/main/java/com/arcgismaps/toolkit/popup/internal/element/textelement/TextPopupElement.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,12 @@ private fun HTML(content: String) {
8181
val completeHtml = "$header$headStyle<html>${content.trim()}</html>"
8282
loadDataWithBaseURL(null, completeHtml, "text/html", "UTF-8", null)
8383
}
84-
})
84+
},
85+
// By default, AndroidViews aren't reused in a lazy list. This means that the `HTML` composable instance will
86+
// get discarded and recreated every time. By defining an `onReset` lambda, we can ensure tha the AndroidView will
87+
// be reused when the composition hierarchy changes.
88+
onReset = {}
89+
)
8590
}
8691

8792
/**

toolkit/popup/src/main/java/com/arcgismaps/toolkit/popup/internal/ui/fileviewer/FileViewer.kt

+38-52
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,24 @@ import androidx.compose.material3.MaterialTheme
3939
import androidx.compose.material3.Scaffold
4040
import androidx.compose.material3.Text
4141
import androidx.compose.runtime.Composable
42-
import androidx.compose.runtime.DisposableEffect
4342
import androidx.compose.runtime.mutableStateOf
4443
import androidx.compose.runtime.remember
4544
import androidx.compose.runtime.rememberCoroutineScope
4645
import androidx.compose.ui.Alignment
4746
import androidx.compose.ui.Modifier
4847
import androidx.compose.ui.platform.LocalContext
48+
import androidx.compose.ui.res.stringResource
4949
import androidx.compose.ui.tooling.preview.Preview
5050
import androidx.compose.ui.unit.dp
51-
import androidx.compose.ui.viewinterop.AndroidView
5251
import androidx.compose.ui.window.Dialog
5352
import androidx.compose.ui.window.DialogProperties
54-
import androidx.media3.common.MediaItem
55-
import androidx.media3.exoplayer.ExoPlayer
56-
import androidx.media3.ui.PlayerView
53+
import coil.compose.AsyncImage
54+
import com.arcgismaps.toolkit.popup.R
5755
import kotlinx.coroutines.CoroutineScope
58-
import kotlinx.coroutines.Dispatchers
5956
import kotlinx.coroutines.launch
60-
import kotlinx.coroutines.withContext
6157

6258
/**
63-
* A file viewer that can display different type of images.
59+
* A file viewer that can display different type of files.
6460
*
6561
* @since 200.5.0
6662
*/
@@ -83,7 +79,7 @@ internal fun FileViewer(scope: CoroutineScope, fileState: ViewableFile, onDismis
8379
IconButton(onClick = { onDismissRequest() }) {
8480
Icon(
8581
Icons.Rounded.Close,
86-
contentDescription = "Back",
82+
contentDescription = stringResource(id = R.string.close),
8783
tint = MaterialTheme.colorScheme.onSurface
8884
)
8985
}
@@ -112,8 +108,14 @@ internal fun FileViewer(scope: CoroutineScope, fileState: ViewableFile, onDismis
112108
contentAlignment = Alignment.Center
113109
) {
114110
when (fileState.type) {
115-
is ViewableFileType.Image -> ImageViewer(fileState.path)
116-
is ViewableFileType.Video -> VideoViewer(fileState.path)
111+
is ViewableFileType.Image ->
112+
AsyncImage(
113+
modifier = Modifier.fillMaxSize(),
114+
model = fileState.path,
115+
contentDescription = stringResource(id = R.string.image),
116+
)
117+
118+
is ViewableFileType.Video -> Text("Video")
117119
is ViewableFileType.Other -> Text("Other")
118120
}
119121
}
@@ -131,70 +133,54 @@ private fun ViewerActions(
131133
val expanded = remember { mutableStateOf(false) }
132134
Box(modifier = modifier) {
133135
IconButton(onClick = { expanded.value = true }) {
134-
Icon(Icons.Rounded.MoreVert, contentDescription = "More", tint = MaterialTheme.colorScheme.onSurface)
136+
Icon(
137+
Icons.Rounded.MoreVert,
138+
contentDescription = stringResource(id = R.string.more),
139+
tint = MaterialTheme.colorScheme.onSurface
140+
)
135141
}
136142

137143
DropdownMenu(expanded = expanded.value, onDismissRequest = { expanded.value = false }) {
138144
DropdownMenuItem(
139-
text = { Text("Share", color = MaterialTheme.colorScheme.onSurface) },
145+
text = { Text(stringResource(id = R.string.more), color = MaterialTheme.colorScheme.onSurface) },
140146
onClick = {
141147
expanded.value = false
142-
coroutineScope.launch(Dispatchers.IO) { viewableFile.share(context) }
148+
coroutineScope.launch { viewableFile.share(context) }
143149
},
144150
leadingIcon = {
145-
Icon(Icons.Rounded.Share, contentDescription = "Share", tint = MaterialTheme.colorScheme.onSurface)
151+
Icon(
152+
Icons.Rounded.Share,
153+
contentDescription = stringResource(id = R.string.share),
154+
tint = MaterialTheme.colorScheme.onSurface
155+
)
146156
}
147157
)
148158

149159
DropdownMenuItem(
150160
text = {
151-
Text("Save", color = MaterialTheme.colorScheme.onSurface)
161+
Text(text = stringResource(id = R.string.save), color = MaterialTheme.colorScheme.onSurface)
152162
},
153163
onClick = {
154164
expanded.value = false
155-
coroutineScope.launch(Dispatchers.IO) {
165+
coroutineScope.launch {
156166
val saveResult = viewableFile.saveToDevice(context)
157-
withContext(Dispatchers.Main) {
158-
saveResult.onSuccess {
159-
Toast.makeText(context, "Save successful", Toast.LENGTH_SHORT).show()
160-
}.onFailure {
161-
Toast.makeText(context, "Save failed", Toast.LENGTH_SHORT).show()
162-
Log.e("ArcGISMapsSDK", "Failed to save file: $it")
163-
}
167+
saveResult.onSuccess {
168+
Toast.makeText(context, context.getString(R.string.save_successful), Toast.LENGTH_SHORT)
169+
.show()
170+
}.onFailure {
171+
Toast.makeText(context, context.getString(R.string.save_failed), Toast.LENGTH_SHORT).show()
172+
Log.e("ArcGISMapsSDK", "Failed to save file: $it")
164173
}
165174
}
166175
},
167176
leadingIcon = {
168-
Icon(Icons.Rounded.Save, contentDescription = "Save", tint = MaterialTheme.colorScheme.onSurface)
169-
}
170-
)
171-
}
172-
}
173-
}
174-
175-
@Composable
176-
internal fun VideoViewer(path: String) {
177-
val context = LocalContext.current
178-
val exoPlayer = remember {
179-
ExoPlayer.Builder(context).build().apply {
180-
val mediaItem = MediaItem.Builder()
181-
.setUri(path)
182-
.build()
183-
setMediaItem(mediaItem)
184-
prepare()
185-
}
186-
}
187-
188-
AndroidView(
189-
factory = {
190-
PlayerView(context).apply {
191-
player = exoPlayer
192-
}
177+
Icon(
178+
Icons.Rounded.Save,
179+
contentDescription = stringResource(id = R.string.save),
180+
tint = MaterialTheme.colorScheme.onSurface
181+
)
193182
}
194183
)
195-
DisposableEffect(Unit) {
196-
onDispose {
197-
exoPlayer.release()
198184
}
199185
}
200186
}

toolkit/popup/src/main/java/com/arcgismaps/toolkit/popup/internal/ui/fileviewer/ViewableFile.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import android.os.Parcelable
2626
import android.provider.MediaStore
2727
import androidx.core.content.FileProvider
2828
import com.arcgismaps.mapping.popup.PopupAttachmentType
29+
import com.arcgismaps.toolkit.popup.R
2930
import kotlinx.coroutines.Dispatchers
3031
import kotlinx.coroutines.withContext
3132
import kotlinx.parcelize.Parceler
@@ -130,7 +131,7 @@ internal suspend fun ViewableFile.share(context: Context) = withContext(Dispatch
130131

131132
val uri = FileProvider.getUriForFile(
132133
context.applicationContext,
133-
"${context.applicationContext.applicationInfo.packageName}.fileprovider",
134+
"${context.applicationContext.applicationInfo.packageName}.arcgis.popup.fileprovider",
134135
file
135136
)
136137
val intent = Intent().apply {
@@ -141,6 +142,6 @@ internal suspend fun ViewableFile.share(context: Context) = withContext(Dispatch
141142
}
142143

143144
context.startActivity(
144-
Intent.createChooser(intent, "Share")
145+
Intent.createChooser(intent, context.getString(R.string.share))
145146
)
146147
}

toolkit/popup/src/main/res/values/strings.xml

+10-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,13 @@
1616

1717
<resources>
1818
<string name="show_or_hide_popup_element_content">expand popup element content</string>
19-
</resources>
19+
<string name="share">Share</string>
20+
<string name="save">Save</string>
21+
<string name="save_successful">Save successful</string>
22+
<string name="save_failed">Save failed</string>
23+
<string name="more">More</string>
24+
<string name="image">Image</string>
25+
<string name="video">Video</string>
26+
<string name="other">Other</string>
27+
<string name="close">Back</string>
28+
</resources>

0 commit comments

Comments
 (0)