@@ -4,15 +4,17 @@ import android.Manifest
44import androidx.annotation.DrawableRes
55import androidx.compose.foundation.clickable
66import androidx.compose.foundation.layout.*
7+ import androidx.compose.foundation.rememberScrollState
8+ import androidx.compose.foundation.text.InlineTextContent
79import androidx.compose.foundation.text.KeyboardActions
810import androidx.compose.foundation.text.KeyboardOptions
11+ import androidx.compose.foundation.text.appendInlineContent
12+ import androidx.compose.foundation.verticalScroll
913import androidx.compose.material.*
1014import androidx.compose.material.icons.Icons
11- import androidx.compose.material.icons.outlined.Add
12- import androidx.compose.material.icons.outlined.Clear
13- import androidx.compose.material.icons.outlined.LocationOn
14- import androidx.compose.material.icons.outlined.Share
15+ import androidx.compose.material.icons.outlined.*
1516import androidx.compose.runtime.*
17+ import androidx.compose.runtime.saveable.rememberSaveable
1618import androidx.compose.ui.Alignment
1719import androidx.compose.ui.Modifier
1820import androidx.compose.ui.focus.FocusRequester
@@ -21,21 +23,26 @@ import androidx.compose.ui.focus.onFocusChanged
2123import androidx.compose.ui.graphics.Color
2224import androidx.compose.ui.graphics.painter.Painter
2325import androidx.compose.ui.platform.LocalFocusManager
26+ import androidx.compose.ui.platform.LocalUriHandler
2427import androidx.compose.ui.res.painterResource
2528import androidx.compose.ui.res.stringResource
29+ import androidx.compose.ui.text.Placeholder
30+ import androidx.compose.ui.text.PlaceholderVerticalAlign
2631import androidx.compose.ui.text.SpanStyle
2732import androidx.compose.ui.text.buildAnnotatedString
2833import androidx.compose.ui.text.font.FontWeight
2934import androidx.compose.ui.text.input.ImeAction
3035import androidx.compose.ui.text.input.KeyboardType
3136import androidx.compose.ui.tooling.preview.Preview
3237import androidx.compose.ui.unit.dp
38+ import androidx.compose.ui.window.Dialog
3339import com.google.accompanist.permissions.ExperimentalPermissionsApi
3440import com.google.accompanist.permissions.PermissionStatus
3541import com.google.accompanist.permissions.rememberMultiplePermissionsState
3642import com.google.android.gms.maps.CameraUpdateFactory
3743import com.google.android.gms.maps.model.LatLng
3844import com.google.maps.android.compose.*
45+ import com.mapcode.BuildConfig
3946import com.mapcode.R
4047import com.mapcode.theme.Green600
4148import com.mapcode.theme.MapcodeTheme
@@ -65,6 +72,11 @@ fun MapBox(
6572) {
6673 val scope: CoroutineScope = rememberCoroutineScope()
6774 val isSatelliteModeEnabled by derivedStateOf { mapProperties.mapType == MapType .HYBRID }
75+ var showAboutDialog by rememberSaveable { mutableStateOf(false ) }
76+
77+ if (showAboutDialog) {
78+ AboutDialog (onDismiss = { showAboutDialog = false })
79+ }
6880
6981 Box (modifier) {
7082 if (renderGoogleMaps) {
@@ -106,11 +118,156 @@ fun MapBox(
106118 },
107119 onMyLocationClick = onMyLocationClick,
108120 onExternalMapAppClick = onExternalMapAppClick,
109- onShareMapcodeClick = onShareMapcodeClick
121+ onShareMapcodeClick = onShareMapcodeClick,
122+ onAboutClick = { showAboutDialog = true }
110123 )
111124 }
112125}
113126
127+ @Composable
128+ fun ScrollableDialog (onDismiss : () -> Unit , title : String , buttonText : String , content : @Composable () -> Unit ) {
129+ Dialog (onDismissRequest = onDismiss) {
130+ Surface (color = MaterialTheme .colors.surface, shape = MaterialTheme .shapes.medium) {
131+ Column (verticalArrangement = Arrangement .SpaceBetween ) {
132+ Text (
133+ modifier = Modifier
134+ .align(Alignment .Start )
135+ .height(64 .dp)
136+ .wrapContentSize()
137+ .padding(start = 24 .dp, end = 24 .dp),
138+ text = title,
139+ style = MaterialTheme .typography.h6
140+ )
141+ Divider (Modifier .height(1 .dp))
142+ Box (
143+ Modifier
144+ .weight(1f , fill = false )
145+ .verticalScroll(rememberScrollState())
146+ .padding(start = 24 .dp, end = 24 .dp, top = 8 .dp, bottom = 8 .dp)
147+ ) {
148+ content()
149+ }
150+ Divider (Modifier .height(1 .dp))
151+ TextButton (
152+ modifier = Modifier
153+ .align(Alignment .End )
154+ .padding(8 .dp),
155+ onClick = onDismiss
156+ ) {
157+ Text (buttonText)
158+ }
159+ }
160+ }
161+ }
162+ }
163+
164+ @Composable
165+ fun AboutDialog (onDismiss : () -> Unit = {}) {
166+ val uriHandler = LocalUriHandler .current
167+ val websiteUrl = stringResource(R .string.website_url)
168+ val sourceCodeUrl = stringResource(R .string.source_code_url)
169+
170+ ScrollableDialog (
171+ onDismiss = onDismiss, title = stringResource(R .string.about_dialog_title, BuildConfig .VERSION_NAME ),
172+ buttonText = stringResource(R .string.close_dialog_button)
173+ ) {
174+ Column {
175+ val dialogTextStyle = MaterialTheme .typography.body1
176+ val inlineContent = mapOf (
177+ " share_icon" to InlineTextContent (
178+ Placeholder (
179+ width = dialogTextStyle.fontSize,
180+ height = dialogTextStyle.fontSize,
181+ placeholderVerticalAlign = PlaceholderVerticalAlign .Center
182+ )
183+ ) {
184+ Icon (Icons .Outlined .Share , " " )
185+ },
186+ " directions_icon" to InlineTextContent (
187+ Placeholder (
188+ width = dialogTextStyle.fontSize,
189+ height = dialogTextStyle.fontSize,
190+ placeholderVerticalAlign = PlaceholderVerticalAlign .Center
191+ )
192+ ) {
193+ Icon (
194+ painter = painterResource(R .drawable.ic_outline_directions_24),
195+ contentDescription = " "
196+ )
197+ },
198+ )
199+
200+ val aboutString = buildAnnotatedString {
201+ pushStyle(dialogTextStyle.toSpanStyle())
202+
203+ append(stringResource(R .string.copyright_welcome))
204+ append(" \n\n " )
205+
206+ pushStyle(MaterialTheme .typography.subtitle2.toSpanStyle())
207+ append(stringResource(R .string.how_to_use_header))
208+ pop()
209+
210+ append(" \n\n " )
211+ append(stringResource(R .string.how_to_use_address))
212+ append(" \n\n " )
213+ append(stringResource(R .string.how_to_use_territory))
214+
215+ append(" \n\n " )
216+ append(stringResource(R .string.how_to_use_share_1))
217+ appendInlineContent(" share_icon" )
218+ append(stringResource(R .string.how_to_use_share_2))
219+
220+ append(" \n\n " )
221+ append(stringResource(R .string.how_to_use_directions_1))
222+ appendInlineContent(" directions_icon" )
223+ append(stringResource(R .string.how_to_use_directions_2))
224+
225+ append(" \n\n " )
226+ append(stringResource(R .string.visit_website_notice))
227+
228+ pop()
229+ }
230+
231+ Text (text = aboutString, inlineContent = inlineContent)
232+ Spacer (Modifier .height(8 .dp))
233+ DialogContentButton (
234+ icon = painterResource(R .drawable.web),
235+ text = stringResource(R .string.website_button)
236+ ) {
237+ uriHandler.openUri(websiteUrl)
238+ }
239+ DialogContentButton (
240+ icon = painterResource(R .drawable.ic_outline_article_24),
241+ text = stringResource(R .string.changelog_button)
242+ ) {
243+ }
244+ DialogContentButton (
245+ icon = painterResource(R .drawable.ic_outline_code_24),
246+ text = stringResource(R .string.source_code_button)
247+ ) {
248+ uriHandler.openUri(sourceCodeUrl)
249+ }
250+ }
251+ }
252+ }
253+
254+ @Composable
255+ fun DialogContentButton (icon : Painter , text : String , onClick : () -> Unit ) {
256+ OutlinedButton (onClick = onClick) {
257+ Icon (icon, contentDescription = null )
258+ Spacer (modifier = Modifier .width(8 .dp))
259+ Text (text)
260+ }
261+ }
262+
263+ @Preview(heightDp = 500 )
264+ @Composable
265+ fun AboutDialogPreview () {
266+ MapcodeTheme {
267+ AboutDialog ()
268+ }
269+ }
270+
114271@Composable
115272fun greyButtonColors (): ButtonColors {
116273 return ButtonDefaults .buttonColors(backgroundColor = Color .LightGray , contentColor = Color .DarkGray )
@@ -125,7 +282,8 @@ fun MapControls(
125282 onZoomOutClick : () -> Unit = {},
126283 onMyLocationClick : () -> Unit = {},
127284 onExternalMapAppClick : () -> Unit = {},
128- onShareMapcodeClick : () -> Unit = {}
285+ onShareMapcodeClick : () -> Unit = {},
286+ onAboutClick : () -> Unit = {}
129287) {
130288 val greenButtonColors = ButtonDefaults .buttonColors(backgroundColor = Green600 , contentColor = Color .White )
131289 val satelliteButtonColors: ButtonColors = if (isSatelliteModeEnabled) {
@@ -141,6 +299,20 @@ fun MapControls(
141299 }
142300
143301 Row (modifier) {
302+ Button (
303+ modifier = Modifier
304+ .size(48 .dp)
305+ .align(Alignment .Bottom ),
306+ onClick = onAboutClick,
307+ contentPadding = PaddingValues (8 .dp),
308+ colors = greyButtonColors()
309+ ) {
310+ Icon (
311+ imageVector = Icons .Outlined .Info ,
312+ contentDescription = stringResource(R .string.about_content_description)
313+ )
314+ }
315+ Spacer (Modifier .width(8 .dp))
144316 Button (
145317 modifier = Modifier
146318 .size(48 .dp)
0 commit comments