Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Read and save files using the Storage Access Framework and Mediastore

This plugin allows you to read and save files using the Storage Access Framework and Mediastore on Android only.
This plugin allows you to read and save files using the Storage Access Framework and Mediastore on Android only. Added copy from file method and some fixes.

## Available methods

Expand Down Expand Up @@ -41,6 +41,16 @@ writeFile(params:{
```
Writes a file to a specific filename, with the folder and subfolder being optional. The subfolder will be created if it does not exist, and the default folder is the Downloads folder (saved via Mediastore). Returns the content URI. ```data``` is a Base 64 string.

```typescript
copyFile(params:{
srcfile:string,
filename:string,
folder?:string,
subFolder?:string
}):Promise<string>
```
Copies srcfile's contents to a specific filename, with the folder and subfolder being optional. The subfolder will be created if it does not exist, and the default folder is the Downloads folder (saved via Mediastore). Returns the content URI. ```srcfile``` is cordova source file name. It may be more memory friendly than handling hundreds of MB of base64.

```typescript
overwriteFile(params:{
uri:string,
Expand Down Expand Up @@ -81,4 +91,4 @@ To call methods:
```typescript
cordova.plugins.safMediastore.<function>(params); //returns a Promise
await cordova.plugins.safMediastore.<function>(params); //in an async function
```
```
1 change: 1 addition & 0 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@

<source-file src="src/android/com/customautosys/saf_mediastore/SafMediastore.java" target-dir="src/com/customautosys/saf_mediastore/"/>
</platform>
<framework src="androidx.documentfile:documentfile:1.0.1" />
</plugin>
157 changes: 150 additions & 7 deletions src/android/com/customautosys/saf_mediastore/SafMediastore.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.FileNotFoundException;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
Expand Down Expand Up @@ -153,6 +154,147 @@ public boolean readFile(JSONArray args,CallbackContext callbackContext){
}
}

public boolean copyFile(JSONArray args,CallbackContext callbackContext){
try{
JSONObject params=args.getJSONObject(0);
String filename=params.getString("filename");
String mimeType=MimeTypeMap.getSingleton().getMimeTypeFromExtension(filename.substring(filename.lastIndexOf('.')+1));
boolean forceoverwrite = false;
try {
forceoverwrite = params.getBoolean("overwrite");
} catch(Exception ex) { ; }
//debugLog(forceoverwrite ? "saf OVERWRITE T " : "saf OVERWRITE F ");
if(mimeType==null)mimeType="*/*";
String folder=null;
try{
if(!params.isNull("folder"))folder=params.getString("folder");
}catch(Exception e){
debugLog(e);
}
String subFolder="";
try{
if(!params.isNull("subFolder"))subFolder=params.getString("subFolder");
}catch(Exception e){
debugLog(e);
}
Uri uri=null;
if(folder!=null&&!folder.trim().equals("")){
DocumentFile documentFile=DocumentFile.fromTreeUri(
cordovaInterface.getContext(),
Uri.parse(folder)
);
if(subFolder!=""){
String subFolders[]=subFolder.split("/");
for(int i=0;i<subFolders.length;++i){
DocumentFile subFolderDocumentFile=null;
for(DocumentFile subFile:documentFile.listFiles()){
if(subFile.isDirectory()&&subFile.getName().equals(subFolder)){
subFolderDocumentFile=subFile;
break;
}
}
documentFile=subFolderDocumentFile!=null?subFolderDocumentFile:documentFile.createDirectory(subFolders[i]);
}
}
DocumentFile file=null;
for(DocumentFile subFile:documentFile.listFiles()){
if(!subFile.isDirectory()&&subFile.getName().equals(filename)){
file=subFile;
break;
}
}
uri=(file!=null?file:documentFile.createFile(
mimeType,
filename
)).getUri();
}else{
ContentResolver contentResolver=cordovaInterface.getContext().getContentResolver();
ContentValues contentValues=new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME,filename);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE,mimeType);
if(!subFolder.startsWith("/"))subFolder="/"+subFolder;
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH,Environment.DIRECTORY_DOWNLOADS+subFolder);
uri=contentResolver.insert(MediaStore.Files.getContentUri("external"),contentValues);
}
/*************/
InputStream ism = null;
try {
boolean nfound = false;
ism = cordovaInterface.getContext().getContentResolver().openInputStream(uri);
if (ism == null) nfound = true;
else { /* got input stream */
byte[] b = new byte[2];
int r = ism.read(b, 0, 2);
if (r < 2 || forceoverwrite) nfound = true;
else { /* at least two bytes in file */
try {
ism.close();
}catch(Exception e){debugLog("saf copy exc close read " + e.toString());}
callbackContext.error("File exists");
//debugLog("E X X X 1");
return false;
}
//debugLog("E X X X 2");
try {
ism.close();
}catch(Exception e){debugLog("saf copy exc close empty " + e.toString());}
}
if (!nfound && !forceoverwrite) {
debugLog("saf copy closed");
callbackContext.error("File exists");
return false;
} else {
if (forceoverwrite) {
debugLog("saf copy overwrite forced");
}
int off = 0;
int len = 0;
int rr = -1;
byte[] buff = new byte[1000000];
try(
InputStream inputStream=cordovaInterface.getContext().getContentResolver().openInputStream(Uri.parse(params.getString("srcfile")));
OutputStream outputStream=cordovaInterface.getContext().getContentResolver().openOutputStream(uri,"wt")){
while ((rr = inputStream.read(buff, off, 1000000)) > 0) {
outputStream.write(buff, off, rr);
}
//debugLog("E X X X 5");
callbackContext.success(uri.toString());
return true;
}catch(Exception e) {
debugLog("saf copy exc copy empty " + e.toString());
callbackContext.error(e.toString());
return false;
}
}
}
catch (FileNotFoundException e) {
debugLog("saf copy filenotfound " + e.toString());
int off = 0;
int len = 0;
int rr = -1;
byte[] buff = new byte[1000000];
try(
InputStream inputStream=cordovaInterface.getContext().getContentResolver().openInputStream(Uri.parse(params.getString("srcfile")));
OutputStream outputStream=cordovaInterface.getContext().getContentResolver().openOutputStream(uri,"wt")){
while ((rr = inputStream.read(buff, off, 1000000)) > 0) {
outputStream.write(buff, off, rr);
}
callbackContext.success(uri.toString());
return true;
}catch(Exception ex2) {
debugLog("saf copy exc copy fnf " + ex2.toString());
callbackContext.error(ex2.toString());
return false;
}
}
}catch(Exception e){
debugLog("saf copy exception");
debugLog(e);
callbackContext.error(e.toString());
return false;
}
}

public boolean writeFile(JSONArray args,CallbackContext callbackContext){
try{
JSONObject params=args.getJSONObject(0);
Expand All @@ -177,7 +319,7 @@ public boolean writeFile(JSONArray args,CallbackContext callbackContext){
cordovaInterface.getContext(),
Uri.parse(folder)
);
if(subFolder!=null){
if(subFolder!=""){
String subFolders[]=subFolder.split("/");
for(int i=0;i<subFolders.length;++i){
DocumentFile subFolderDocumentFile=null;
Expand Down Expand Up @@ -216,7 +358,8 @@ public boolean writeFile(JSONArray args,CallbackContext callbackContext){
callbackContext.success(uri.toString());
return true;
}catch(Exception e){
callbackContext.error(debugLog(e));
debugLog(e);
callbackContext.error(e.toString());
return false;
}
}
Expand Down Expand Up @@ -405,7 +548,7 @@ public String debugLog(Throwable throwable){
throwable.printStackTrace(printWriter);
String stackTrace=stringWriter.toString();
Log.d(throwable.getLocalizedMessage(),stackTrace,throwable);
cordovaWebView.getEngine().evaluateJavascript(
/*cordovaWebView.getEngine().evaluateJavascript(
"console.log('" +stackTrace.replace(
"'",
"\\'"
Expand All @@ -417,7 +560,7 @@ public String debugLog(Throwable throwable){
"\\t"
)+"');",
this
);
);*/
printWriter.close();
stringWriter.close();
return stackTrace;
Expand All @@ -429,7 +572,7 @@ public String debugLog(Throwable throwable){

public String debugLog(String message){
Log.d(getClass().getName(),message);
cordovaWebView.getEngine().evaluateJavascript(
/*cordovaWebView.getEngine().evaluateJavascript(
"console.log('" +message.replace(
"'",
"\\'"
Expand All @@ -441,10 +584,10 @@ public String debugLog(String message){
"\\t"
)+"');",
this
);
);*/
return message;
}

@Override
public void onReceiveValue(String value){}
}
}
8 changes: 7 additions & 1 deletion www/safMediastore.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ interface SafMediastore{
openFolder(uri:string):Promise<void>,
openFile(uri:string):Promise<void>,
readFile(uri:string):Promise<ArrayBuffer>,
copyFile(params:{
data:string,
filename:string,
folder?:string,
subFolder?:string
}):Promise<string>,
writeFile(params:{
data:string,
filename:string,
Expand All @@ -30,4 +36,4 @@ interface SafMediastore{

interface CordovaPlugins{
safMediastore:SafMediastore
}
}
3 changes: 2 additions & 1 deletion www/safMediastore.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module.exports=(function(){
'openFile',
'readFile',
'writeFile',
'copyFile',
'overwriteFile',
'saveFile',
'deleteFile',
Expand All @@ -33,4 +34,4 @@ module.exports=(function(){
].forEach(action=>exports[action]=callPromise(action));

return exports;
})();
})();