Context and Version Information:
The issue has been observed on the following versions:
- Device: Samsung A52
- Android: 14
- One UI: 6.0
- Browser: Chrome 125.0.6422.113
- Enketo Form: Test Form
Problem Description:
In this specific version context, the input
type file
no longer supports taking photos directly; it only allows image uploads from the gallery. Additionally, sometimes, updating the IndexedDB fails. When this happens, the error is caught here : enketo/packages/enketo-express/public/js/src/module/controller-webform.js at main · enketo/enketo · GitHub
A screenshot of the error is available in my previous post. The generated error occurs when attempting to convert the ‘file’ input to a blob for storage in IndexedDB.
Reproducing the Issue:
To replicate the issue, I perform the following steps:
- Control my phone via Chrome on my PC.
- Open the Test Form on my phone.
- Insert a photo into the first input of the form.
- Execute this JavaScript code in the phone’s console:
// A simple function that tries to read the file
function getBase64(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
console.log("ok, base64 result: ", reader.result);
};
reader.onerror = function (error) {
console.error('Error: ', error);
};
}
// Get all input elements (as Enketo does)
fileInputs = [...document.querySelectorAll('form.or input[type="file"], form.or input[type="text"][data-drawing="true"]')];
// Try to read the file from the first input
getBase64(fileInputs[0].files[0]);
// Often it works the first time, but if you try reading multiple times (even with delays):
getBase64(fileInputs[0].files[0]);
// 5 seconds delay...
getBase64(fileInputs[0].files[0]);
...
// At some point, the reader rejects with an error:
DOMException: The requested file could not be read, typically due to permission problems.
Since Enketo reads all file inputs during each _autoSaveRecord
to update IndexedDB, this error scenario occurs relatively quickly.
Proposed Temporary Solution:
My initial fix is to replace the files
attribute of the input field with a JavaScript File object containing the input file as Blob. This way, the reference to the input file is no longer on the OS (Android), but a blob within the browser, eliminating the file read error.
Here’s the first version of my fix, though not very elegant:
I modified the filepicker widget by injecting a promise allowing the input files to be “switched” with this code:
const self = this;
//...
const reader = new FileReader();
reader.onload = function(e) {
const blob = new Blob([e.target.result], { type: file.type });
const dataTransfer = new DataTransfer();
const newFile = new File([blob], file.name, { type: file.type });
dataTransfer.items.add(newFile);
self.element.files = dataTransfer.files;
resolve();
};
reader.onerror = function (error) {
input.value = '';
reject(error);
};
reader.readAsArrayBuffer(file);
// Here, file = event.target.files[0];
This solution worked quite well except in rare cases where a file read error still occurred (Android indicated the file was modified, But when writing this post, I cannot reproduce the error and specify the error code here). In such cases, I cleared the input and used gui.alert
to notify the user.
Fixing the Direct Photo Capture Issue:
To address the issue of not being able to take a photo directly from the input, I followed this post and added text/plain
to the input’s accept attribute in the filepicker widget:
// At the beginning of init
if (!this.element.getAttribute('accept').includes("text/plain")) {
this.element.setAttribute('accept', this.element.getAttribute('accept') + ",text/plain");
}
I then modified the switch and resizeFile functions to ensure they worked correctly: enketo/packages/enketo-core/src/widget/file/filepicker.js at main · enketo/enketo · GitHub enketo/packages/enketo-core/src/widget/file/filepicker.js at main · enketo/enketo · GitHub
The camera is now available, along with the gallery!
Conclusion:
Upon writing this post, I removed my initial blob copying fix to capture and document the error trace. Surprisingly, the file access issue no longer occurred without my initial fix. It appears that adding the text/plain
accept attribute to the input might fully resolve the file access issue. It’s possible Chrome creates a blob copy when this accept attribute is present or something similar?
To conclude, I plan to submit a issue to the Enketo project to integrate text/plain
into the input
type file
and discuss this with the developers to get their insights.
I hope this message will be complete enough for you to be able to reproduce the bug and understand my search for resolutions
EDIT:
Issue here : Photo/Camera upload on Android 14 · Issue #1321 · enketo/enketo · GitHub