I’m working on a Flutter app that uploads photos and videos to a server. The upload process involves selecting files from the gallery. The app works fine on devices with Android versions 13 and below. However, on Android 14, there’s a problem when selecting photos taken by the camera. Photos downloaded from apps like WhatsApp or Messenger upload without issues, but when selecting a photo taken by the camera, the server doesn’t receive the file (it receives the other information sent in the formData but not the actual file).
Initially, I suspected it was a package issue, so I switched from image_picker to file_picker, but the problem persists. I also considered that it might be related to the newly added permission READ_MEDIA_VISUAL_USER_SELECTED, and I managed to request it using the photo_manager package, but with no success. I tested the app on 3 Android 14 phones: Samsung A04, Samsung A05s and Vivo y17s.
Here is the content of my AndroidManifest.xml:
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature
android:name="android.hardware.camera"
android:required="false"
tools:targetApi="eclair" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<application
android:label="accompagnateur_5sur5"
android:name="${applicationName}"
android:requestLegacyExternalStorage="true"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
And here is the code where I upload the images/videos:
Future<void> uploadVisualAttachment(String sejourCode, String date) async {
const directoryName = 'uploads';
try {
String? token = preferences.getString(AppStrings.tokenKey);
if (token != null) {
if (!JwtDecoder.isExpired(token)) {
//await requestReadMediaPermission();
final PermissionState ps = await PhotoManager.requestPermissionExtend();
// right now i'm not handling the different cases because i'm testing the app
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.media,
allowMultiple: true,
);
if (result != null && result.files.isNotEmpty) {
final List<ConnectivityResult> connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult.contains(ConnectivityResult.mobile) || connectivityResult.contains(ConnectivityResult.wifi)) {
// Clear existing files in the uploads directory before starting new uploads
await clearDirectory(directoryName);
for (var media in result.files) {
var mediaType = getMediaType(media.path!);
print("The selected image path: ${media.path}");
// Extract the file extension
String originalFilename = media.name;
String fileExtension = originalFilename.split('.').last;
// Construct the new filename with "ahmed" and the original extension
String newFilename = "ahmed.$fileExtension";
File tempFile;
if (media.path != null) {
tempFile = await saveFileFromPath(media.path!, originalFilename, directoryName);
} else if (media.bytes != null) {
tempFile = await saveFile(media.bytes!, originalFilename, directoryName);
} else {
throw Exception("Unable to process the picked file.");
}
final formData = FormData.fromMap({
"file": await MultipartFile.fromFile(tempFile.path, filename: newFilename),
"date": date,
"type": mediaType
});
var response = await dio.post(
'${AppStrings.baseUrl}${AppStrings.attachment}$sejourCode',
data: formData,
options: Options(headers: {"Authorization": "Bearer $token"})
);
// Debugging: Print response
print("Response Status Code: ${response.statusCode}");
print("Response Data: ${response.data}");
}
// Clear the files after upload is completed
await clearDirectory(directoryName);
} else {
print("No internet connection");
throw NoInternetConnectionException(medias: [], date: date);
}
} else {
throw NoMediaPickedException();
}
} else {
throw UnAuthorizedException(message: "Veuillez vous reconnecter");
}
} else {
throw UnAuthorizedException(message: "Veuillez vous reconnecter");
}
} on DioException catch (e) {
var msg = e.response?.data.toString() ?? e.message ?? e.toString();
print(msg);
throw ServerException(message: msg);
}
}
Does anyone have any idea what might be causing this issue on Android 14? Any help would be greatly appreciated!