Giter VIP home page Giter VIP logo

media_store_plus's Introduction

media_store_plus

To use Android MediaStore API in Flutter. It supports both read & write operation in every android version through flutter. It also requests for appropriate permissions, if needed.

Motivation

From API level 30, we have to use Scoped Storage to write file. Though, we can read all files by direct path until API level 32, from API level 33 we need to use Scoped Storage for reading also.

We can write in DCIM, Pictures, Movies, Alarms, Audiobooks, Music, Podcasts, Ringtones, Download folders by MediaStore without any kinds of Storage permission. You can also read without any permission from these folders as long as the files are created by the app. But if we uninstall the app, and install it again, then, it will lose the read/write access for the files previously created by the app.

Again, we can read all files by requesting android.permission.READ_EXTERNAL_STORAGE permission until API level 32.From API level 33, it has no usage. Android introduces three specific permission i.e. android.permission.READ_MEDIA_IMAGES, android.permission.READ_MEDIA_AUDIO & android.permission.READ_MEDIA_VIDEO to read audio, video & images.

But, we can't read other folders from API level 33 without requesting explicit permission for those folders.

Sum up, From flutter there is no way write in any folder with/without storage permission other than app data folder!

Because, we can't use MediaStore API from flutter, that is required for writing, other than app data folder. Again we can't request to read/write any specfic folder using file picker from flutter.

So, only solution to use platform channel for this. Actually, this issue lead me to create this media_store_plus plugin.

Usage

You can read, write, edit, delete in the DCIM, Pictures, Movies, Alarms, Audiobooks, Music, Podcasts, Ringtones, Download with this plugin. You can also request to read or write any specific folder other than these mentioned above, by file picker with this plugin. Again when you will try to read, edit or delete a file that is not created by your app, it will request permission from user for that task automatically.

You can read the full documentation from here. You can also check the example app for implementation. Reading the plugin's source code also help you in that case.

Getting Started

First, add media_store_plus as a dependency in your pubspec.yaml file.

dependencies:
  media_store_plus: ^0.1.1

Don't forget to flutter pub get.

Android Side

Edit the AndroidManifest.xml like this.

    <!-- required from API level 33 -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- To read images created by other apps -->
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- To read audios created by other apps -->
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- To read vidoes created by other apps -->

    <uses-permission
        android:name="android.permission.READ_EXTERNAL_STORAGE" <!-- To read all files until API level 32 -->
        android:maxSdkVersion="32" />

    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE" <!-- To write all files until API level 29. We will MediaStore from API level 30 -->
        android:maxSdkVersion="29" />

    <application
        ---------------------------
        android:requestLegacyExternalStorage="true"> 
        <!-- Need for API level 29. Scoped Storage has some issue in Android 10. So, google recommanded to add this. -->
        <!-- Read more from here: https://developer.android.com/training/data-storage/shared/media#access-other-apps-files-->
    </application>

Contribution

You can create issue(s) for any missing feature(s) that is relevant to this plugin.You can also help by pointing out any bugs. Pull requests are also welcomed

Status

This is an active project as MediaStore is the future of accessing files in android. There's a lot of rooms to improve this plugin.

Support the package (optional)

If you find this package useful, you can support it by giving it a star.

Credits

This package is developed by Shahriar Nasim Nafi

media_store_plus's People

Contributors

douglasiacovelli avatar refi64 avatar snnafi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

media_store_plus's Issues

Feature Request: Add Functionality to Convert content:// URIs to File Paths

Hello!
I am currently using your media_store_plus library in my Flutter project and have found it extremely useful for handling media files. However, I have encountered a limitation that I believe could be a valuable addition to your library.

Feature Description:
I would like to request a feature that enables the conversion of content:// URIs to actual file system paths. As of now, the library provides content:// URIs for media files, but there's no straightforward way to convert these URIs into file paths within Flutter.

Use Case:
In my application, I am retrieving media files using media_store_plus, which returns the media in the form of content:// URIs. For specific operations, I need to access the actual file path. This is a common scenario for apps that need more direct interaction with the file system.

Proposed Solution:
It would be immensely helpful if media_store_plus could include a method to convert content:// URIs to file paths. This function could either be a direct URI-to-path conversion method or an enhancement in the existing file retrieval methods to include an option to obtain the file path directly.

Thank you for considering this feature request. I am looking forward to any updates on this and would be happy to contribute to the discussion if needed.

Best regards,
Lorenzo

MediaStorePlugin.saveFile error with API 28

Hello again ๐Ÿ˜ƒ

Wtih API 28 I got this error if MediaStore.appFolder does not exist when calling mediaStorePlugin.saveFile.
In my case MediaStore.appFolder is 'Beep & Speech'

E/flutter (11402): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: FileSystemException: Cannot copy file to '/storage/emulated/0/Notifications/Beep & Speech/Toto.wav', path = '/data/user/0/fr.hyperburst.beepandspeech.beep_and_speech/app_flutter/beep_and_speech_data/Toto.wav' (OS Error: No such file or directory, errno = 2)
E/flutter (11402): #0      _File.copy.<anonymous closure> (dart:io/file_impl.dart:328:9)
E/flutter (11402): <asynchronous suspension>
E/flutter (11402): #1      MediaStore.saveFile (package:media_store_plus/media_store_plus.dart:82:19)
E/flutter (11402): <asynchronous suspension>
await mediaStorePlugin.saveFile(
        tempFilePath: notificationFile.path,
        dirType: DirType.audio,
        dirName: DirName.notifications)

DocumentTree fromJson

The fromJson method in the class DocumentTree takes encoded data and therefore has to start with jsonDecode(data), see

factory DocumentTree.fromJson(dynamic data) {
    final json = jsonDecode(data);
...
}

In contrast, the Document.fromJson method takes an unencoded Map<String, dynamic> data.
That's, I believe, the usually expected way to use fromJson.

It would be nice to have this consistent and according to usual norms of the "fromJson" method.

In addition, it would be nice to have a "toJson" method as well for both, Document and DocumentTree.

For Document, something like:

Map<String, dynamic> toJson() => {
        'uri_string': uriString,
        'name': name,
        'is_virtual': isVirtual,
        'is_directory': isDirectory,
        'file_type': fileType,
        'last_modified': lastModified,
        'file_length': fileLength,
        'is_writable': isWritable,
        'is_deletable': isDeletable
      };

For DocumentTree:

Map<String, dynamic> toJson() => {
        'uri_string': uriString,
        'children': children
            .map((doc) => doc.toJson())
            .toList(),
      };

Again, thanks a lot for this package!

saveFile fails when path contains spaces

Hello.

saveFile fails when path contains spaces:

MediaStore.appFolder = "Beep & Speech";

await mediaStorePlugin.saveFile(
        tempFilePath: '/data/user/0/fr.hyperburst.beepandspeech/app_flutter/beep_and_speech_data/Sound one.wav',
        dirType: DirType.audio,
        dirName: DirName.notifications
);

Return:

E/Exception(26239): java.lang.NullPointerException
E/Exception(26239): 	at com.snnafi.media_store_plus.MediaStorePlusPlugin.createOrUpdateFile(MediaStorePlusPlugin.kt:251)
E/Exception(26239): 	at com.snnafi.media_store_plus.MediaStorePlusPlugin.saveFile(MediaStorePlusPlugin.kt:182)
E/Exception(26239): 	at com.snnafi.media_store_plus.MediaStorePlusPlugin.onMethodCall(MediaStorePlusPlugin.kt:67)
E/Exception(26239): 	at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:262)
E/Exception(26239): 	at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
E/Exception(26239): 	at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:319)
E/Exception(26239): 	at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
E/Exception(26239): 	at android.os.Handler.handleCallback(Handler.java:938)
E/Exception(26239): 	at android.os.Handler.dispatchMessage(Handler.java:99)
E/Exception(26239): 	at android.os.Looper.loop(Looper.java:246)
E/Exception(26239): 	at android.app.ActivityThread.main(ActivityThread.java:8653)
E/Exception(26239): 	at java.lang.reflect.Method.invoke(Native Method)
E/Exception(26239): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
E/Exception(26239): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)

If I replace spaces with _, everything works fine. :
'/data/user/0/fr.hyperburst.beepandspeech/app_flutter/beep_and_speech_data/Sound_one.wav'

How to not crash in a shared iOS/Android project?

Receiving this error when following the example code:

flutter: MissingPluginException(No implementation found for method getPlatformSDKInt on channel media_store_plus)

Any help would be appreciated

java.lang.NullPointerException

When trying to save image file, encountered NullPointerException in device of sdk 29.

However when dirType is other than photo, its working as expected.

Screenshot 2024-01-19 at 1 03 38โ€ฏPM

MediaStore.saveFile.uri return null only on release mode

Hello. I need help for this issue.

i use your plugin to save my file to download folder.
this is my code

if(mediaStore == null){
  mediaStore = MediaStore();
}

MediaStore.appFolder = "Digital Office";

Directory directory = await getApplicationSupportDirectory();
File tempFile = File("${directory.path}/$fileName");
tempFile.writeAsBytesSync(file.readAsBytesSync());

  final SaveInfo? saveInfo = await mediaStore!.saveFile(
    tempFilePath: tempFile.path,
    dirType: DirType.download,
    dirName: DirType.download.defaults,
  );

  if(saveInfo != null){
    var uri = saveInfo!.uri.toString();
    fileLocation = (await mediaStore!.getFilePathFromUri(uriString: uri))!;
  }else{
    fileLocation = null;
  }

return fileLocation;

this code works well when i run it on debug mode both on emulator and real device.
but if i build the app on release mode, saveInfo.uri return null on API 30 above.

can you fix this issue.
Thanks you

Save file to the download folder Android, SDK 24 - SDK 29

Hello! Thank you for your lib. I faced with some problem for SDK 24 and possible for SDK 29. I'm trying to save image in the download folder, but get the follow exception:
I/flutter ( 7776): Error: PathNotFoundException: Cannot copy file to '/storage/emulated/0/Download/MemeBattle/meme_1712858537978.png', path = '%2Fdata%2Fuser%2F0%2Fcom.memebattle.app%2Fapp_flutter%2Fmeme_1712858537978.png' (OS Error: No such file or directory, errno = 2)

I have already updated libs to the 0.0.8, but it didn't help me. This is my code:

  /// Download meme to the download folder.
  Future<void> downloadMeme(int memeId) async {
    final localizations = AppLocalizations.of(context)!;
    try {
      _increaseShareCounterForBadge(memeId);
      if (Platform.isIOS) {
        // iOS saving to photos library
        final String fileName = 'meme_${DateTime.now().millisecondsSinceEpoch}.png';
        final bytes = await _captureAndSave(); // Capture meme bytes.
        final Uint8List data = Uint8List.fromList(bytes);
        await ImageGallerySaver.saveImage(data, name: fileName);
        // Show a success message or handle further actions
        setState(() {
          showSnackbar(context, localizations.utils_download_success_ios);
        });
        return Future.value();
      } else if (Platform.isAndroid) {
        await requestPermission();

        // Get download folder and prepare for downloading.
        final Directory innerMemory = await getApplicationDocumentsDirectory();
        final String fileName = 'meme_${DateTime.now().millisecondsSinceEpoch}.png';

        // Save file to the internal memory.
        final bytes = await _captureAndSave(); // Capture meme bytes.
        final Uint8List data = Uint8List.fromList(bytes);
        final File file = File('${innerMemory.path}/$fileName');
        await file.writeAsBytes(data);

        // Save to the download folder. 
        final result = **await mediaStorePlugin?.saveFile(** // <--- exception happens here.
          tempFilePath: file.path,
          dirType: DirType.download,
          dirName: DirName.download,
          relativePath: 'MemeBattle',
        );
        // Show a success message or handle further actions
        setState(() {
          showSnackbar(context, localizations.utils_download_success_android);
        });
      }
    } catch (e) {
      setState(() {
        showSnackbar(context, localizations.utils_download_fail);
      });
    }
  }

Can you please help me with it? Maybe I used it incorrectly? Anyway, I'm not a strong flutter developer and can not reimport you lib, investigate and fix. It's just a hobby.

P.S. All permissions are granted and it works for SDK 34

Something wrong in passing on DocumentTree and Document json data from native code

In the file document_tree.dart, I had to correct the .fromJson methods in release mode (it worked without the adjustments in debug mode).
I got the error message:
Unhandled Exception: type 'Null' is not a subtype of type 'List' in type cast new DocumentTree.fromJson (package:media_store_plus/src/document_tree.dart:16)

In the DocumentTree class description, I had to use json["b"] // instead of json["children"]

factory DocumentTree.fromJson(dynamic data) {
    final json = jsonDecode(data);
    var children =
        (json["b"] // instead of json["children"] 
        as List<dynamic>).map((e) => Document.fromJson(e)).toList();
    return DocumentTree(uriString: json["uri_string"], children: children);
  }

and in the Document class description data["a"], // instead of data["name"]

  factory Document.fromJson(Map<String, dynamic> data) => Document(
        data["uri_string"],
        data["a"], // instead of data["name"]
        data["is_virtual"],
        data["is_directory"],
        data["file_type"],
        data["last_modified"],
        data["file_length"],
        data["is_writable"],
        data["is_deletable"],
      );

[Help Wanted] Should I use it given these circumstances

Hi @SNNafi , first of all thank you for your work on this package!

If you can, could you please help me sort out what I should do given the following circumstances? I have read all the official docs, searched on StackOverflow and asked ChatGPT in all kind of ways but it's still unclear for me what should be done to migrate existing video files created by my app to the Movies directory.

My open source video diary update got rejected by Google because they won't allow the manage all files permission, which I was using to avoid such issues on Android 13. The published version of my app created a directory in the root path (Internal Storage > MyAppFolder) and saved videos there. The sdk target in manifest is targetSdkVersion 29.

From what I have seen in the migration docs, I should keep the targetSdkVersion 29, keep the android:requestLegacyExternalStorage="true" and then move the files and delete this original folder. Then after all users updated and migrated the files, I should target a newer sdk and keep saving files there (I think the Movies folder makes more sense).

This file moving process should be done with your package in order to register them in the MediaStore API? Will this work for any Android version or should I do different things for Android 10 and lower? I would like to preserve these files even if the user unninstalls the app, so I want to be able to still have access to them after a reinstall. And after the migration is complete, should I use your package to write the files?

I will be extremely grateful if you could clarify these points, my app is ready to update with plenty new features and this is the last thing holding me back. Thanks so much!

Invalid file path

When my file name has special characters such as %, Invalid file path will appear

final bool status = await mediaStore.saveFile(
          tempFilePath: tempFile.path,
          dirType: DirType.download,
          dirName: DirType.download.defaults,
          relativePath: fileSavePath);
D/DirName (15690): Download
I/StubController(15690): system app validUid:12261, isFriend:false
D/DisplayName banshu-20230706111451%.pdf(15690): null
E/Exception(15690): Invalid file path
E/Exception(15690): java.io.FileNotFoundException: Invalid file path
E/Exception(15690): 	at java.io.FileInputStream.<init>(FileInputStream.java:155)
E/Exception(15690): 	at com.snnafi.media_store_plus.MediaStorePlusPlugin.createOrUpdateFile(MediaStorePlusPlugin.kt:285)
E/Exception(15690): 	at com.snnafi.media_store_plus.MediaStorePlusPlugin.saveFile(MediaStorePlusPlugin.kt:181)
E/Exception(15690): 	at com.snnafi.media_store_plus.MediaStorePlusPlugin.onMethodCall(MediaStorePlusPlugin.kt:66)
E/Exception(15690): 	at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:258)
E/Exception(15690): 	at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
E/Exception(15690): 	at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:322)
E/Exception(15690): 	at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
E/Exception(15690): 	at android.os.Handler.handleCallback(Handler.java:900)
E/Exception(15690): 	at android.os.Handler.dispatchMessage(Handler.java:103)
E/Exception(15690): 	at android.os.Looper.loop(Looper.java:219)
E/Exception(15690): 	at android.app.ActivityThread.main(ActivityThread.java:8673)
E/Exception(15690): 	at java.lang.reflect.Method.invoke(Native Method)
E/Exception(15690): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
E/Exception(15690): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1109)

Then a file with a size of 0 B will appear
1688613966303

Reply Already Submitted Media Store Plus - App Crashes on Start when I Download Images/Videos on Home Screen

I'm Downloading some images and videos when app starts and home screen is opened. Everything works fine when I run app for first time. If I make the app go back and resume it. Then it crashes by saying the below mentioned thing.
Please help me in this regards. @SNNafi

E/AndroidRuntime (17054): FATAL EXCEPTION: android.g
E/AndroidRuntime (17054) : Process: jo.veeve.retail, PID: 17054
E/AndroidRuntime (17054): java. lang. IllegalStateException: Reply already submitted
E/AndroidRuntime (17054) :
at io.flutter .embedding.engine. dart .DartMessenger$Reply. reply (DartMessenger. java: 435)
E/AndroidRuntime (17054) :
at io.flutter .plugin. common. MethodChannel$IncomingMethodCallHandler$1.success(MethodChannel.java: 263)
E/AndroidRuntime (17054) :
at com.snnafi.media_store_plus.MediaStorePlusPlugin.uriFromFilePath$lambda$7(MediaStorePlusPlugin.kt:385)
E/AndroidRuntime (17054) :
at com.snnafi. media_store_plus. MediaStorePlusPlugin.$r8$lambda$ekw24z1euWmzTSOUwB10s2bGUA (Unknown Source: 0)
E/AndroidRuntime (17054) :
at com.snnafi.media_store_plus. MediaStorePlusPlugin$$ExternalSyntheticLambda0.onScanCompleted(Unknown
Source:2)
E/AndroidRuntime (17054) :
at android.media.MediaScannerConnection. runCallBack (MediaScannerConnection. java: 211)
E/AndroidRuntime (17054) :
at android. media. MediaScannerConnection. lambda$scanFile$1 (MediaScannerConnection. java: 188)
E/AndroidRuntime (17054) :
at android. media. MediaScannerConnection$$ExternalSyntheticLambda0.run (Unknown Source:6)
E/AndroidRuntime (17054) :
at android.os. Handler. handleCallback (Handler. java: 938)
E/AndroidRuntime (17054) :
at android.os. Handler. dispatchMessage (Handler. java: 99)
E/AndroidRuntime (17054) :
at android.os. Looper. loopOnce (Looper . java: 201)
E/AndroidRuntime (17054) :
at android.os. Looper. loop (Looper. java: 288)
E/AndroidRuntime (17054) :
at android.os. HandlerThread.run (HandlerThread. java:67)
I/Process (17054): Sending signal. PID: 17054 SIG: 9

build failed on gradle 8.0, missing namespace

build failed when using gradle 8.0 due to missing namespace, could you update the package? @SNNafi
thank you!

FAILURE: Build failed with an exception.

  • What went wrong:
    A problem occurred configuring project ':media_store_plus'.

Could not create an instance of type com.android.build.api.variant.impl.LibraryVariantBuilderImpl.
Namespace not specified. Please specify a namespace in the module's build.gradle file like so:

 android {
     namespace 'com.example.namespace'
 }

 If the package attribute is specified in the source AndroidManifest.xml, it can be migrated automatically to the namespace value in the build.gradle file using the AGP Upgrade Assistant; please refer to https://developer.android.com/studio/build/agp-upgrade-assistant for more information.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.