Media View showcase - Overview and local media handling
Updated: Sep 29, 2025
Media View is a mixed reality Quest application that shows developers how to build a spatialized media viewing app using the Spatial SDK. One of the key use cases of mixed reality on Quest is the ability to take full advantage of the space around you for content, instead of being confined to a screen or monitor. Additionally, Quest devices excel at viewing media formats that might be difficult to fully enjoy on mobile or desktop, such as panoramic or 360 content. The Media View app demonstrates the concepts and code required to visually organize media content, display various formats of content in space at the same time, and how to integrate with a cloud hosting solution to easily transfer more media content to the headset for viewing.
Additionally, new developers using the Meta Spatial SDK should explore the underlying design guidelines (covered in
Cloud Integration and Design Guidelines) after reviewing this showcase. This exploration will deepen your understanding of creating immersive user experiences and expanding your app’s audience reach.
In a media viewing application, visually organizing the list of files on a device, loading local files, and displaying local media are all essential functions.
Media View provides robust media handling capabilities, leveraging the Android MediaStore for accessing, managing, and storing media files on the device. This document dives into the details of how Media View interacts with the MediaStore, detects different media types, and manages sample media assets.
Media View uses the Android MediaStore to access and manage media files on the user’s device. The data/gallery module encapsulates the logic for MediaStore interaction, providing a clean separation of concerns.
- The
MediaStoreQueryBuilder class constructs queries for retrieving media files based on specified filters (MediaFilter) and sort options (MediaSortBy). - The
buildSelection method dynamically generates selection clauses for the MediaStore query, allowing for flexible filtering based on media types and other criteria. - The
buildSortOrder method constructs the sort order clause based on the selected MediaSortBy option, allowing users to organize their media by date, size, or name.
- The
MediaStoreFileDto class represents a media file retrieved from the MediaStore. It includes logic to determine the type of media based on file properties like mime type, aspect ratio, dimensions, and bit rate. - Methods like
isPanorama, isRayban, is360, and isSpatial use heuristics to categorize media files. For example, panoramas are identified based on their aspect ratio exceeding a certain threshold, while 360-degree media are detected based on a 2:1 aspect ratio. - Based on these detection methods, the
mediaType and mediaFilter properties of MediaStoreFileDto are set, facilitating filtering and presentation in the UI.
- The
DeviceGalleryService class handles the process of saving new media files to the device. The saveMediaFile method utilizes ContentValues to create a new entry in the MediaStore, specifying properties like display name, mime type, and relative path. - The method also opens a file descriptor for the new MediaStore entry, allowing data to be written to the associated file using a
FileOutputStream. The IS_PENDING flag is used to indicate that the file is still being written. Once the write is complete, the flag is cleared, signifying that the file is available.
Media View includes sample media assets that can be saved to the user’s device, providing an initial set of content to explore. The data/user module is responsible for managing user preferences, including the tracking of whether sample media has been saved.
- The
UserRepository class provides a centralized way to access and modify user preferences. - The
UserPreferencesService interacts with SharedPreferences to store and retrieve preferences. The isSampleMediaSaved method checks whether the user has previously saved the sample media.
- The
PermissionViewModel handles the process of loading sample assets from the assets folder. This occurs after the user grants storage permissions. - Before saving assets, the code checks if a sample media folder exists from a previous installation. If found, it is deleted to avoid conflicts or duplicates.
- The
saveAssetDirectory and saveAssetFile methods recursively iterate through the sample assets in the assets folder, saving each file to the device using the DeviceGalleryService. The setSampleMediaSaved method in UserRepository is called after successful asset saving to update the user’s preference.
fun buildSelection(filter: MediaFilter?): Pair<String?, Array<String>?> {
return Pair(
"${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?",
arrayOf(
"${MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE}",
"${MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO}"
),
) // ... Add support for other MediaFilters
}
private fun isPanorama(): Boolean {
return aspectRatio?.let { it > MediaType.panoramaAspectRatioMin } ?: false
}
suspend fun saveMediaFile(
displayFileName: String?,
mimeType: String?,
relativeSubPath: String?,
onWrite: (FileOutputStream) -> Unit
): Boolean {
// ... ContentValues setup and MediaStore insertion
contentResolver.openFileDescriptor(mediaUri, "w", null).use { file ->
// ... Writing data to the FileOutputStream
}
// ... Update MediaStore entry to clear IS_PENDING flag
}
fun isSampleMediaSaved(): Boolean {
return userPreferencesService.isSampleMediaSaved()
}
By combining the robust capabilities of the Android MediaStore with custom logic for media type detection and sample asset management, Media View delivers a comprehensive media handling experience. This approach ensures a seamless workflow for accessing, managing, and presenting diverse media content within the immersive mixed reality environment.