Skip to content

Native File Viewer

A common scenario that occurs when files are downloaded to mobile devices is that the user will need to pick third-party apps to open different file types. To provide smoother interaction, the FioriNativeFileViewer can recognize several public format files and display them properly so that users can open those files without leaving the current application.

Supported File Types

The FioriNativeFileViewer will internally use MimeTypeMap.getFileExtensionFromUrl(file_path) to determine the MIME type of the input file.

Files of following MIME types will be handled by the FioriNativeFileViewer and rendered on-screen.

  • Text

  • Image

  • Video

  • Audio

  • PDF

Files recognized as PDF will be handled internally by FioriPDFViewer.

  • Zip

Note that only archive files in zip format can be handled. Other archiving formats are not supported. Once a zip file is opened by the FioriNativeFileViewer, it will be unzipped into a temp folder in the cache folder of the application. The name of the temp folder can be customized using LocalZipDestFolder. Subsequently, the viewer will work as an explorer to display all the files in decompressed format. All the supported file types listed above can then be handled correctly when clicked in the file explorer, including any nested zip files.

Zip

  • Other

Files not recognized as any of the formats listed above will be handled as "other file" and will be handled by the internal webview.

API

Setting up the FioriNativeFileViewer is quite simple:

@Composable
fun FioriNativeFileViewer(
    modifier: Modifier = Modifier,
    file: File, // The file to be handled.
    state :FioriNativeFileViewerState = rememberNativeFileViewerState() // Developers can ignore this argument if they do not want to print the file.
)

@Composable
fun rememberNativeFileViewerState(
    password:String? = null, // If this is an encrypted PDF file, use password to decrpyt this file. Setting it to null implies the PDF file is not encrypted.
    decodedCallback:()->Unit= {}, // If the PDF file is successfully decrpyted with password, this callback will be triggered.
    invalidPasswordCallback:()->Unit={}, // If the PDF file cannot be decrypted with password correctly, this callback will be triggered.
):FioriNativeFileViewerState

By simply calling the function in composable scope, then input files will be handled. In case developers need to print the files (text, image, and PDF files can be printed), they can create the state argument using rememberNativeFileViewerState().

fun printFile(context: Context):Boolean{
    ...
}

This function is available in the state instance. The return value represents whether the system printer is successfully opened.

Note that the printer is not always available as the input files may not be recognized yet. There is a printerAvailable State<Boolean> value to use so that developers can monitor when this value updates to the UI state.

val printerAvailable: State<Boolean>

Regarding the zip file handling, because the back button action cannot easily be handled by a composable function, the FioriNativeFileViewer provides a ZipNavigator instance so that developers can use it to control the navigation in the zip file explorer. Developers can retrieve it using LocalZipViewNavigator.current.

fun canNavigateUp(context: Context):Boolean{
    ...
}

The canNavigateUp function defined in FioriNativeFileViewer returns whether the explorer can navigate to the parent folder. If the current folder displayed in the explorer is the root of the decompressed file, then it will return false.

fun navigateUp(context: Context){
    ...
}

Developers can invoke the navigateUp function in the navigator to make the explorer navigate to the parent folder.

The zip file will be unzipped into a temp folder. The name of the folder can be customized using LocalZipDestFolder.

A composable function usually releases resources using the onDispose function in DisposableEffect. But this is not suitable for the zip file handler since the onDisposable function can also be triggered when the screen is rotated. It is improper to delete the temp folder on rotating the screen. So the FioriNativeFileViewer exposes the Recycler to developers. Developers can determine when to recycle resources on their own.

val LocalZipRecycler = staticCompositionLocalOf {
    Recycler()
}

Developers can retrieve the Recycler using LocalZipRecycler and invoke the recycle() function to recycle all temporary resources.

Customize File Handling

There may be some additional file types that are not expected to be handled by webview. To make it feasible for developers to intercept the file handling logic, the FioriNativeFileViewer uses the Providable FileHandler to do the MimeType checking and subsequent file handling.

val LocalFileHandler:ProvidableCompositionLocal<FileHandler> = staticCompositionLocalOf {
    FioriFileHandlerImpl()
}

Developers can provide their own FileHandler implementation to override the handleFile method.

For example:

override fun handleFile(file: File): HandleType {

    val customized_suffix = ".your_customized_suffix"

    if(files.absolutePath.endswith(customized_suffix)){
        // Customize your handling here.
        return HandleType.Customized
    }

    return super.handleFile(file)
}

Last update: March 4, 2025