Skip to content

popup media element and media image thumbnail support #449

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions toolkit/popup/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ apiValidation {

dependencies {
api(arcgis.mapsSdk)
implementation(platform(libs.coil.bom))
implementation(libs.coil.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.bundles.composeCore)
implementation(libs.bundles.core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.arcgismaps.mapping.popup.FieldsPopupElement
import com.arcgismaps.mapping.popup.AttachmentsPopupElement
import com.arcgismaps.mapping.popup.FieldsPopupElement
import com.arcgismaps.mapping.popup.MediaPopupElement
import com.arcgismaps.mapping.popup.Popup
import com.arcgismaps.mapping.popup.TextPopupElement
import com.arcgismaps.toolkit.popup.internal.element.attachment.AttachmentsElementState
Expand All @@ -56,6 +57,9 @@ import com.arcgismaps.toolkit.popup.internal.element.attachment.rememberAttachme
import com.arcgismaps.toolkit.popup.internal.element.fieldselement.FieldsElementState
import com.arcgismaps.toolkit.popup.internal.element.fieldselement.FieldsPopupElement
import com.arcgismaps.toolkit.popup.internal.element.fieldselement.rememberFieldsElementState
import com.arcgismaps.toolkit.popup.internal.element.media.MediaElementState
import com.arcgismaps.toolkit.popup.internal.element.media.MediaPopupElement
import com.arcgismaps.toolkit.popup.internal.element.media.rememberMediaElementState
import com.arcgismaps.toolkit.popup.internal.element.state.PopupElementStateCollection
import com.arcgismaps.toolkit.popup.internal.element.state.mutablePopupElementStateCollection
import com.arcgismaps.toolkit.popup.internal.element.textelement.TextElementState
Expand Down Expand Up @@ -165,6 +169,12 @@ private fun PopupBody(popupState: PopupState) {
)
}

is MediaPopupElement -> {
MediaPopupElement(
entry.state as MediaElementState
)
}

else -> {
// other popup elements are not created
}
Expand Down Expand Up @@ -228,6 +238,13 @@ internal fun rememberStates(
)
}

is MediaPopupElement -> {
states.add(
element,
rememberMediaElementState(element = element, popup = popup)
)
}

else -> {
// TODO remove for release
println("encountered element of type ${element::class.java}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2024 Esri
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.arcgismaps.toolkit.popup.internal.element.media

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

/**
* A central place for theming values. To be promoted to a public theme type.
*/
internal object MediaElementDefaults {
@Composable
fun shapes(): MediaElementShapes = MediaElementShapes(
borderThickness = 1.dp,
containerShape = RoundedCornerShape(5.dp),
tileShape = RoundedCornerShape(8.dp),
galleryPadding = 15.dp,
tileStrokeWidth = 0.5.dp,
tileWidth = 276.dp,
tileHeight = 225.dp
)

@Suppress("unused")
@Composable
fun colors() : MediaElementColors = MediaElementColors(
containerColor = MaterialTheme.colorScheme.background,
galleryContainerColor = MaterialTheme.colorScheme.onBackground,
borderColor = MaterialTheme.colorScheme.outline,
tileBorderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.6f),
tileTextBackgroundColor = MaterialTheme.colorScheme.onBackground,
tileTextColor = MaterialTheme.colorScheme.background
)
}

internal data class MediaElementShapes(
val borderThickness: Dp,
val containerShape: RoundedCornerShape,
val tileShape: RoundedCornerShape,
val galleryPadding: Dp,
val tileStrokeWidth: Dp,
val tileWidth: Dp,
val tileHeight: Dp
)

internal data class MediaElementColors(
val containerColor : Color,
val galleryContainerColor: Color,
val tileBorderColor : Color,
val borderColor : Color,
val tileTextBackgroundColor: Color,
val tileTextColor: Color
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright 2024 Esri
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.arcgismaps.toolkit.popup.internal.element.media

import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import com.arcgismaps.mapping.popup.MediaPopupElement
import com.arcgismaps.mapping.popup.Popup
import com.arcgismaps.mapping.popup.PopupMedia
import com.arcgismaps.mapping.popup.PopupMediaType
import com.arcgismaps.toolkit.popup.internal.element.state.PopupElementState

/**
* Represents the state of an [MediaPopupElement]
*/
@Immutable
internal class MediaElementState(
val description: String,
val title: String,
val media: List<PopupMediaState>,
override val id : Int = createId(),
) : PopupElementState() {

constructor(mediaPopupElement: MediaPopupElement): this(
description = mediaPopupElement.description,
title = mediaPopupElement.title,
media = mediaPopupElement.media
.filter { it.type is PopupMediaType.Image }
.map { PopupMediaState(it) }
)

companion object {
fun Saver(
element: MediaPopupElement
): Saver<MediaElementState, Any> = Saver(
save = { null },
restore = {
MediaElementState(
element
)
}
)

}
}

@Composable
internal fun rememberMediaElementState(
element: MediaPopupElement,
popup: Popup
): MediaElementState {
return rememberSaveable(
inputs = arrayOf(popup, element),
saver = MediaElementState.Saver(element)
) {
MediaElementState(
element
)
}
}

/**
* Represents the state of a [PopupMedia value].
*/
internal class PopupMediaState(
val title: String,
val caption: String,
@Suppress("unused") val refreshInterval: Long,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@Suppress("unused") val refreshInterval: Long,
@Suppress("unused") val imageRefreshInterval: Long,

@Suppress("unused") val linkUrl: String,
val sourceUrl: String,
val type: PopupMediaType
) {
constructor(media: PopupMedia) : this(
title = media.title,
caption = media.caption,
refreshInterval = media.imageRefreshInterval,
linkUrl = media.value?.linkUrl ?: "",
sourceUrl = media.value?.sourceUrl ?: "",
type = media.type
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2024 Esri
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.arcgismaps.toolkit.popup.internal.element.media

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.arcgismaps.mapping.popup.PopupMediaType
import com.arcgismaps.toolkit.popup.internal.ui.ExpandableCard

@Composable
internal fun MediaPopupElement(
state: MediaElementState
) {
MediaPopupElement(
title = state.title,
description = state.description,
stateId = state.id,
media = state.media
)
}

@Composable
private fun MediaPopupElement(
description: String,
title: String,
@Suppress("UNUSED_PARAMETER") stateId: Int,
media: List<PopupMediaState>
) {
ExpandableCard(
title = title,
description = description
) {
Column(
modifier = Modifier.padding(MediaElementDefaults.shapes().galleryPadding)
) {
val listState = rememberLazyListState()
MediaGallery(listState, media)
}
}
}

@Composable
private fun MediaGallery(state: LazyListState, media: List<PopupMediaState>) {
LazyRow(
state = state,
horizontalArrangement = Arrangement.spacedBy(15.dp),
) {
items(media, key = { it.title + it.type + it.caption }) {
MediaTile(it)
}
}
}

@Preview
@Composable
private fun MediaPopupElementPreview() {
MediaPopupElement(
title = "Media",
description = "description of Media",
stateId = 1,
media = listOf(
PopupMediaState(
"Photo 1.jpg",
"caption",
1234L,
linkUrl = "",
sourceUrl = "https://i.postimg.cc/65yws9mR/Screenshot-2024-02-02-at-6-20-49-PM.png",
PopupMediaType.Image,
)
)
)
}
Loading