SNNafi / media_store_plus

To use Android MediaStore API in Flutter
https://pub.dev/packages/media_store_plus
MIT License
33 stars 17 forks source link

[Help Wanted] Should I use it given these circumstances #1

Closed KyleKun closed 1 year ago

KyleKun commented 1 year ago

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!

SNNafi commented 1 year ago

Yes, It will work with any android version. I have tested this with an Android 10 device. So it should work with the lower versions as well.

Yes, you should keep these two, android:requestLegacyExternalStorage="true" and android:maxSdkVersion="29". They are needed especially for android 10.

KyleKun commented 1 year ago

Hi @SNNafi , sorry to bother you again,

I am trying to use saveFile method and getting this error (the first file is copied successfully and then it throws the error):

D/MimeType( 1530): video/mp4
D/DirName ( 1530): DCIM
D/DisplayName 2021-05-07.mp4( 1530): null
D/saveFile( 1530): 2021-05-07.mp4
D/MimeType( 1530): video/mp4
D/DirName ( 1530): DCIM
D/DisplayName 2021-04-17.mp4( 1530): null
E/Exception( 1530): UNIQUE constraint failed: files._data (code 2067 SQLITE_CONSTRAINT_UNIQUE[2067])
E/Exception( 1530): android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: files._data (code 2067 SQLITE_CONSTRAINT_UNIQUE[2067])
E/Exception( 1530):     at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:178)
E/Exception( 1530):     at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
E/Exception( 1530):     at android.content.ContentProviderProxy.update(ContentProviderNative.java:653)
E/Exception( 1530):     at android.content.ContentResolver.update(ContentResolver.java:2407)
E/Exception( 1530):     at android.content.ContentResolver.update(ContentResolver.java:2369)
E/Exception( 1530):     at com.snnafi.media_store_plus.MediaStorePlusPlugin.createOrUpdateFile(MediaStorePlusPlugin.kt:287)
E/Exception( 1530):     at com.snnafi.media_store_plus.MediaStorePlusPlugin.saveFile(MediaStorePlusPlugin.kt:182)
E/Exception( 1530):     at com.snnafi.media_store_plus.MediaStorePlusPlugin.onMethodCall(MediaStorePlusPlugin.kt:67)       
E/Exception( 1530):     at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:262)

My code:

          MediaStore.appFolder = 'OneSecondDiary';
          final mediaStorePlugin = MediaStore();

          // Map all files inside old folders
          final oldFolderFiles = await oldAppFolder.list(recursive: true).toList();

          // Loop through all files and copy them to new folder (DCIM/OneSecondDiary)
          await Future.forEach(oldFolderFiles, (file) async {

            if (file is io.File && file.path.endsWith('.mp4')) {
                  final String tempFile = '${internalDirectoryPath.path}/${file.path.split('/').last}';
                  await file.copy(tempFile);

                  await mediaStorePlugin.saveFile(
                    tempFilePath: tempFile,
                    dirType: DirType.video,
                    dirName: DirName.dcim,
                 );
            }
         }

Any ideas what it might be? Thanks!

SNNafi commented 1 year ago

@KyleKun , please use the latest version i.e 0.0.5, as there is a fix for below API level 29 while saving files. Tested in API 27, and 28 personally. Thanks to @Fifut for the initial find-out.

SNNafi commented 1 year ago

Sorry, missed your earlier comment. It says the column files._data's value is not unique in the media store database. But it must be unique. Basically, the media store inserts an entry for each file(file info, permission level, etc) in its internal SQLite database. In this way, it enforces the new permissions.

KyleKun commented 1 year ago

Sorry, missed your earlier comment. It says the column files._data's value is not unique in the media store database. But it must be unique. Basically, the media store inserts an entry for each file(file info, permission level, etc) in its internal SQLite database. In this way, it enforces the new permissions.

Don't worry, I managed to solve the problem restarting the device. It seems like some Android security measure, after you manually delete a file, checking if it exists returns false but the id is still stored in the sqlite database, and you can't save the file with the same path again (works after the restart).

The package is working perfectly on my app, and thanks for the new version with that other fix!

SNNafi commented 1 year ago

It should not return false. I am deleting the previous entry by searching the display name from the DB. Then add the new entry. So, I think there are more cases to handle.

https://github.com/SNNafi/media_store_plus/blob/1a778a33ae952174b3483194105151719bc5436a/android/src/main/kotlin/com/snnafi/media_store_plus/MediaStorePlusPlugin.kt#L269

KyleKun commented 1 year ago

It should not return false. I am deleting the previous entry by searching the display name from the DB. Then add the new entry. So, I think there are more cases to handle.

https://github.com/SNNafi/media_store_plus/blob/1a778a33ae952174b3483194105151719bc5436a/android/src/main/kotlin/com/snnafi/media_store_plus/MediaStorePlusPlugin.kt#L269

Oh, the deletion with the package works fine. I meant manually deleting the file in the file explorer. If you do it and check if the file exists with default File from dart:io, it says the file does not exist (which is expected), but the entry is still in the sqlite so you can't save it again without restarting. See this for reference.

SNNafi commented 1 year ago

Yes. I am also talking about this. If you delete a file manually, it will not delete it from the DB entry. So, while saving a file, I am checking if a file with the same name exists in the DB entry (not the actual file. It should be handled by the package user). If it exists with the same name, then I just delete the entry as we can't save a new entry in the DB with the same name.

SNNafi commented 1 year ago

However, I will try to check if there is any other way to improve this plugin. The above case should be handled by the plugin. We can't just ask a user to restart his/her device for just saving a file that he/she deleted earlier.