UPDATE : USE SCOPED STORAGE
Alternatively, I found this library react-native-file-access which is use Scoped Storage. in my case, I stored the file in Download directory.
First, with RNSF we need to download the file and set the destination at RNFS.TemporaryDirectoryPath
, then copy the downloaded file with react-native-file-access to specific folder (Download in my case).
FileSystem.cpExternal(localFileUrl, `${filenameFormatted}`,'downloads')
maybe you can use this library to store file in your specific directory with some adjustment
USE PERMISSION MANAGE EXTERNAL STORAGE (NOT RECOMMENDED) :
In Android 11 and above, the permission for accessing storage has changed. you have to add additional permission called MANAGE_EXTERNAL_STORAGE, see these post :
https://developer.android.com/about/versions/11/privacy/storage#directory-access
https://developer.android.com/training/data-storage/manage-all-files
add this in your AndroidManifest.xml
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
i create simple permisson request based on this post call this native function from react native.
create PermissionFileModule.java in app/src/main/<your_package>
public class PermissionFileModule extends ReactContextBaseJavaModule implements ActivityEventListener {
public PermissionFileModule(@Nullable ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addActivityEventListener(this);
}
@NonNull
@Override
public String getName() {
return "PermissionFile";
}
@ReactMethod
public void checkAndGrantPermission(Callback errorCallback, Callback successCallback) {
try {
if (!checkPermission()) {
requestPermission();
successCallback.invoke(false);
} else {
successCallback.invoke(true);
}
} catch (IllegalViewOperationException e) {
errorCallback.invoke(e.getMessage());
}
}
private boolean checkPermission() {
if (SDK_INT >= Build.VERSION_CODES.R) {
return Environment.isExternalStorageManager();
} else {
int result = ContextCompat.checkSelfPermission(getReactApplicationContext(), READ_EXTERNAL_STORAGE);
int result1 = ContextCompat.checkSelfPermission(getReactApplicationContext(), WRITE_EXTERNAL_STORAGE);
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
}
}
private void requestPermission() {
if (SDK_INT >= Build.VERSION_CODES.R) {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse(String.format("package:%s",getReactApplicationContext().getPackageName())));
getCurrentActivity().startActivityForResult(intent, 2296);
} catch (Exception e) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
getCurrentActivity().startActivityForResult(intent, 2296);
}
} else {
//below android 11
ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{WRITE_EXTERNAL_STORAGE}, 100);
}
}
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
if (requestCode == 2296) {
if (SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
Toast.makeText(getReactApplicationContext(), "Access granted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getReactApplicationContext(), "Access not granted", Toast.LENGTH_SHORT).show();
}
}
}
}
@Override
public void onNewIntent(Intent intent) {
// do nothing
}
}
create PermissionFilePackage.java in app/src/main/<your_package>
public class PermissionFilePackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new PermissionFileModule(reactContext));
return modules;
}
@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
in MainApplication.java, add PermissionFilePackage.java as additional package
...
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new PermissionFilePackage());
return packages;
}
...
in your RN Component, call the permission file like this
...
import {NativeModules} from 'react-native';
var PermissionFile = NativeModules.PermissionFile;
...
if (Platform.Version >= 30) {
PermissionFile.checkAndGrantPermission(
(err) => {
DeviceUtils.showAlert(
'Sorry',
'Access not granted',
);
},
(res) => {
if (res) {
checkDirectoryAndDownload(url, name, ext);
}
},
);
} else {
DeviceUtils.grantPermissionSingleAndroid(
PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,
(isAllow) => {
if (isAllow) {
checkDirectoryAndDownload(url, name, ext);
} else {
DeviceUtils.showAlert(
'Sorry',
'Access not granted',
);
}
},
);
}