Skip to content

Commit 7611b89

Browse files
committed
Implement Share for Routes
This code allows sharing from the Route details screen. The output is a JSON file in the same format as the iOS share.
1 parent 5505624 commit 7611b89

4 files changed

Lines changed: 135 additions & 5 deletions

File tree

app/src/main/java/org/scottishtecharmy/soundscape/MainActivity.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,22 @@ class MainActivity : AppCompatActivity() {
317317
}
318318
}
319319

320+
fun shareRoute(shareUri: Uri?) {
321+
if(shareUri != null) {
322+
val sendIntent: Intent =
323+
Intent().apply {
324+
action = Intent.ACTION_SEND
325+
putExtra(Intent.EXTRA_TITLE, "Shared route")
326+
putExtra(Intent.EXTRA_STREAM, shareUri)
327+
type = "text/plain"
328+
flags += Intent.FLAG_GRANT_READ_URI_PERMISSION
329+
}
330+
331+
val shareIntent = Intent.createChooser(sendIntent, null)
332+
startActivity(shareIntent)
333+
}
334+
}
335+
320336
override fun onNewIntent(intent: Intent) {
321337
super.onNewIntent(intent)
322338
Log.d(TAG, "onNewIntent")

app/src/main/java/org/scottishtecharmy/soundscape/screens/markers_routes/screens/routedetailsscreen/RouteDetailsScreen.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ fun RouteDetailsScreenVM(
6868
routePlayerState: RoutePlayerState
6969
) {
7070
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
71+
val context = LocalContext.current
7172
RouteDetailsScreen(
7273
navController,
7374
routeId,
@@ -77,6 +78,7 @@ fun RouteDetailsScreenVM(
7778
getRouteById = { viewModel.getRouteById(routeId) },
7879
startRoute = { viewModel.startRoute(routeId) },
7980
stopRoute = { viewModel.stopRoute() },
81+
shareRoute = { viewModel.shareRoute(context, routeId) },
8082
clearErrorMessage = { viewModel.clearErrorMessage() },
8183
userLocation = userLocation,
8284
heading = heading
@@ -90,9 +92,10 @@ fun RouteDetailsScreen(
9092
modifier: Modifier,
9193
uiState: RouteDetailsUiState,
9294
routePlayerState: RoutePlayerState,
93-
getRouteById: (routeId: Long) -> Unit,
94-
startRoute: (routeId: Long) -> Unit,
95+
getRouteById: () -> Unit,
96+
startRoute: () -> Unit,
9597
stopRoute: () -> Unit,
98+
shareRoute:(routeId: Long) -> Unit,
9699
clearErrorMessage: () -> Unit,
97100
userLocation: LngLatAlt?,
98101
heading: Float
@@ -105,7 +108,7 @@ fun RouteDetailsScreen(
105108

106109
// Fetch the route details when the screen is launched
107110
LaunchedEffect(routeId) {
108-
getRouteById(routeId)
111+
getRouteById()
109112
}
110113

111114
// Display error message if it exists
@@ -212,7 +215,7 @@ fun RouteDetailsScreen(
212215
text = stringResource(R.string.route_detail_action_start_route),
213216
color = MaterialTheme.colorScheme.onSurface
214217
) {
215-
startRoute(uiState.route.route.routeId)
218+
startRoute()
216219
// Pop up to the home screen
217220
navController.navigate(HomeRoutes.Home.route) {
218221
popUpTo(navController.graph.findStartDestination().id) {
@@ -241,7 +244,7 @@ fun RouteDetailsScreen(
241244
talkbackHint = stringResource(R.string.route_detail_action_share_hint),
242245
color = MaterialTheme.colorScheme.onSurface
243246
) {
244-
/*TODO*/
247+
shareRoute(uiState.route.route.routeId)
245248
}
246249
}
247250
// Small map showing route
@@ -324,6 +327,7 @@ fun RoutesDetailsPopulatedPreview() {
324327
getRouteById = {},
325328
startRoute = {},
326329
stopRoute = {},
330+
shareRoute = {},
327331
clearErrorMessage = {},
328332
userLocation = null,
329333
heading = 0.0F,
@@ -342,6 +346,7 @@ fun RoutesDetailsLoadingPreview() {
342346
getRouteById = {},
343347
startRoute = {},
344348
stopRoute = {},
349+
shareRoute = {},
345350
clearErrorMessage = {},
346351
userLocation = null,
347352
heading = 0.0F,
@@ -360,6 +365,7 @@ fun RoutesDetailsEmptyPreview() {
360365
getRouteById = {},
361366
startRoute = {},
362367
stopRoute = {},
368+
shareRoute = {},
363369
clearErrorMessage = {},
364370
userLocation = null,
365371
heading = 0.0F,

app/src/main/java/org/scottishtecharmy/soundscape/screens/markers_routes/screens/routedetailsscreen/RouteDetailsViewModel.kt

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
package org.scottishtecharmy.soundscape.screens.markers_routes.screens.routedetailsscreen
22

3+
import android.content.Context
4+
import android.net.Uri
5+
import androidx.core.content.FileProvider.getUriForFile
36
import androidx.lifecycle.ViewModel
47
import androidx.lifecycle.viewModelScope
58
import dagger.hilt.android.lifecycle.HiltViewModel
69
import kotlinx.coroutines.flow.MutableStateFlow
710
import kotlinx.coroutines.flow.StateFlow
811
import kotlinx.coroutines.flow.asStateFlow
912
import kotlinx.coroutines.launch
13+
import org.scottishtecharmy.soundscape.MainActivity
1014
import org.scottishtecharmy.soundscape.SoundscapeServiceConnection
1115
import org.scottishtecharmy.soundscape.database.local.dao.RouteDao
16+
import org.scottishtecharmy.soundscape.database.local.model.RouteWithMarkers
17+
import java.io.File
18+
import java.io.FileOutputStream
1219
import javax.inject.Inject
1320

1421
@HiltViewModel
@@ -45,6 +52,106 @@ class RouteDetailsViewModel @Inject constructor(
4552
soundscapeServiceConnection.routeStop()
4653
}
4754

55+
//
56+
// Example JSON for shared route:
57+
//
58+
// {
59+
// "name": "Test Route",
60+
// "id": "F41302B9-A91A-4D8F-8958-6C052E334076",
61+
// "routeDescription": "",
62+
// "waypoints": [
63+
// {
64+
// "marker": {
65+
// "nickname": "Home",
66+
// "location": {
67+
// "name": "Home",
68+
// "coordinate": {
69+
// "latitude": 55.9553596,
70+
// "longitude": -3.2031666
71+
// }
72+
// },
73+
// "estimatedAddress": "28A Heriot Row, Edinburgh, Scotland, EH3 6EN",
74+
// "id": "489A82CE-EF43-46F8-85FB-8D79F88763F6",
75+
// "lastUpdatedDate": 759964150.733105
76+
// },
77+
// "index": 0,
78+
// "markerId": "489A82CE-EF43-46F8-85FB-8D79F88763F6"
79+
// },
80+
// ]
81+
// }
82+
//
83+
private fun generateRouteJson(route: RouteWithMarkers?, outputFile: File) {
84+
85+
val outputStream = FileOutputStream(outputFile, false)
86+
outputStream.write(
87+
(
88+
"{\n" +
89+
"\t\"name\": \"${route?.route?.name ?: ""}\",\n" +
90+
"\t\"id\": \"${route?.route?.routeId ?: 0L}\",\n" +
91+
"\t\"routeDescription\": \"${route?.route?.description ?: ""}\",\n" +
92+
"\t\"waypoints\": [\n"
93+
).toByteArray()
94+
)
95+
96+
for ((index, marker) in (route?.markers ?: emptyList()).withIndex()) {
97+
if (index > 0) {
98+
outputStream.write(",\n".toByteArray())
99+
}
100+
outputStream.write(
101+
(
102+
"\t\t{\n" +
103+
"\t\t\t\"marker\": {\n" +
104+
"\t\t\t\t\"nickname\": \"${marker.name}\",\n" +
105+
"\t\t\t\t\"location\": {\n" +
106+
"\t\t\t\t\t\"name\": \"${marker.name}\",\n" +
107+
"\t\t\t\t\t\"coordinate\": {\n" +
108+
"\t\t\t\t\t\t\"latitude\": ${marker.latitude},\n" +
109+
"\t\t\t\t\t\t\"longitude\": ${marker.longitude}\n" +
110+
"\t\t\t\t\t}\n" +
111+
"\t\t\t\t},\n" +
112+
"\t\t\t\t\"estimatedAddress\": \"${marker.fullAddress}\",\n" +
113+
"\t\t\t\t\"id\": \"${marker.markerId}\"\n" +
114+
"\t\t\t},\n" +
115+
"\t\t\t\"index\": $index,\n" +
116+
"\t\t\t\"markerId\": \"${marker.markerId}\"\n" +
117+
"\t\t}"
118+
).toByteArray()
119+
)
120+
}
121+
outputStream.write(
122+
(
123+
"\n\t]\n" +
124+
"}\n"
125+
).toByteArray()
126+
)
127+
}
128+
129+
private fun writeRouteAndReturnUri(context: Context, route: RouteWithMarkers?) : Uri? {
130+
131+
// Write the route to a file and share it
132+
val path = "${context.filesDir}/route/"
133+
val routeStorageDir = File(path)
134+
if (!routeStorageDir.exists()) {
135+
routeStorageDir.mkdirs()
136+
}
137+
val outputFile = File(routeStorageDir, "route.json")
138+
generateRouteJson(route, outputFile)
139+
140+
return getUriForFile(context, "org.scottishtecharmy.fileprovider", outputFile)
141+
}
142+
143+
144+
fun shareRoute(context: Context, routeId: Long) {
145+
// Get the route from the database
146+
val route = routeDao.getRouteWithMarkers(routeId)
147+
if(route != null) {
148+
// Write the route to a file, and then call the MainActivity shareRoute function with a
149+
// shared URI to the file.
150+
val shareUri = writeRouteAndReturnUri(context, route)
151+
(context as MainActivity).shareRoute(shareUri)
152+
}
153+
}
154+
48155
fun clearErrorMessage() {
49156
_uiState.value = _uiState.value.copy(errorMessage = null)
50157
}

app/src/main/res/xml/filepaths.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<paths>
33
<files-path path="recordings/" name="recordings" />
4+
<files-path path="route/" name="route" />
45
</paths>

0 commit comments

Comments
 (0)