Code Monkey home page Code Monkey logo

Comments (8)

rolinger avatar rolinger commented on June 16, 2024 1

@breautek - final update on this one. On Android, I got everything working on compiledSDK/targetSDK 33.

  1. Download file to custom /MyApp/sub-folder1/ path in the users standard /Download folder - IE: /Download/MyApp/sub-folder1/thisFile.pdf
  2. Got around issue if user manually deleted file from /Download/MyApp/sub-folder1/ by triggering off of native Android error of EEXIST. If that error is detected, then redownload the same file and save to target with a different name (add a character 1) to the end of the filename. This gets around the Android scoping issue without having to get into complex user permissions or related issues.
  3. Finally, using cordova-plugin-file-opener2, I can display an open file button in my app that will auto-launch a app picker for the user to select which app to use to open the file with. In THOSE apps, they ask for permission access to files downloaded by my app. The user needs to authorize my access to my app files and all is good. The same process happens if the user navigates their devices File Manager to my app folders and tries to open a file - they select the app, and if permissions not yet granted those apps will walk user through granting access.

An update, this worked with only the following permissions set in my config.xml

            <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
            <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
            <application android:requestLegacyExternalStorage="true" />

Now...just need to validate all this on iOS.

from cordova-plugin-file-transfer.

rolinger avatar rolinger commented on June 16, 2024

I figured out what is going on. If your app downloads the file and then the user manually deletes the file you cannot save the file with the same name again; this is related to some Android caching of some sort. I found this reference here:

itinance/react-native-fs#1078
https://learn.microsoft.com/en-us/answers/questions/932579/after-manually-delete-a-file-and-use-fileoutputstr

Apparently this started in Android 11+. The proper solutions offered haven't worked for me. But the hack solution does work.

If the user manually deleted the file then use the error to "EEXIST" to determine that, then redownload the file again and save it with a slightly different name...add a random character to the end of the name and it will save.

from cordova-plugin-file-transfer.

rolinger avatar rolinger commented on June 16, 2024

Whats crazy about this issue is after manually deleting the file with the phones File Manager, my app reads the directory and doesn't see the file. But when writing the file, with the same previous file name, it can't do it because Android says the file is still there. So why is one saying its not there and the other is?

from cordova-plugin-file-transfer.

breautek avatar breautek commented on June 16, 2024

It sounds like you're experiencing scoped storage rules, which is effective since API 29. I wrote a blip in a PR for the file plugin but the same would apply here.

Basically scoped storage applies to all external storage partitions (/storage/emulated/0/ is considered an external storage partition, which is used to emulate external storage when a physical medium is not available).

With scoped storage, applications do not require permission to read or write files, however it can only read and write files that the application itself has written to. So for example, if another app (Say the chrome browser) downloads a file and stores it at: /storage/emulated/0/Download/MyApp/BYG/REIA_Check_List.doc, that file will not be read or writeable by your app since the file is not owned by your app.

Listing directories will not show files that your app doesn't have access to. However attempting to write to a filename that already exists will be blocked.

There is limited capability of reading files owned by other apps with READ_EXTERNAL_STORAGE permission on pre-API 33 devices, and READ_MEDIA_AUDIO, READ_MEDIA_VIDEO, and READ_MEDIA_IMAGES for API 33+ devices. If you're observant you'll notice that there are only permissions for multimedia assets, but not nothing for document files. This is because scoped storage only allows reading media files. So reading your .doc file is not possible via a file API. Additionally, there are no write permissions for writing to files owned by other apps. You cannot modify a file owned by another app via the file api.

The file transfer plugin uses the file plugin behind the scenes, which naturally uses the file apis.

So how do work with non-media files?

Well the native way is to use a non-file API, and use something called a MediaStore. Apache doesn't have a plugin that interfaces with this MediaStore outside of context specific plugins (like the camera plugin which deals with images/video). But there are third-party plugins available that interfaces with the media store with a generic API: https://www.npmjs.com/search?q=ecosystem%3Acordova%20storage%20access%20framework

If placing these files in external storage is a requirement, then you'll probably need to use the file transfer plugin to download into your internal cache directory (cordova.file.cacheDirectory), then use a media store plugin to transfer from the internal cache directory to media store.

Lastly if you support API 29, then using MediaStore APIs is a requirement because Android lacks a filesystem API into scoped storage, which was only introduced in API 30. So accessing any external storage mechanism on API 29 is blocked.

If you think any of this sounds bizarre, then you're not alone. You can read learn more about scoped storage in the android docs

from cordova-plugin-file-transfer.

rolinger avatar rolinger commented on June 16, 2024

@breautek - As always Norman, you give very thorough explanations and I REALLY appreciate your responses and the time you put into your responses. Android needs to hire you to rewrite their docs because they suck.

Based on what you wrote above about reading scoped files I have an additional question. If using this file-transfer plugin I am able to download the files. My app doesn't need to modify/edit change the docs, just download so the user can reference/read them. If my app downloads thisFile.pdf to my own MyApp/Sub-Folder/thisFile.pdf location, can my app open that file into another app PDF viewer - it should still work yes? And if the user navigates to the file via their phones File Manager they should be able to open it in what ever app they choose correct?

So long as users can download files from my app and open them other apps without issue I am going to continue to use this plugin. It keeps it simple and clean. All I have to do now add code to download the file again, changing the name slightly, if the FileTransfer gets the error with EEXIST in it.

from cordova-plugin-file-transfer.

breautek avatar breautek commented on June 16, 2024

If my app downloads thisFile.pdf to my own MyApp/Sub-Folder/thisFile.pdf location, can my app open that file into another app PDF viewer - it should still work yes?

This part I'm not 100% sure, but I don't think it will work out of the box. The part that makes it more difficult is the fact that your file are document files, not a media file. (Media as in an image, video or audio).

Normally if you had a image file owned by App A. App B could read it without explicit permission from App A if App B has READ_EXTERNAL_STORAGE or READ_MEDIA_IMAGE permission granted.

However because you're working with document files, that convenience escape hatch is not available. In order to share document files with other apps, App A needs to implement something called a ContentProvider which can accept and grant read (or write) permission to App B.

I could be completely wrong here.... but I believe the typical flow in that kind of use case is that:

  1. App B uses a file picker intent to open up a document tree where they may pick your App A's thisFile.pdf.
  2. This will will trigger a ContentProvider request implemented by App A where
  3. App A can decide to grant temporary permission to App B.
  4. App B can then read the document file to use.

Cordova doesn't implement any content providers which is why I don't think it will just work. And I think implementing one will be difficult since the details of a content provider will be largely app-specific.

I don't have a complete understanding on the content provider concepts in Android, it's not something I have actually dabbled with. But it is the mechanism for sharing content between apps, from what I can understand.

from cordova-plugin-file-transfer.

rolinger avatar rolinger commented on June 16, 2024

@breautek - well....good news. Changing the name allows for redownload of file. And after downloading, navigating to folder with phones general file manager allowed me to open the file with another app. However, the other app did require permissions to access the file; it opens dialog "Go To Settings", which then opens to "All Files Access" forcing the user to allow access for the app they choose to view the file with. Tried it on several different app types and it was the same for all of them.

The equally good news is that the other app asks for the permissions, so I don't have to code in my app giving permissions. I don't want the user viewing files in my app anyway...too many issues rendering different file formats in my app - just easier to let native apps do it.

function priFileDL(fInfo){
  startStatus(fInfo.cfID,"black","Starting download...") ;

  var fileTransfer = new FileTransfer();
  var folderPath=fInfo.dir+fInfo.cfSave ;
  
  var onSuccess= function(e){
    fileDone(fInfo,e) ;
    if (ionic.Platform.isIOS()) {
      fileMove(folderPath) ;
    }
  };

  var onError=function(e) {
    // this handle issue if user had downloaded file before
    // and then manually deleted it from a file manager
    // this appends the name, making it a unique download
    if (e.exception.includes("EEXIST")) {
      var n = fInfo.cfSave.split(".") ;
      n[0] += "1" ;
      fInfo.cfSave = n.join(".") ;
      priFileDL(fInfo) ;
    } else {
      fileError(1,e,fInfo) ;
    }
  };
  
  fileTransfer.onprogress = function(pe) {
    fileProgress(pe,fInfo) ;
  }
  
  fileTransfer.download(fInfo.url,folderPath,onSuccess,onError);
}  

from cordova-plugin-file-transfer.

rolinger avatar rolinger commented on June 16, 2024

@breautek - btw...all of this was on Android. I haven't tested iOS yet. The other issue now is how a user, from within my app, can click a button that then opens the file in another app using some kind of app picker. Trying cordova-plugin-file-opener2 as a first stab at it, but so far no luck.

from cordova-plugin-file-transfer.

Related Issues (20)

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.