Native file documents
Overview
View file documents on the device. Access the file system of the native device. Share documents to other apps on the device. Resize and crop images. Work with zip archives. Capture Mendix log to files.View file documents on the device. Access the file system of the native device. Share documents to other apps on the device. Resize and crop images. Work with zip archives. Capture Mendix log to files.
Manual actions are required after installing this module in your app. Read the documentation!
Documentation
Manual actions required after you install this module, check 'Dependencies' and 'Installation' below
Description
Access the file system of the native device. View file documents on the device, as temporary solution until the Mendix platform supports this.
Due to the nature of this module, it comes with a disclaimer and warning, by using this module you accept and agree to it:
This module provides a convenient way for viewing files and images. However, it is an advanced, highly technical module! You need to create a custom build to use it, it does not work in the Make It Native app!
The module is free to use, as is, and we will update it regularly for new Mendix releases.
Unfortunately, we cannot provide extended support on using this module. The functions are documented and a sample project will be available too.
Be advised that incorrect usage of the JavaScript actions in this module can cause issues in your app, like deleting files that you did not create yourself. We do not accept any liability or responsibility!
If you are interested we are of course willing to provide assistance, you may contact use here: https://developer.mendixcloud.com/link/partnerprofile/4808
Since you’re still reading, now comes the good stuff!
Typical usage scenario
To work with file documents on the native device
Features and limitations
JavaScript actions:
- copyFile
- cropImage
- cropImageCleanupTempFiles
- deleteItem
- downloadFile
- exists
- getFileDocumentUrl
- getFullPath
- getPlatform
- IsNativeFileSystemAvailable
- mkdir
- moveFile (also can rename files)
- openDocumentPicker
- readDir
- readFile
- readTextFile
- resizeImage
- saveToMendixFileDocument
- shareOpen
- stopDownload
- unzipToFolder
- viewFile
- writeFile
- writeTextFile
- zipFolder
JavaScript actions log listener:
- endLogListener
- getDefaultLogLevel
- getLogNodes
- isListenerActive
- resetAllLogLevels
- rotateLog
- setLogLevel
- startLogListener
Each action is documented in the module. Most have a writeToLog parameter, which controls whether the log details are written to NativeActionLog. Snippet Snippet_SyncDebug_NativeActionLog shows the log on the native app.
Call IsNativeFileSystemAvailable to allow your app to fail gracefully when a developer attempts to use the functionality in the Make It Native app.
Widgets:
- PDF viewer
Dependencies
- => Your logic to turn logging on or off. See below <=
- NativeMobileResources
- DataWidgets
- Combobox
- Mendix 8.18.3+
- Mendix 9: Native dependencies are included automatically when you build the app
- Mendix 8: Node modules react-native-fs, react-native-file-viewer, react-native-share and react-native-document-picker Use npm install or edit package.json to add them to your build. Check the dependency json files for the right versions!
Installation
Download the Mendix appstore module.
Make sure that the NativeActionLog and DeviceLogFile synchronization setting is set to 'Nothing (clear data)’. (Native navigation profile, Sync configuration button)
Patch for document picker on Android
Unfortunately the latest releases of the document picker on Android will always return 'Cancelled by user' exception.
A patch is available, get it here: https://github.com/Itvisors/patches/blob/main/react-native-document-picker%2B9.3.1.patch
Drop the patch in the patches folder of your native app build project and it will be picked up automatically when you run npm install
Minimum target release for Apple builds when using the zip/unzip actions
The library used by the zip/unzip actions now requires a deployment target set to iOS 15.5 or newer.
This can be seen when running pod install with the default configuration, there will be an error:
Specs satisfying the `RNZipArchive (from `../node_modules/react-native-zip-archive`)` dependency were found, but they required a higher minimum deployment target.
This needs to be configured in the podfile, which is in the ios folder of your build project. It also needs to be set in the Xcode project itself
- Change the value for deployment_target to '15.5' <= including the single quotes! Do not copy-paste but type it in the editor like Visual Studio Code to prevent ending up with so called smart quotes which will lead to errors.
- Run pod install
- Open the project in XCode
- Set the minimum deployment to iOS 15 for the shorthand list, or iOS15.5 in the full drop down. If you prefer a newer version that is also fine for this situation of course.
Custom logic to turn logging on or off
Previously, an attribute on Administration.Account was used to turn logging on or off. This caused issues in apps where a specialization of Account is used. So the native part of the module does not retrieve Account anymore. Account is used as parameter in several places, allowing a specialization to be used as well.
To control logging, nanoflow SUB_IsLoggingEnabled calls nanoflow Application.SUB_NativeFileDocuments_IsLoggingEnabled and expects a boolean return value. In the demo app, this nanoflow just retrieves the Account for the current user and returns Account/OfflineDebug. (See screenshot.)
After installing or upgrading the module, you will probably get an error because you don't have that nanoflow.
Options:
- Create nanoflow Application.SUB_NativeFileDocuments_IsLoggingEnabled and make it return a boolean value. Retrieve Account for the current user and return Account/OfflineDebug to keep things the same as they were before the upgrade. Retrieve your own specialization of Account if applicable.
- Create a nanoflow with any name that suits you and make that return a boolean value. Change nanoflow SUB_IsLoggingEnabled to call your nanoflow. Everytime you update NativeFileDocuments you will need to make that change again. (Hopefully in the future this change will stick.)
- Change nanoflow SUB_IsLoggingEnabled to include your logic to determine a boolean value that controls logging. Least desirable option as your logic will be gone after upgrading the module. Best to use a nanoflow call for that purpose.
Details
Viewing file documents and images
It is possible to synchronize FileDocument entity types to the device. However, currently the viewFile JS action does not work with them because the file on the device has lost the file extension. The extension is necessary for the library to work correctly.
To come around this issue, nanoflow SUB_ViewDocument is available. It can be called with any specialization of FileDocument and will copy the file on the device to its original name and start the viewer.
Supported file types are PDF, JPG, PNG. Most Office types are supported too but on Android these open in the specific app.
Unsupported file types are treated differently on each platform: On Android nothing will happen at all on older versions, but recent Android versions throw an error when file cannot be viewed. On iOS, the viewer will open but not show any contents.
Viewing PDF documents
On Android the PDF will be opened in a separate app when using the viewFile JS action. The PDF Viewer widget allows PDFs to be viewed without leaving the Mendix app. The only property is the path to the file. The same restrictions apply as with viewing other documents: the extension must be correct. On top of that, the path must be specified differently for iOS and Android.
To make viewing PDFs easy, nanoflow SUB_CopyPdfForViewing is available. Similar to SUB_ViewDocument, it will copy the file on the device to its original name. However, the PDF viewer is a widget so the nanoflow will return the path to the file as result.
Create your own page context object and store the path in an unlimited string attribute. Configure that attribute on the widget property and it will work.
Again, make sure to cleanup the viewer temp files often!
Reading contents of a file
For some libraries, the contents of the file need to be passed in base64 encoded format. The readFile JS action does just that.
For ease of use, two nanoflows are provided:
- SUB_ReadFileBase64: Reads content from the passed FileDocument directly.
- SUB_ReadFileBase64WithCopy: First copies the file to a temp file, similar to viewing documents.
When using the readFile JS action in real life situations, it turned out that copying the file before reading its contents is not required.
So it is expected SUB_ReadFileBase64 can be used in all situations. As fallback, SUB_ReadFileBase64WithCopy is provided as well.
Temp files cleanup
The viewFile action cannot tell if and when the viewer was closed. So there will be temporary copies on the device. Nanoflow SUB_DeleteFileViewerTempDirectory deletes the temporary stuff so be sure to call that flow regularly. This also applies when using the PDF Viewer widget and resizing images.
Document picking on the device
With JavaScript action openDocumentPicker the user can select documents to be used in the Mendix app. The result has a uri, which can be saved into a Mendix FileDocument using JavaScript action saveToMendixFileDocument. The sample project has an example of this.
Resizing images
Quite often images need to be small for sharing and larger to store full details on the server. Of course the image size can be limited while taking a picture but what if you want a hires picture and share a smaller sized one? The JS action resizeImage can be used to resize an image. It requires a path to a file on the device so the Mendix Image object must first be prepared using nanoflow SUB_CopyMendixFileDocumentForViewing. The returned path can be used to resize the image to a new file. The JS action returns an object with the new image dimensions, the file name and the file path. You can use the file path as parameter for JS action saveToMendixFileDocument to store the resized image in a Mendix Image object. Again, this creates temporary files in the app storage so be sure to clear the temp directory. The parameters are described in detail on the JS action itself. The demo app has an example on taking a hires picture and resizing it.
Cropping images
While taking a picture, the image can be cropped on the camera. The JS action cropImage allows you to crop an image stored earlier as Mendix Image object. Note that this action too requires a path to a file so the Mendix Image object must first be prepared using nanoflow SUB_CopyMendixFileDocumentForViewing. The returned path can be used as input for cropImage. This JS action starts a UI where the user can drag and pinch-zoom to adjust the content. Upon completion, a Mendix object is returned which contains a full path to the result image. This image can be stored into a new Mendix image object using saveToMendixFileDocument. Be sure to delete the file yourself as the crop library does not know when we're done with it. The result size you specify also defines the aspect ration of the crop frame shown to the user. Note that the result width/height may be larger than the screen size of the device. The frame is scaled to the screen using the dimensions you specify. There is also a cleanup JS action, cropImageCleanupTempFiles, The crop library can use temporary files, calling the cleanup JS action makes sure these are deleted as well. The demo app has a sample of cropping an image and saving the result in a new image.
Sharing content
With JavaScript action shareOpen the user can share content outside the app. Note that the share JavaScript action in NanoflowCommons allows you to share single texts, so if that is your requirement, no need for anything more. With shareOpen, you can share documents as well. The sample project has an example of this.
Limitation: Some apps handle the content passed to them better than others. For example, the iOS default mail app will accept subject, body message and one or more images or documents in one shareOpen call. WhatsApp on iOS will ignore any documents as soon as texts are passed as well. Currently the only way around this is to share any images or documents in a separate shareOpen call. Be sure to test on both platforms to check whether the result matches the requirements of your app!
Store Mendix log to a file
As of Mendix 9.18, an API is available to receive all logs directly from the Mendix runtime. This module contains an implementation of that API. Log entries are written to a file on the device. By default, debug and trace logs are not written to the file. You can configure which log nodes and which log levels are included using the JavaScript actions in the module. The log files can be sent to the backend for use by admins or developers.
Nanoflow exceptions that trigger the generic error popup also get sent to the listener in detail so that is quite helpful to investigate intermittent app errors.
Typical steps to use the log listener:
- Call startLogListener to start the listener
- Choose log levels to include if different from INFO.
- Call endLogListener to end the listener. Listener ends anyway when the app is closed. (Not when the app is in the background)
If you want the listener to be active from app start, put the call to startLogListener in your navigation nanoflow, or the datasource nanoflow of the home page. No need to end the listener as it will end anyway when the app is closed.
By default, log entries from all log nodes are included at the INFO level and higher.
Use TRACE logging only for short periods of time as it generates a lot of log data!
Native logs can also be sent to the backend runtime using the new feature in 9.18. One does not exclude the other. You could combine both methods: Capture the unexpected errors using the logs sent to the backend and use the listener implementation in this module to capture debug or trace logging.
The sample project has a fully working example of all features.
Log nodes and log levels
When the log data gets sent to the backend the lognodes are used as described in the documentation. However, the log nodes on the device do not have the Client_ prefix. For example, just use Nanoflow, Database, Navigation or Synchronization when configuring the log level for these nodes.
Sending logs to the backend
Nanoflow SUB_LogListener_SendToBackend is available to send the log files on the device to the backend. It will also delete log files from the device that have been uploaded. The files get uploaded to entity DeviceLogFile. If you want the files to be stored in a different entity, make a copy of nanoflow SUB_LogListener_SendToBackend in your own module and and adjust to your needs.
You can call this nanoflow in your sync nanoflow.
Snippet Snippet_DeviceLogFile_Overview_Web can be used to view the files in the browser after they are uploaded from the device to the backend.
Native build
Create a native build using the native builder UI. You need at least version 1.0.78 and allow it to update the native template if it prompts you for the update. The additional native module dependencies will be included automatically during the native build for Mx9.
When building for Mx8, use npm install to include the native dependencies react-native-fs, react-native-file-viewer, react-native-share and react-native-document-picker. Check the native dependency files in javascriptsource\nativefiledocuments\actions\ for the right version!
Also run pod install in the ios folder again for iOS builds. (For all Mendix releases)
Android specific
You need to add a query to the Android Manifest for Android 11 and up. Place it directly before the close tag, see screenshot. Note that recent versions of the native template add a similar intent by default so you no longer need to do this manually. However, if company profiles block the image viewer, add the intent to your build.
Replace [ and ] with LT and GT signs as the entire block is lost if these put in directly.
[intent]
[action android:name="android.intent.action.VIEW"]
[data android:mimeType="*/*" /]
[/intent]