Git Product home page Git Product logo

react-native-scoped-storage's Introduction

react-native-scoped-storage

Since the release of Android 10, Google has made Scoped Storage the default way to access and read/write files on an Android device but up until Android 11, it was possible to override this by putting android:requestLegacyExternalStorage="true" in AndroidManifest.xml.

However, this is changing fast since the start of 2021. Google is enforcing all apps to use Scoped Storage to store or read files on a user's device. This library provides an API for react-native to use scoped storage in their apps. Scoped storage allows you to prompt the user that you need access to some file/folders. The user can the allow access to their desired location. It is your responsibility to store the uri you recieve for later use.

Remember, you do not need WRITE_EXTERNAL_STORAGE permission in you AndroidManifest.xml file using this library.

Until React Native targets API 29, you do not need this library. But if you are targeting API 30, it's not possible to access user's files and folders without scoped storage.

Read my blog: Scoped Storage in React Native: New Android 10 API for File System Access

Getting started

Install the library

yarn add react-native-scoped-storage

or

npm install react-native-scoped-storage

How this works

Unlike regular file storage on android, scoped storage works differently in that you can only have access to those parts of user's phone storage where the user has allowed you to read/write. Let's see an example:

We will ask the user to give us permission to a folder on their phone were we can read/write data. This folder can be anywhere in the phone. For this purpose will will launch the phone's file manager

import * as ScopedStorage from "react-native-scoped-storage"

let dir = await ScopedStorage.openDocumentTree(true);

Once the user selects a directory, we will recieve the information about the directory and its uri. Now we can use this uri to read/write.

await ScopedStorage.writeFile(dir.uri,"myimage.png","image/png",imageData,"base64");

// We can store this directory in AsyncStorage for later use.
await AsyncStorage.setItem('userMediaDirectory',JSON.stringify(dir));

And later if we want to access the uris where we have access to read/write:

// Get the directory we requested earlier
let dir = await AsyncStorage.getItem("userMediaDirectory");
dir = JSON.parse(dir);

// Get list of persisted Uris
const persistedUris = await ScopedStorage.getPersistedUriPermissions();

// Check if the directory uri exists in the list of uris where we have access to read/write.
if (persistedUris.indexOf(dir.uri) !== -1) {

    // If uri is found, we can proceed to write/read data.
    await ScopedStorage.writeFile(dir.uri,"myimage.png","image/png",imageData,"base64");
} else {
    // We can request for permission again and store the new directory if access has been revoked by user here.
}

Asking for directory to store file everytime

This works similar to it does in the browser, if you try to save an image or any asset from the webpage, it will ask you for a location where this file should be stored everytime.

import * as ScopedStorage from "react-native-scoped-storage"

let file = await ScopedStorage.createDocument("myimage.png","image/png",imageBase64Data,"base64");

// Remember that the returned file can have a different name than you provide because user can change it upon saving to a folder.

Table of contents

Type aliases

Functions

Type aliases

FileType

Ƭ FileType: Object

Type declaration

Name Type Description
data string data read from the file
lastModified number Last modified date of the file or directory
mime string mime type of the file
name string Name of the file or directory
path string Storage path for the file
type "file" | "directory" -
uri string Document Tree Uri for the file or directory

Defined in

index.ts:5

Functions

copyFile

copyFile(source, destination, callback): Promise<void>

Create a new file at the given directory.

Parameters

Name Type Description
source string Source file (Supports file:// & content:// uris)
destination string Destination file (Supports file:// & content:// uris)
callback () => void -

Returns

Promise<void>

Defined in

index.ts:170


createDirectory

createDirectory(uri, dirName): Promise<FileType>

Create a directory at the given path.

Parameters

Name Type Description
uri string -
dirName string Name of the new directory

Returns

Promise<FileType>

Defined in

index.ts:117


createDocument

createDocument(fileName, mime, data, encoding?): Promise<FileType>

Open Document picker to create a file at the user specified location.

Parameters

Name Type Description
fileName string Name of the file to create.
mime string mime of the file to create. eg image/jpeg
data string Data to write to the file once it is created.
encoding? "utf8" | "base64" | "ascii" Encoding of the dat you are writing.

Returns

Promise<FileType>

Defined in

index.ts:36


createFile

createFile(uri, fileName, mime): Promise<FileType>

Create a new file at the given directory.

Parameters

Name Type Description
uri string -
fileName string Name of the new file.
mime string Mime type of the file, e.g. image/jpeg

Returns

Promise<FileType>

Defined in

index.ts:155


deleteFile

deleteFile(uri): Promise<boolean>

Delete a file or directory at the given path.

Parameters

Name Type Description
uri string Path to the file or directory to delete

Returns

Promise<boolean>

Defined in

index.ts:109


getPersistedUriPermissions

getPersistedUriPermissions(): Promise<string[]>

There is a limit to the number of uri permissions your app can persist. Get a list of all the persisted document tree uris so you are remove the ones you are not using or perform other operations.

Returns

Promise<string[]>

Defined in

index.ts:65


listFiles

listFiles(uri): Promise<FileType[]>

List all files and folders in a directory uri

Parameters

Name Type Description
uri string Path to a directory.

Returns

Promise<FileType[]>

Defined in

index.ts:81


openDocument

openDocument(readData?, encoding?): Promise<FileType>

Open Document picker for the user to select a file.

Parameters

Name Type Default value Description
readData boolean false Do you want to read data from the user specified file?
encoding? "utf8" | "base64" | "ascii" undefined Encoding for the file you are reading.

Returns

Promise<FileType>

Defined in

index.ts:55


openDocumentTree

openDocumentTree(persist?): Promise<FileType>

Open the Document Picker to select a folder. Read/Write Permission will be granted to the selected folder.

Parameters

Name Type Default value
persist boolean false

Returns

Promise<FileType>

Defined in

index.ts:25


readFile

readFile(uri, encoding?): Promise<string>

Read file at a given path. The path of the file must be a document tree uri.

Parameters

Name Type Description
uri string Path to the file you want to read.
encoding? "utf8" | "base64" | "ascii" Encoding for the file you are reading.

Returns

Promise<string>

Defined in

index.ts:90


releasePersistableUriPermission

releasePersistableUriPermission(uri): Promise<void>

Remove a uri from persisted uri list.

Parameters

Name Type Description
uri string The uri you want to remove from persisted uri permissions.

Returns

Promise<void>

Defined in

index.ts:73


rename

rename(uri, name): Promise<string>

Rename a file or directory at the given path.

Parameters

Name Type Description
uri string Path to the file or directory to rename
name string New name for the file or directory

Returns

Promise<string>

Defined in

index.ts:102


stat

stat(uri): Promise<any>

Get details for a file/directory at a given uri.

Parameters

Name Type
uri string

Returns

Promise<any>

Defined in

index.ts:182


writeFile

writeFile(uri, data, fileName?, mime?, encoding?, append?): Promise<string>

Write to a file at the give directory. If the file does not exist, it will be created.

Parameters

Name Type Default value Description
uri string undefined -
data string undefined Data you want to write
fileName? string undefined Name of the file (Optional if writing to an existing file)
mime? string undefined Mime of the file. eg image/jpeg (Optional if writing to an existing file)
encoding? "utf8" | "base64" | "ascii" undefined Encoding of the data you are writing.
append boolean false Should the data be appended to the existing data in file?

Returns

Promise<string>

Defined in

index.ts:132

Thanks to

  • rn-fetch-blob for the amazing library. Some part of code is taken from there.

I want to contribute

That is awesome news! There is alot happening at a very fast pace in this library right now. Every little help is precious. You can contribute in many ways:

  • Suggest code improvements on native iOS and Android
  • If you have suggestion or idea you want to discuss, open an issue.
  • Open an issue if you want to make a pull request, and tell me what you want to improve or add so we can discuss

License

This library is licensed under the MIT license.

Copyright © Ammar Ahmed (@ammarahm-ed)

Notesnook Logo

react-native-scoped-storage's People

Contributors

ammarahm-ed avatar eduardofrausto avatar penghouho avatar rembo10 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  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  avatar  avatar  avatar

react-native-scoped-storage's Issues

deleteFile deletes the entire directory it is in when attempting to only delete one file

When I call deleteFile with:

await ScopedStorage.deleteFile('content://com.android.externalstorage.documents/tree/01F6-AC3F%3APodverse/document/01F6-AC3F%3APodverse/esgi0q1Pm.mp3')

RNSS deletes the entire directory instead of just deleting the .mp3 file. Is my content uri structured incorrectly?

It would be a disaster if we accidentally deleted a directory for the user. Shouldn't deleteFile and deleteDirectory be separate functions? That way, in the worst case, deleteFile could be safe-guarded to make sure deleting a directory by accident is not possible.

Getting access denied error.

I'm trying to list the file names from a particular directory using the listFiles method. It is giving no permission error.

  • Directory trying to read - /storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/.Statuses

Error message

/storage/emulated/0/Android/media/com.whatsapp/WhatsApp/Media/.Statuses do not have permission to read/write. Is there any other prerequisite I need to do to get the access?

getPersistedUriPermissions returned is difference from openDocumentTree value

Value returned from
await ScopedStorage.getPersistedUriPermissions()
["content://com.android.externalstorage.documents/tree/primary%3ADownload%2FMyCompany"]

Value returned from
await ScopedStorage.openDocumentTree(true)
{"lastModified":1674134539000,"name":"MyCompany","type":"directory","uri":"content://com.android.externalstorage.documents/tree/primary%3ADownload%2FMyCompany/document/primary%3ADownload%2FMyCompany"}

The /primary%3ADownload%2FMyCompany is added additionally

How do I get a uri for application folder that the system provides?

The APIs in this lib require uris to directories in order to write files. But there is no indication where to get any user folder uris, such as Documents, Downloads, or even an App directory where to write cache files.

Where can I get those?
I am looking for something similar to Expo's FileSystem.cacheDirectory.

Not able to run this respository after cloning the same following is the error

Last login: Thu Aug 26 19:30:45 on ttys001
fsd2@Nikhils-MacBook-Pro ~ % /Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/react-native/scripts/launchPackager.command ; exit;

                    #######                       
               ################                   
            #########     #########               
        #########             ##########          
    #########        ######        #########      
   ##########################################     
  #####      #####################       #####    
  #####          ##############          #####    
  #####    ###       ######       ###    #####    
  #####    #######            #######    #####    
  #####    ###########    ###########    #####    
  #####    ##########################    #####    
  #####    ##########################    #####    
  #####      ######################     ######    
   ######        #############        #######     
     #########        ####       #########        
          #########          #########            
              ######### #########                 
                   #########                      
                                                  
                                                  
                Welcome to Metro!
          Fast - Scalable - Integrated

To reload the app press "r"
To open developer menu press "d"

BUNDLE ./index.js

error: Error: While trying to resolve module react-native-scoped-storage from file /Users/fsd/Downloads/react-native-scoped-storage-master/example/App.tsx, the package /Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/react-native-scoped-storage/package.json was successfully found. However, this package itself specifies a main module field that could not be resolved (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/react-native-scoped-storage/index.js. Indeed, none of these files exist:

  • /Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/react-native-scoped-storage/index.js(.native|.android.js|.native.js|.js|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)
  • /Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/react-native-scoped-storage/index.js/index(.native|.android.js|.native.js|.js|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)
    at DependencyGraph.resolveDependency (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/node-haste/DependencyGraph.js:436:17)
    at Object.resolve (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/lib/transformHelpers.js:317:42)
    at resolve (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:629:33)
    at /Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:645:26
    at Array.reduce ()
    at resolveDependencies (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:644:33)
    at /Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:329:33
    at Generator.next ()
    at asyncGeneratorStep (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:137:24)
    at _next (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:159:9)
    info Reloading app...
    BUNDLE ./index.tsx

error: Error: While trying to resolve module react-native-scoped-storage from file /Users/fsd/Downloads/react-native-scoped-storage-master/example/App.tsx, the package /Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/react-native-scoped-storage/package.json was successfully found. However, this package itself specifies a main module field that could not be resolved (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/react-native-scoped-storage/index.js. Indeed, none of these files exist:

  • /Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/react-native-scoped-storage/index.js(.native|.android.js|.native.js|.js|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)
  • /Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/react-native-scoped-storage/index.js/index(.native|.android.js|.native.js|.js|.android.json|.native.json|.json|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx)
    at DependencyGraph.resolveDependency (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/node-haste/DependencyGraph.js:436:17)
    at Object.resolve (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/lib/transformHelpers.js:317:42)
    at resolve (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:629:33)
    at /Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:645:26
    at Array.reduce ()
    at resolveDependencies (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:644:33)
    at /Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:329:33
    at Generator.next ()
    at asyncGeneratorStep (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:137:24)
    at _next (/Users/fsd/Downloads/react-native-scoped-storage-master/example/node_modules/metro/src/DeltaBundler/traverseDependencies.js:159:9)

When I delete the app, the saved file remains

In scoped storage, I know that when the app is deleted, the files downloaded from the app are also deleted.
After i use this library to save my files, the files remain even i delete the app. Can you tell me what's happening?

this is what i wanted, but i want know why

Todo

Implement the following methods

  • copyFile(to:string,from:string): copy a file from one directory to another
  • moveFile(to:string,from:string): move a file from on location to another
  • stat(uri:string): list info about a file/directory for a given uri

readFile return is not a file even when it is a file

I'm using readFile to read a file but it seems that

dir.isFile() returns false even when it is a file.

After debugging isFile() reads muri which is returned something like this content://com.android.externalstorage.documents/tree/primary%3Aredacted/tree/primary%3Aredacted/.

file path passed is content://com.android.externalstorage.documents/tree/primary%3Aredacted/redacted--redacted.json.

Please let me know if I'm doing anything wrong.

Questions about Read/Write access

Hello, thx for awesome lib. I'm having a problem using this. My question is if using the function openDocumentTree i will be able to use another library that i will have read and write access without any problem.

Example:

Using openDocumentTree in path:
"/storage/emulated/0/Android/media",

this return
{"lastModified": 1641555076000, "name": "media", "path": "/storage/emulated/0/Android/media", "type": "directory", "uri": "content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fmedia"}

So, this means that I will have read and write access to the above path and all its subdirectories ? I'm trying to use this library to get the permissions, and soon after I'll use another lib (RNFetchBlob) to read files in 'Android/Media/....Statuses' from Whatsapp Path.

I'm using:

"react-native-blob-util": "^0.13.17",
"react-native-scoped-storage": "^1.9.1",
"react-native": "0.66.3",

Thx a lot.

No Activity found to handle Intent for createDocument and openDocument

FATAL EXCEPTION: mqt_native_modules

android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.CREATE_DOCUMENT }

FATAL EXCEPTION: mqt_native_modules
Process: com.ahangap.app, PID: 10760
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.CREATE_DOCUMENT }
	at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2067)
	at android.app.Instrumentation.execStartActivity(Instrumentation.java:1727)
	at android.app.Activity.startActivityForResult(Activity.java:5320)
	at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:675)
	at android.app.Activity.startActivityForResult(Activity.java:5278)
	at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:662)
	at com.ammarahmed.scopedstorage.RNScopedStorageModule.createDocument(RNScopedStorageModule.java:297)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
	at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:151)
	at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
	at android.os.Handler.handleCallback(Handler.java:938)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
	at android.os.Looper.loop(Looper.java:223)
	at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:226)
	at java.lang.Thread.run(Thread.java:923)
FATAL EXCEPTION: mqt_native_modules

android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.OPEN_DOCUMENT }

FATAL EXCEPTION: mqt_native_modules
Process: com.ahangap.app, PID: 11102
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.OPEN_DOCUMENT }
	at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2067)
	at android.app.Instrumentation.execStartActivity(Instrumentation.java:1727)
	at android.app.Activity.startActivityForResult(Activity.java:5320)
	at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:675)
	at android.app.Activity.startActivityForResult(Activity.java:5278)
	at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:662)
	at com.ammarahmed.scopedstorage.RNScopedStorageModule.openDocument(RNScopedStorageModule.java:355)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
	at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:151)
	at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
	at android.os.Handler.handleCallback(Handler.java:938)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
	at android.os.Looper.loop(Looper.java:223)
	at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:226)
	at java.lang.Thread.run(Thread.java:923)

Unexpeted behaviour while writting to a file

Hi,

There seems to be a issue writing to already existing files when the text to be written is smaller that the text that already exists on the file.

For example, with the mode currently in use w, let there be a file called foo.txt with the text foobar;
If we try to write to that file, the string foo, there wont be any changes in the file contents, the content will still be foobar.

Now with the mode wt, the file contents now change to be foo as expected.

This was only tested on android 12 but I believe this behavior happens for android 10+ has the following link states.

"Error: does not have permission to read/write"

Even after calling
let dir = await ScopedStorage.openDocumentTree(true)
and getting permission to specific directory it doesn't allow to read file but can be write without any problem.
Using same uri which i got from writing if I use that to read it gives me the permission error. Below I have provided my code snippet.

const testFunc = async () => {
      try {
        let dir = await ScopedStorage.openDocumentTree(true);
        const jsonData = await JSON.stringify(state, null, 2);
        const fileName = `data-${authInfo.phoneNum}`;

        const createDir = await ScopedStorage.createDirectory(
          dir.uri,
          '.hkbackup',
        );

        console.log('=======createDir=============================');
        console.log(createDir);
        console.log('==========createDir==========================');

        const wroteFile = await ScopedStorage.writeFile(
          createDir.uri,
          fileName,
          'application/octet-stream', // 'application/json',
          jsonData,
          'utf-8',
        );
        console.log('===============wroteFile=====================');
        console.log(wroteFile);
        console.log('=============wroteFile=======================');
        const stat = await ScopedStorage.stat(wroteFile);

        console.log('==========stat==========================');
        console.log(stat);
        console.log('============stat========================');

        const readDoc = await ScopedStorage.openDocument(true, 'utf8');

        console.log('=======readDoc=============================');
        console.log(readDoc);
        console.log(readDoc.uri);
        console.log('=========readDoc===========================');

        const readFile = await ScopedStorage.readFile(readDoc.uri, 'utf8');

        console.log('=======readFile=============================');
        console.log(readFile);
        console.log('=========readFile===========================');
      } catch (error) {
        console.log('===============error=====================');
        console.log(error);
        console.log('==============error======================');
      }
    };
    testFunc();

Error given below

  ```

console.log('===============error=====================');

Error: 'content://com.android.externalstorage.documents/document/primary%3ADocuments%2F.hkbackup%2F.hkbackup%2Fdata-01758515157'does not have permission to read/write
at Object.promiseMethodWrapper [as readFile] (D:\Nanoid\halkhata-app\node_modules\react-native\Libraries\BatchedBridge\NativeModules.js:103)
at Object. (D:\Nanoid\halkhata-app\node_modules\react-native-scoped-storage\dist\index.js:128)
at step (D:\Nanoid\halkhata-app\node_modules\react-native-scoped-storage\dist\index.js:32)
at Object.next (D:\Nanoid\halkhata-app\node_modules\react-native-scoped-storage\dist\index.js:13)
at D:\Nanoid\halkhata-app\node_modules\react-native-scoped-storage\dist\index.js:7
at tryCallTwo (D:\Nanoid\halkhata-app\node_modules\react-native\node_modules\promise\setimmediate\core.js:45)
at doResolve (D:\Nanoid\halkhata-app\node_modules\react-native\node_modules\promise\setimmediate\core.js:200)
at new Promise (D:\Nanoid\halkhata-app\node_modules\react-native\node_modules\promise\setimmediate\core.js:66)
at __awaiter (D:\Nanoid\halkhata-app\node_modules\react-native-scoped-storage\dist\index.js:3)
at Object.readFile (D:\Nanoid\halkhata-app\node_modules\react-native-scoped-storage\dist\index.js:125)

console.log('==============error======================');

 

Android 11 - Unable to create directory in external storage

Hi Team,

'/storage/emulated/0'does not have permission to create directories - This error is occurred by using below code for creating the directory in external storage.

createDirectory=async()=>{

const path = ${RNFS.ExternalStorageDirectoryPath};
try {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE );
if (granted == PermissionsAndroid.RESULTS.GRANTED)
{
ScopedStorage.createDirectory(path, "TEST").then((obj) => {
alert(obj)
})
.catch(error => {
console.log("ERROR" + error)
})

        } else {
            console.log('permission denied');
        }
    } catch (err) {
        console.warn(err);
    }

}

"ACTIVITY_NOT_FOUND" on android 11

It seems that now you are only considering cases where the user is asking for Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION. Did I assumed it correctly?

This part of the code is holding the scenario of opening the file manager.
if (intent.resolveActivity(reactContext.getPackageManager()) == null) { promise.reject("ACTIVITY_NOT_FOUND", "No activity fount to handle scoped storage access"); return; }

In my opinion this should be excluded to another function, where user is asked for permission. Or this is connected to something different?

When I removed this part, everything is working correctly.
I'm testing in on phone with Android 11.

How to request permission for writing

Hi, I am using scoped library. I am unable create file. It say you have no permission to create. Can you please how can I call permission. If possible please create a video tutorial on this.

Create file using file data

Hi,

How can i save the file in the external storage folder from URL?

createFile(data.uri,filename,mimetype).

Thank you.

Unable to run in Android.

Hi,

I updated my react-native project to minSdkVersion = 26 compileSdkVersion = 33 and targetSdkVersion = 33. I am getting the below error in Android.
error: lambda expressions are not supported in -source 7
AsyncTask.execute(() -> {
^
(use -source 8 or higher to enable lambda expressions)

/node_modules/react-native-scoped-storage/android/src/main/java/com/ammarahmed/scopedstorage/RNScopedStorageModule.java:817

react-native-cli: 2.0.1
react-native: 0.68.7

can't write file in android 13

openDocumentTree returns uri like this
content://com.android.externalstorage.documents/tree/primary%3ADownload%2FTest/document/primary%3ADownload%2FTest
writeFile fails printing to log
File could not be created/does not exist

Download file from API

Hi,

How to download files to internal storage from API.
I have an API for downloading files from SharePoint rest API.
Any method?

Thank you.

How to read file ..

This is my example code

async function WriteScoped() {
  let dir = await ScopedStorage.openDocumentTree(true);
  console.log('DIR', dir);
  let text = 'Hello world';
  if (dir) {
    var result = await ScopedStorage.writeFile(
      dir.uri,
      'helloworld.txt',
      'text/plain',
      text,
      'utf8',
    );
    console.log('SCOPED WRITED', result);
  }
}

async function ReadScoped() {
  let dir = await ScopedStorage.openDocumentTree(true);
  console.log('DIR read', dir);
  if (dir) {
    let result = await ScopedStorage.readFile(
      dir.uri + '%2Fhelloworld.txt',
      'utf8',
    );
    console.log('SCOPED READED');
  }
}

When use ReadScoped the result is:

Possible Unhandled Promise Rejection
'content://com.android.externalstorage.documents/tree/primary%3ADocuments%2Fhelloworld.txt'is not a file

also If I use

dir.uri + '/helloworld.txt',

How to open a file using default android app

Hi, I'm working on an app in which the user could open a file stored in scoped storage with the default android apps (like RNFetchBlob's ActionViewIntent). However, I can't find a way to do it, as RNFetchBlob requires a path rather than an URI.
Also, I need the user to access the original file and not a copy of it, so he can edit it.
I tried to stat the file path (and I get something like '/storage/emulated/0/.../myfile', but no file is found by RNFetchBlob ActionViewIntent.
As RNFetchBlob is not maintained anymore, I can't find any alternative to manage this situation... Any help will be greatly appreciated

How to enable Manage access to all files

I want a user to be prompted to all allow my app to manage access to all file.

for example

If one clicks on a button. This will be the prompt 👇
IMG_20220812_094430

I know it has to do with Manage External Storage permission but it's has been 2 weeks since I am trying to implement it in react native Cli.
Can someone please help.

Running listFiles on a subdirectory uri creates a new folder

Hi.... I really like your library even through I'm running into a little issue. If I run listFiles on a document tree that I selected, and then run listFiles on one of the subdirectories that were returned, it seems to create a directory in the root document tree, instead of listing the files.

For example, I have a /sdcard/Bills directory. When I run openDocumentTree, and select that directory, it returns:
[{"lastModified": 1630735526000, "name": "Pending", "type": "directory", "uri": "content://com.android.externalstorage.documents/tree/primary%3Abills/document/primary%3ABills%2FPending"}]

If I run listFiles on that uri.... it creates an empty directory: /sdcard/bills/document/primary%3ABills%2FPending/

Is this a problem with my document picker? It's Files by Google v1.0.389363820

Thanks for the help

question : how ts works

Hi @ammarahm-ed

I had this question in mind and I have struggled to find the answer but yet I could not

the question I have is :

how this package work is my js react native project although it contains only ts file and ts config but no ts package or compiler is available in the package.json + there is no compile process defined for it

and as I checked the node modules folder still I see only the ts file and no transformation file in the js

please tell me how this package works in the js project

How to ask permission for specific folder

I installed one app from playstore and it downloads the whatsapp stories (basically fetches the . statuses folder from internal storage)

When I installed that app, It directly opened WhatsApp/. statuses folder to ask permission for that specific folder

As per new android 11 policy now no app can get third party folder access it need the access from file manager.

I want to ask if there is any method I can ask permission for specific folder. Like WhatsApp or any other folder ?

Doesn't work at all

I was trying to follow the documentation, and this is the result I get when I run await ScopedStorage.openDocumentTree(true);

Possible Unhandled Promise Rejection (id: 1):
TypeError: Cannot read property 'openDocumentTree' of null

How to copy image file from one folder to another?

I have written this code:

ScopedStorage.copyFile(item.uri, dir.uri)
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      });

The app crashes when I click on the button to copy the file. Can anyone provide me the correct way to copy file?

openDocumentTree and go back on UI

there is currently this issue that when you want to choose a directory for ( downloading ) its works fine if you proceed and choose one folder but when the user decides to cancel the download ( android go back button ) the at some point it will be back on the app but the download process is still on going ?

how should we cancel the process safely?

        if (Platform.OS === 'android') {
            // Android saves downloaded file buffer to user selected folder
            openDocumentTree(false).then(response => {
                writeFile(response.uri, filename, '*/*', buff, 'base64', false).then(() => {
                    HelperSendLocalNotification(
                        'Download Completed',
                        'File:' + filename,
                        moment().valueOf().toString(),
                        EnumLocalNotificationId.Download_Complete,
                        response.uri,
                    );
                    resolve();
                });
            });
        } 

new improvements

Hello @ammarahm-ed thx for this awesome lib,

I would like to know if it's possible to add an initial uri path in the openDocumentTree function for user selection?

Is it possible to copy a file from normal Android file storage into scoped storage?

Hello, sorry as I should probably be able to figure this out on my own...but it is possible to call copyFile to move a file from a normal file storage directory, into a scoped storage directory?

By "normal" file storage directory I mean files saved to the paths that react-native-fs provides:

MainBundlePath (String) The absolute path to the main bundle directory (not available on Android)
CachesDirectoryPath (String) The absolute path to the caches directory
ExternalCachesDirectoryPath (String) The absolute path to the external caches directory (android only)
DocumentDirectoryPath (String) The absolute path to the document directory
DownloadDirectoryPath (String) The absolute path to the download directory (on android only)
TemporaryDirectoryPath (String) The absolute path to the temporary directory (falls back to Caching-Directory on Android)
LibraryDirectoryPath (String) The absolute path to the NSLibraryDirectory (iOS only)
ExternalDirectoryPath (String) The absolute path to the external files, shared directory (android only)
ExternalStorageDirectoryPath (String) The absolute path to the external storage, shared directory (android only)

Whenever I call copyFile using these paths (although I haven't tried all of them yet) I get a read/write permission error, such as:

Error: 'file://storage/emulated/0/Android/data/com.podverse/files/imYqxyt4gk.mp3'does not have permission to read/write

We're using react-native-background-downloader, and I haven't been able to figure out a way to get files downloaded with it into scoped storage...it seems like react-native-background-downloader library just won't work with content:// as a destination, and react-native-scoped-storage can't read files from the paths I listed above...so it seems like we're stuck? No workarounds? Any info or advice would be appreciated.

How can i share images/videos with ScopedStorage.getPersistedUriPermissions() url

I am building Whatsapp status saver app . there when i am going to share the images/videos to whatsapp it shows " the file format not supported"

I have this type of url => "content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fmedia%2Fcom.whatsapp%2FWhatsApp%2FMedia%2F.Statuses/9df4d2b41db944869af2910a199868ca.mp4"

Files is saved empty to the download folder

Hello. On some phones (or Android systems), files are saved with a weight of 0 bytes. In other directories everything is saved normally.

NetworkRepository.DownloadService.downloadAppealFileAndroid(this.item?.code, item).then(async (response) =>  {
    let base64Str = response.data;
    let fileName = item.name;
    let data = base64Str;
    let encoding = "base64";
    try{
      await ScopedStorage.openDocumentTree(true).then(async (file) => {
        await ScopedStorage.writeFile(file.uri, fileName, '*/*',  data, encoding, false).then(() => {
          this.isAppealFileLoad = false;
        })
      });
    } catch (e) {
      this.isAppealFileLoad = false;
      console.log(`***error*** ${e}`)
    }
  });

> Task :react-native-scoped-storage:compileDebugJavaWithJavac FAILED

At the moment of running on android this message is shown up:

Task :react-native-scoped-storage:compileDebugJavaWithJavac FAILED

\node_modules\react-native-scoped-storage\android\src\main\java\com\ammarahmed\scopedstorage\RNScopedStorageModule.java:641: error: ';' expected public void writeFile(String path, String fileName, String mimeType, String data, String encoding, final boolean append, final Promise promise)

export `exists()` from native module

I was checking the java files and it seems theres a exists() method available there, it would be good if we had access to it as well. it shouldn't take much time (like 2 minutes).

Crash on return from directory picker

Hi,
I use openDocumentTree method in order to get access to a directory, but after clicking on use this folder screen becomes black, and it doesn't return to the app properly.

react: 17.0.1,
react-native: 0.64.1,
react-native-scoped-storage: ^1.0.0

buildToolsVersion = "29.0.3"
minSdkVersion = 21
compileSdkVersion = 29
targetSdkVersion = 29

Here is the log:

07-20 20:10:08.502 27653 27653 E AndroidRuntime: FATAL EXCEPTION: main
07-20 20:10:08.502 27653 27653 E AndroidRuntime: Process: com.test, PID: 27653
07-20 20:10:08.502 27653 27653 E AndroidRuntime: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=27867, result=-1, data=Intent { dat=content://com.android.externalstorage.documents/tree/primary: flg=0xc3 }} to activity {com.test/com.test.MainActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'void com.facebook.react.bridge.ActivityEventListener.onActivityResult(android.app.Activity, int, int, android.content.Intent)' on a null object reference
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at android.app.ActivityThread.deliverResults(ActivityThread.java:5057)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at android.app.ActivityThread.handleSendResult(ActivityThread.java:5098)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2105)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:106)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:223)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at android.app.ActivityThread.main(ActivityThread.java:7700)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at java.lang.reflect.Method.invoke(Native Method)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:997)
07-20 20:10:08.502 27653 27653 E AndroidRuntime: Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'void com.facebook.react.bridge.ActivityEventListener.onActivityResult(android.app.Activity, int, int, android.content.Intent)' on a null object reference
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at com.facebook.react.bridge.ReactContext.onActivityResult(ReactContext.java:305)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at com.facebook.react.ReactInstanceManager.onActivityResult(ReactInstanceManager.java:762)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at com.facebook.react.ReactDelegate.onActivityResult(ReactDelegate.java:90)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at com.facebook.react.ReactActivityDelegate.onActivityResult(ReactActivityDelegate.java:112)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at com.facebook.react.ReactActivity.onActivityResult(ReactActivity.java:68)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at android.app.Activity.dispatchActivityResult(Activity.java:8310)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        at android.app.ActivityThread.deliverResults(ActivityThread.java:5050)
07-20 20:10:08.502 27653 27653 E AndroidRuntime:        ... 11 more
07-20 20:10:08.504   427   540 I libPowerHal: [perfNotifyAppState] pack:com.test, pid:27653, STATE_DEAD
07-20 20:10:08.508  1006 27738 I DropBoxManagerService: add tag=data_app_crash isTagEnabled=true flags=0x2
07-20 20:10:08.509  1006  3326 I InputDispatcher: setInputWindows displayId=0 Window{5e4cc58 u0 NavigationBar0} Window{370b3a0 u0 StatusBar} com.google.android.documentsui/com.android.documentsui.picker.PickActivity#1 com.google.android.documentsui/com.android.documentsui.picker.PickActivity#0 Window{57217cb u0 com.android.systemui.ImageWallpaper} 
07-20 20:10:08.517  1006  3325 W ActivityTaskManager:   Force finishing activity com.test/.MainActivity
07-20 20:10:08.527  1006  3326 I InputDispatcher: setInputWindows displayId=0 Window{5e4cc58 u0 NavigationBar0} Window{370b3a0 u0 StatusBar} com.google.android.documentsui/com.android.documentsui.picker.PickActivity#1 com.google.android.documentsui/com.android.documentsui.picker.PickActivity#0 Window{57217cb u0 com.android.systemui.ImageWallpaper} 
07-20 20:10:08.529   442   442 I SurfaceFlinger: operator()(), mtkRenderCntDebug 13, screenshot (com.google.android.documentsui/com.android.documentsui.picker.PickActivity#0)
07-20 20:10:08.529   442   442 I SurfaceFlinger: operator()(), mtkRenderCntDebug 13, screenshot (Dim Layer for - Task=521#0)
07-20 20:10:08.529   442   442 I SurfaceFlinger: operator()(), mtkRenderCntDebug 13, screenshot (com.google.android.documentsui/com.android.documentsui.picker.PickActivity#1)
07-20 20:10:08.536   427   540 I libPowerHal: [perfLockAcq] idx:1 hdl:242 hint:22 pid:427 duration:10000 => ret_hdl:242
07-20 20:10:08.536   427   540 I libPowerHal: [PE] scn:1 hdl:242 hint:22 comm:[email protected] pid:427

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.