flutter_share icon indicating copy to clipboard operation
flutter_share copied to clipboard

Can't share file in Application Document Directory - found work around

Open skionthursday opened this issue 5 years ago • 4 comments

I was having success when the file to share was located on the android external storage. I elected to move my files from an external storage location to the Application Document Directory, located using getApplicationDocumentsDirectory(). I did this so I can deploy to iOS which does not allow access to external storage. I can write the file to:

/data/user/0/com.PlantListGPSv1.Android.PlantListGPSv1/app_flutter/Backup/Backup 2020-08-12@20_18.pgps

placing the above full path into a string variable _fullPathName, and running the code below,

/// print('Full path name: $_fullPathName'); FlutterShare.shareFile( title: _fileName, text: 'Plant List GPS File:' + _fileName, filePath: _fullPathName, ); ///

I get a debug console message and this error message

Full path name: /data/user/0/com.PlantListGPSv1.Android.PlantListGPSv1/app_flutter/Backup/Backup 2020-08-12@20_18.pgps Exception has occurred. PlatformException (PlatformException(Failed to find configured root that contains /data/data/com.PlantListGPSv1.Android.PlantListGPSv1/app_flutter/Backup/Backup 2020-08-12@20_18.pgps, null, null))

====

In trying to troubleshoot what FutterShare stopped working I find that my code runs without problem on iOS which has me thinking something changed with Android. I currently have an app working on Google play, build at the end of July with Android 28. After this post, I moved to Android 29 and I can remember when exactly things started breaking, but was soon after I started working in Android 29. I found that my permission-handler plugin went from permission_handler: '^4.2.0+hotfix.2' to permission_handler: ^5.0.1+1 which was a major change. I suspect internal storage, external storage, shared storage(new?) has changed too and could be the root cause of why the flutter_share stopped working with the path provide by getApplicationDocumentsDirectory. I offer this insight to help other to thing in this direction if a cause has not yet been found.

I have developed a work-around as sharing files is important to my app. In a high level, my app reads from the ApplicationDocumentDirectory and writes the file to ExternalStorageDirectory when flutter_share works. Below is an extract of my code that is working for me. I have logged a provider error message that I do not understand, but since flutter_share works, I'm not sure how relevant the exception is, and would appreciate any help in resolving it.

//permission_handler: ^5.0.1+1
Future<String> askStoragePermission() async {
	var permission = await Permission.storage.request().isGranted;
	if (permission) {
	  return 'Ok';
	} else {
	  print('Storage permission denied');
	  return 'Not OK';
	}
  }

Future<void> shareFile(_subDir, _fileName) async {
final String _fullPathName = await getShareFileFullPath(_subDir, _fileName);
print('Find file at: \n$_fullPathName');
await FlutterShare.shareFile(
  title: _fileName,
  text: 'PLGPS File:' + _fileName,
  filePath: _fullPathName,
);
}

Future<String> getShareFileFullPath(String _subDir, String _fileName) async { String _fullPathName, _rootDir, _storagePermission; Io.Directory _storDir; if (Platform.isAndroid) { // work around FlutterShare not able to read App Doc Dir // get file to re-write to external Storage _storDir = await getApplicationDocumentsDirectory(); _rootDir = _storDir.toString(); _fullPathName = _rootDir.substring(12, _rootDir.length - 1) + _subDir + _fileName; print('$_fullPathName'); final _myFile = new Io.File(_fullPathName); final String _fileData = await _myFile.readAsString(); // have file read // // write file to external storage so it can be shared _storagePermission = await _iOX.askStoragePermission(); print('Storage permission: $_storagePermission'); var _path = await ExtStorage.getExternalStorageDirectory(); _subDir = 'Test'; final String _plgpsDirPath = _path + '/$_subDir/'; Io.Directory(_plgpsDirPath) .create(recursive: true) .then((Io.Directory directory) {}); final String _fullPathFileName = _plgpsDirPath + _fileName; new File(_fullPathFileName).writeAsString(_fileData); print('File written to:\n$_fullPathFileName'); return _fullPathFileName; } // iOS and other OS routine else { Io.Directory _storDir = await getApplicationDocumentsDirectory(); _rootDir = _storDir.toString(); _fullPathName = _rootDir.substring(0, _rootDir.length - 1) + _subDir + "/" + _fileName; print('$_fullPathName'); return _fullPathName; } }

AndroidManifest.xml highlights

I'm not sure about the following error message. The share process powers through the Permission denied exception. I suspect sharing between routines is not happening, but not familiar enough with the topic to know the issue. Maybe someone who knows can chime in to help with the reported issue. Thanks.

Debug Console:

I/flutter (23897): /data/user/0/com.PlantListGPSv1.Android.PlantListGPSv1/app_flutter/Backup/Backup 2020-08-14@21_14.pgps I/flutter (23897): Storage permission: Ok I/flutter (23897): File written to: I/flutter (23897): /storage/emulated/0/Test/Backup 2020-08-14@21_14.pgps I/flutter (23897): Find file at: I/flutter (23897): /storage/emulated/0/Test/Backup 2020-08-14@21_14.pgps I/.PlantListGPSv(23897): NativeAlloc concurrent copying GC freed 7847(424KB) AllocSpace objects, 0(0B) LOS objects, 49% free, 1589KB/3179KB, paused 44.346ms total 466.370ms E/DatabaseUtils(23897): Writing exception to parcel E/DatabaseUtils(23897): java.lang.SecurityException: Permission Denial: reading androidx.core.content.FileProvider uri content://com.PlantListGPSv1.Android.PlantListGPSv1.provider/external_files/Test/Backup%202020-08-14%4021_14.pgps from pid=21487, uid=1000 requires the provider be exported, or grantUriPermission() E/DatabaseUtils(23897): at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:729) E/DatabaseUtils(23897): at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:602) E/DatabaseUtils(23897): at android.content.ContentProvider$Transport.query(ContentProvider.java:231) E/DatabaseUtils(23897): at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:104) E/DatabaseUtils(23897): at android.os.Binder.execTransactInternal(Binder.java:1021) E/DatabaseUtils(23897): at android.os.Binder.execTransact(Binder.java:994)

skionthursday avatar Aug 13 '20 03:08 skionthursday

There are several changes with Android API 29 and API 30 related to file handling. In fact even API 28 had some changes. https://developer.android.com/training/data-storage/shared/media https://developer.android.com/about/versions/10/behavior-changes-10

MrCsabaToth avatar Sep 21 '20 21:09 MrCsabaToth

@MrCsabaToth so what's the recommended API number that works with , e.g. flutter_share 1.0.3?

mw66 avatar May 28 '21 01:05 mw66

Saw this one:

https://stackoverflow.com/questions/63688285/flutter-platformexceptionerror-failed-to-find-configured-root-that-contains

""" My understanding is as follow, you can read and write in ApplicationDocumentsDirectory but the data can't be accessed outside of the app. Since when you call FlutterEmailSender the data is then sent through an other application, you'll have to save your .csv to a "public" location. """

Maybe this is the reason.

mw66 avatar May 28 '21 02:05 mw66

I copied the app image file to the public download dir:

import 'package:downloads_path_provider/downloads_path_provider.dart';
...

                      File file = File(path);
                      Directory downloadsDirectory = await DownloadsPathProvider.downloadsDirectory;
                      // copy file to downloadsDirectory
                      String fileBasename = basename(file.path);
                      String pubPath = '${downloadsDirectory.path}/$fileBasename';
                      await file.copy(pubPath);
                      FlutterShare.shareFile(
                        title: 'share image',
                        text: "share image",
                        filePath: pubPath,
                      );

The operation seems successful, no error report by the app. But in the other to-be-shared app, the sharing is not happening.

And I checked the "/Downloads/....jpeg", the file is copied there.

Do you know why the sharing is not actually happening, while no error reported? What I should check further?

P.S.: I just found some app e.g. Email/Gmail sharing is working; while other app is not, e.g. WeChat (only simple text share is working on WeChat).

Has anyone been able to share image on WeChat?

Thanks.

mw66 avatar May 28 '21 04:05 mw66