Skip to content

Commit 00a24cd

Browse files
Sample build JSON asset data (#332)
Co-authored-by: TADraeseke <[email protected]>
1 parent 2d0c7de commit 00a24cd

File tree

7 files changed

+129
-66
lines changed

7 files changed

+129
-66
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ build/
2626
Icon?
2727
ehthumbs.db
2828
Thumbs.db
29+
*.salive

Diff for: app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/SampleViewerLauncherActivity.kt

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class SampleViewerLauncherActivity : ComponentActivity(), ExceptionListener {
4545

4646
startActivity(Intent(this, MainActivity::class.java).apply {
4747
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
48+
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
4849
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
4950
putExtras(extras)
5051
})

Diff for: app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/DefaultSampleInfoRepository.kt

+41-39
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package com.esri.arcgismaps.kotlin.sampleviewer.model
1919
import android.content.Context
2020
import android.util.Log
2121
import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample.Companion.loadActivityPath
22+
import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample.Companion.loadCodeFiles
2223
import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample.Companion.loadReadMe
2324
import com.esri.arcgismaps.kotlin.sampleviewer.model.Sample.Companion.loadScreenshot
2425
import com.esri.arcgismaps.kotlin.sampleviewer.model.room.AppDatabase
@@ -33,7 +34,7 @@ import kotlinx.serialization.json.Json
3334
import java.util.concurrent.atomic.AtomicBoolean
3435

3536
/**
36-
* The single source of truth for app wide data. It reads the sample metadata to create as list of
37+
* The single source of truth for app wide data. It reads the sample metadata to create a list of
3738
* [Sample] objects and populates the database used for search.
3839
* It also provides functions to get samples by category, name, or all samples.
3940
*/
@@ -47,51 +48,52 @@ object DefaultSampleInfoRepository : SampleInfoRepository {
4748
private val sampleData = _sampleData.asStateFlow()
4849

4950
/**
50-
* Load the sample metadata from the metadata folder in the assets directory and updates sampleList
51+
* Load the sample metadata from 'samples.json' in the assets directory and updates sampleList
5152
* of [Sample] objects.
5253
*/
5354
suspend fun load(context: Context) {
5455
if (isInitialized.compareAndSet(false, true)) {
5556
// List that will be populated with samples
5657
val sampleList = mutableListOf<Sample>()
57-
// Iterate through the metadata folder for all metadata files
58-
context.assets.list("samples")?.forEach { samplePath ->
59-
// Get this metadata files as a string
60-
context.assets.open("samples/$samplePath/README.metadata.json").use { inputStream ->
61-
val metadataJsonString = inputStream.bufferedReader().use { it.readText() }
62-
try {
63-
val metadata = json.decodeFromString<SampleMetadata>(metadataJsonString)
6458

65-
// Create and add a new sample metadata data class object to the list
66-
val sample = Sample(
67-
name = metadata.title,
68-
codeFiles = Sample.loadCodeFiles(
69-
context = context,
70-
sampleName = metadata.title
71-
),
72-
url = "https://developers.arcgis.com/kotlin/sample-code/" +
73-
metadata.title.replace(" ", "-").lowercase(),
74-
readMe = loadReadMe(
75-
context = context,
76-
sampleName = samplePath
77-
),
78-
screenshotURL = loadScreenshot(
79-
sampleName = metadata.title,
80-
imageArray = metadata.imagePaths
81-
),
82-
mainActivity = loadActivityPath(
83-
codePaths = metadata.codePaths
84-
),
85-
metadata = metadata,
86-
)
87-
// Add the new sample to the list
88-
sampleList.add(sample)
89-
} catch (e: Exception) {
90-
Log.e(
91-
DefaultSampleInfoRepository::class.simpleName,
92-
"Exception at $samplePath: " + e.printStackTrace()
93-
)
94-
}
59+
// Read the entire 'samples.json' from build/sampleAssets/samples/
60+
val samplesJsonString = context.assets.open("samples/samples.json").use { stream ->
61+
stream.bufferedReader().use { it.readText() }
62+
}
63+
64+
// Parse it into a map of: sampleFolderName -> mapOf( filename -> fileContent )
65+
val allSamplesData = json.decodeFromString<Map<String, Map<String, String>>>(samplesJsonString)
66+
67+
// Build each Sample using the metadata from "README.metadata.json"
68+
allSamplesData.forEach { (sampleFolderName, fileMap) ->
69+
val metadataJsonString = fileMap["README.metadata.json"]
70+
?: throw Exception("README.metadata.json not found in sample: $sampleFolderName")
71+
try {
72+
val metadata = json.decodeFromString<SampleMetadata>(metadataJsonString)
73+
74+
// Create and add a new sample metadata data class object to the list
75+
val sample = Sample(
76+
name = metadata.title,
77+
codeFiles = loadCodeFiles(fileMap),
78+
url = "https://developers.arcgis.com/kotlin/sample-code/" +
79+
metadata.title.replace(" ", "-").lowercase(),
80+
readMe = loadReadMe(fileMap),
81+
screenshotURL = loadScreenshot(
82+
sampleName = metadata.title,
83+
imageArray = metadata.imagePaths
84+
),
85+
mainActivity = loadActivityPath(
86+
codePaths = metadata.codePaths
87+
),
88+
metadata = metadata
89+
)
90+
// Add the new sample to the list
91+
sampleList.add(sample)
92+
} catch (e: Exception) {
93+
Log.e(
94+
DefaultSampleInfoRepository::class.simpleName,
95+
"Exception at $sampleFolderName: ${e.stackTraceToString()}"
96+
)
9597
}
9698
}
9799
_sampleData.value = sampleList

Diff for: app/src/main/java/com/esri/arcgismaps/kotlin/sampleviewer/model/Sample.kt

+11-26
Original file line numberDiff line numberDiff line change
@@ -62,41 +62,26 @@ data class Sample(
6262
)
6363

6464
/**
65-
* Returns a list of [CodeFile] objects for the given sample name.
65+
* Returns a list of [CodeFile] objects for the given sample [fileMap].
6666
*/
67-
fun loadCodeFiles(context: Context, sampleName: String): List<CodeFile> {
68-
// List of code files to be populated
67+
fun loadCodeFiles(fileMap: Map<String, String>): List<CodeFile> {
6968
val codeFiles = mutableListOf<CodeFile>()
70-
// Code file folders stored in assets directory as kebab case
71-
val sampleNameKebabCase = sampleName.replace(" ", "-").lowercase()
72-
val sampleAssetFiles = context.assets.list("samples/$sampleNameKebabCase/")
73-
// Get the code files from sub-directories (components/, screens/)
74-
sampleAssetFiles?.forEach { sampleAssetFile ->
75-
if (sampleAssetFile.contains(".kt")) {
76-
val codeString = context.assets.open(
77-
/* fileName = */ "samples/$sampleNameKebabCase/$sampleAssetFile"
78-
).bufferedReader().use { it.readText() }
79-
codeFiles.add(
80-
CodeFile(
81-
name = sampleAssetFile,
82-
code = codeString
83-
)
84-
)
69+
fileMap.forEach { (fileName, fileContent) ->
70+
if (fileName.endsWith(".kt", ignoreCase = true)) {
71+
codeFiles.add(CodeFile(name = fileName, code = fileContent))
8572
}
8673
}
8774
return codeFiles
8875
}
8976

9077
/**
91-
* Returns the readme for a given sample name.
78+
* Returns the readme for a given sample [fileMap].
9279
*/
93-
fun loadReadMe(context: Context, sampleName: String): String {
94-
// Get this metadata files as a string
95-
context.assets.open("samples/$sampleName/README.md").use { inputStream ->
96-
val readMeString = inputStream.bufferedReader().use { it.readText() }
97-
// Remove screenshot markdown text from the README
98-
return readMeString.lines().filterNot { it.contains("![") }.joinToString("\n")
99-
}
80+
fun loadReadMe(fileMap: Map<String, String>): String {
81+
val readMeString = fileMap["README.md"]
82+
?: throw Exception("README.md not found in sample.")
83+
// Remove screenshot markdown text from the README
84+
return readMeString.lines().filterNot { it.contains("![") }.joinToString("\n")
10085
}
10186

10287
/**

Diff for: gradle-plugins/build.gradle.kts

+4
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,7 @@ gradlePlugin {
1616
}
1717
}
1818
}
19+
20+
dependencies {
21+
implementation(libs.kotlinx.serialization.json)
22+
}

Diff for: gradle-plugins/settings.gradle.kts

+12
Original file line numberDiff line numberDiff line change
@@ -1 +1,13 @@
11
rootProject.name = "gradle-plugins"
2+
3+
dependencyResolutionManagement {
4+
repositories {
5+
google()
6+
mavenCentral()
7+
}
8+
versionCatalogs {
9+
create("libs") {
10+
from(files("../gradle/libs.versions.toml"))
11+
}
12+
}
13+
}

Diff for: gradle-plugins/src/main/kotlin/com/arcgismaps/CopySampleFilesTask.kt

+59-1
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
package com.arcgismaps
22

3+
import kotlinx.serialization.encodeToString
4+
import kotlinx.serialization.json.Json
35
import org.gradle.api.Plugin
46
import org.gradle.api.Project
57
import org.gradle.api.file.DuplicatesStrategy
68
import org.gradle.api.file.FileCollection
79
import org.gradle.api.tasks.Copy
810
import org.gradle.kotlin.dsl.register
11+
import java.io.File
912

1013
/**
1114
* Copy md, kt, and json files to a directory (with the name of the associated sample)
1215
* into the app's build assets directory.
1316
*/
1417
class CopySampleFilesTask : Plugin<Project> {
18+
19+
private val json = Json { prettyPrint = true }
20+
1521
override fun apply(project: Project) {
1622
project.tasks.register<Copy>("copyCodeFiles") {
1723
description = """
@@ -42,6 +48,58 @@ class CopySampleFilesTask : Plugin<Project> {
4248

4349
// Place in the assets codeFiles folder of the app.
4450
into(outputDir)
51+
52+
// After the copy finishes, run JSON generation + cleanup
53+
doLast {
54+
// Create a JSON file that maps each sample folder’s files to their contents
55+
createJsonAssetFile(outputDir)
56+
57+
// Delete everything but the JSON file itself
58+
outputDir.listFiles()?.forEach { file ->
59+
if (file.isDirectory || file.name != "samples.json") {
60+
file.deleteRecursively()
61+
}
62+
}
63+
}
4564
}
4665
}
47-
}
66+
67+
/**
68+
* Creates the main list of all samples as "samples.json" file in [outputDir] with structure:
69+
*
70+
* {
71+
* "sampleA": {
72+
* "MainActivity.kt": "...content...",
73+
* "README.md": "...content...",
74+
* ...
75+
* },
76+
* "sampleB": {
77+
* ...
78+
* }
79+
* }
80+
*/
81+
private fun createJsonAssetFile(outputDir: File) {
82+
// This map will hold the structure: sampleDirName -> { filename -> fileContent }
83+
val samplesMap = mutableMapOf<String, MutableMap<String, String>>()
84+
85+
// List subdirectories in outputDir (i.e., each sample folder)
86+
outputDir.listFiles()
87+
?.filter { it.isDirectory }
88+
?.forEach { sampleDir ->
89+
val filesMap = mutableMapOf<String, String>()
90+
sampleDir.listFiles()?.forEach { file ->
91+
if (file.isFile) {
92+
filesMap[file.name] = file.readText()
93+
}
94+
}
95+
samplesMap[sampleDir.name] = filesMap
96+
}
97+
98+
// Serialize using Kotlin Serialization with pretty printing
99+
val jsonString = json.encodeToString(samplesMap)
100+
101+
// Write the JSON to a file named "samples.json" in outputDir
102+
val jsonFile = File(outputDir, "samples.json")
103+
jsonFile.writeText(jsonString)
104+
}
105+
}

0 commit comments

Comments
 (0)