Skip to content

Commit

Permalink
* Rework event system
Browse files Browse the repository at this point in the history
* Additional parameter for androiddownloads to enforce the downloaded file being added to the downloads collection

resolves joltup#247
resolves joltup#248
resolves joltup#245
resolves joltup#244
resolves joltup#236

Merge branch 'develop'
  • Loading branch information
Ron Radtke committed May 31, 2023
2 parents f2d7d8f + 0a44109 commit 1ea2e8f
Show file tree
Hide file tree
Showing 27 changed files with 4,496 additions and 4,196 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ DerivedData
*.ipa
*.xcuserstate
project.xcworkspace
/iOS/pods
Podfile*
contents.xcworkspacedata

# VSCode
#
.vscode

# Android/IJ
#
Expand All @@ -44,3 +51,4 @@ buck-out/
\.buckd/
android/app/libs
android/keystores/debug.keystore
/ios/ReactNativeBlobUtil.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public class ReactNativeBlobUtilConst {
public static final String EVENT_PROGRESS = "ReactNativeBlobUtilProgress";
public static final String EVENT_HTTP_STATE = "ReactNativeBlobUtilState";
public static final String EVENT_MESSAGE = "ReactNativeBlobUtilMessage";
public static final String EVENT_FILESYSTEM = "ReactNativeBlobUtilFilesystem";
public static final String FILE_PREFIX = "ReactNativeBlobUtil-file://";
public static final String CONTENT_PREFIX = "ReactNativeBlobUtil-content://";
public static final String FILE_PREFIX_BUNDLE_ASSET = "bundle-assets://";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.events.EventDispatcher;

import java.io.File;
import java.io.FileInputStream;
Expand All @@ -37,9 +39,6 @@ class ReactNativeBlobUtilFS {

private ReactApplicationContext mCtx;
private DeviceEventManagerModule.RCTDeviceEventEmitter emitter;
private String encoding = "base64";
private OutputStream writeStreamInstance = null;
private static HashMap<String, ReactNativeBlobUtilFS> fileStreams = new HashMap<>();

ReactNativeBlobUtilFS(ReactApplicationContext ctx) {
this.mCtx = ctx;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Base64;
Expand Down Expand Up @@ -49,6 +50,8 @@
import java.util.ArrayList;
import java.util.HashMap;

import java.util.UUID;

import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executors;
Expand Down Expand Up @@ -252,19 +255,29 @@ public void run() {
if (options.addAndroidDownloads.hasKey("path")) {
req.setDestinationUri(Uri.parse("file://" + options.addAndroidDownloads.getString("path")));
}
// #391 Add MIME type to the request
if (options.addAndroidDownloads.hasKey("mime")) {
req.setMimeType(options.addAndroidDownloads.getString("mime"));
}
if (options.addAndroidDownloads.hasKey("mediaScannable") && options.addAndroidDownloads.getBoolean("mediaScannable")) {
req.allowScanningByMediaScanner();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.addAndroidDownloads.hasKey("storeInDownloads") && options.addAndroidDownloads.getBoolean("storeInDownloads")) {
String t = options.addAndroidDownloads.getString("title");
if(t == null || t.isEmpty())
t = UUID.randomUUID().toString();
if(this.options.appendExt != null && !this.options.appendExt.isEmpty())
t += "." + this.options.appendExt;

req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, t);
}

// set headers
ReadableMapKeySetIterator it = headers.keySetIterator();
while (it.hasNextKey()) {
String key = it.nextKey();
req.addRequestHeader(key, headers.getString(key));
}

// Attempt to add cookie, if it exists
URL urlObj;
try {
Expand Down Expand Up @@ -862,7 +875,6 @@ public void onReceive(Context context, Intent intent) {
DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
dm.query(query);
Cursor c = dm.query(query);
// #236 unhandled null check for DownloadManager.query() return value
if (c == null) {
this.invoke_callback("Download manager failed to download from " + this.url + ". Query was unsuccessful ", null, null);
return;
Expand All @@ -872,7 +884,6 @@ public void onReceive(Context context, Intent intent) {
try {
// the file exists in media content database
if (c.moveToFirst()) {
// #297 handle failed request
int statusCode = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (statusCode == DownloadManager.STATUS_FAILED) {
this.invoke_callback("Download manager failed to download from " + this.url + ". Status Code = " + statusCode, null, null);
Expand Down Expand Up @@ -910,7 +921,15 @@ public void onReceive(Context context, Intent intent) {
ex.printStackTrace();
this.invoke_callback(ex.getLocalizedMessage(), null);
}
} else {
}
else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.addAndroidDownloads.hasKey("storeInDownloads") && options.addAndroidDownloads.getBoolean("storeInDownloads")){
Uri downloadeduri = dm.getUriForDownloadedFile(downloadManagerId);
if(downloadeduri == null)
this.callback.invoke("Download manager could not resolve downloaded file uri.", ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, null);
else
this.callback.invoke(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, downloadeduri.toString());
}
else {
if (filePath == null)
this.invoke_callback("Download manager could not resolve downloaded file path.", ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, null);
else
Expand Down Expand Up @@ -946,6 +965,4 @@ public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder

return client;
}


}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.ReactNativeBlobUtil;

import static com.ReactNativeBlobUtil.ReactNativeBlobUtilConst.EVENT_FILESYSTEM;

import android.net.Uri;
import android.os.SystemClock;
import android.util.Base64;
Expand Down Expand Up @@ -249,15 +251,17 @@ private void emitStreamEvent(String streamName, String event, String data) {
WritableMap eventData = Arguments.createMap();
eventData.putString("event", event);
eventData.putString("detail", data);
this.emitter.emit(streamName, eventData);
eventData.putString("streamId", streamName);
this.emitter.emit(EVENT_FILESYSTEM, eventData);
}

// "event" always is "data"...
private void emitStreamEvent(String streamName, String event, WritableArray data) {
WritableMap eventData = Arguments.createMap();
eventData.putString("event", event);
eventData.putArray("detail", data);
this.emitter.emit(streamName, eventData);
eventData.putString("streamId", streamName);
this.emitter.emit(EVENT_FILESYSTEM, eventData);
}

// "event" always is "error"...
Expand All @@ -266,7 +270,8 @@ private void emitStreamEvent(String streamName, String event, String code, Strin
eventData.putString("event", event);
eventData.putString("code", code);
eventData.putString("detail", message);
this.emitter.emit(streamName, eventData);
eventData.putString("streamId", streamName);
this.emitter.emit(EVENT_FILESYSTEM, eventData);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ public ReactNativeBlobUtil(ReactApplicationContext reactContext) {
delegate = new ReactNativeBlobUtilImpl(reactContext);
}

// Required for rn built in EventEmitter Calls.
@ReactMethod
public void addListener(String eventName) {

}

@ReactMethod
public void removeListeners(Integer count) {

}

@Override
protected Map<String, Object> getTypedExportedConstants() {
Map<String, Object> res = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ public ReactNativeBlobUtil(ReactApplicationContext reactContext) {
delegate = new ReactNativeBlobUtilImpl(reactContext);
}

// Required for rn built in EventEmitter Calls.
@ReactMethod
public void addListener(String eventName) {

}

@ReactMethod
public void removeListeners(Integer count) {

}

@NonNull
@Override
public String getName() {
Expand Down
128 changes: 66 additions & 62 deletions class/ReactNativeBlobUtilReadStream.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,84 @@
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.

import {
DeviceEventEmitter,
NativeAppEventEmitter,
} from 'react-native';
import {NativeEventEmitter} from 'react-native';
import UUID from '../utils/uuid';

import ReactNativeBlobUtil from "../codegenSpecs/NativeBlobUtils";
const emitter = DeviceEventEmitter;

const emitter = new NativeEventEmitter(ReactNativeBlobUtil);

export default class ReactNativeBlobUtilReadStream {

path : string;
encoding : 'utf8' | 'ascii' | 'base64';
bufferSize : ?number;
closed : boolean;
tick : number = 10;
path: string;
encoding: 'utf8' | 'ascii' | 'base64';
bufferSize: ?number;
closed: boolean;
tick: number = 10;

constructor(path:string, encoding:string, bufferSize?:?number, tick:number) {
if(!path)
throw Error('ReactNativeBlobUtil could not open file stream with empty `path`');
this.encoding = encoding || 'utf8';
this.bufferSize = bufferSize;
this.path = path;
this.closed = false;
this.tick = tick;
this._onData = () => {};
this._onEnd = () => {};
this._onError = () => {};
this.streamId = 'RNFBRS'+ UUID();
constructor(path: string, encoding: string, bufferSize?: ?number, tick: number) {
if (!path)
throw Error('ReactNativeBlobUtil could not open file stream with empty `path`');
this.encoding = encoding || 'utf8';
this.bufferSize = bufferSize;
this.path = path;
this.closed = false;
this.tick = tick;
this._onData = () => {
};
this._onEnd = () => {
};
this._onError = () => {
};
this.streamId = 'RNFBRS' + UUID();

// register for file stream event
let subscription = emitter.addListener(this.streamId, (e) => {
let {event, code, detail} = e;
if(this._onData && event === 'data') {
this._onData(detail);
return;
}
else if (this._onEnd && event === 'end') {
this._onEnd(detail);
}
else {
const err = new Error(detail);
err.code = code || 'EUNSPECIFIED';
if(this._onError)
this._onError(err);
else
throw err;
}
// when stream closed or error, remove event handler
if (event === 'error' || event === 'end') {
subscription.remove();
this.closed = true;
}
});
// register for file stream event
let subscription = emitter.addListener('ReactNativeBlobUtilFilesystem', (e) => {
if (typeof e === 'string') e = JSON.parse(e);
console.log(e, this.streamId, e.streamId, e.streamId === this.streamId)
if (e.streamId !== this.streamId) return; // wrong stream
let {event, code, detail} = e;
if (this._onData && event === 'data') {
this._onData(detail);
return;
}
else if (this._onEnd && event === 'end') {
this._onEnd(detail);
}
else {
const err = new Error(detail);
err.code = code || 'EUNSPECIFIED';
if (this._onError)
this._onError(err);
else
throw err;
}
// when stream closed or error, remove event handler
if (event === 'error' || event === 'end') {
subscription.remove();
this.closed = true;
}
});

}
}

open() {
if(!this.closed)
ReactNativeBlobUtil.readStream(this.path, this.encoding, this.bufferSize || 10240 , this.tick || -1, this.streamId);
else
throw new Error('Stream closed');
}
open() {
if (!this.closed)
ReactNativeBlobUtil.readStream(this.path, this.encoding, this.bufferSize || 10240, this.tick || -1, this.streamId);
else
throw new Error('Stream closed');
}

onData(fn:() => void) {
this._onData = fn;
}
onData(fn: () => void) {
this._onData = fn;
}

onError(fn) {
this._onError = fn;
}
onError(fn) {
this._onError = fn;
}

onEnd (fn) {
this._onEnd = fn;
}
onEnd(fn) {
this._onEnd = fn;
}

}
17 changes: 6 additions & 11 deletions class/ReactNativeBlobUtilSession.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.

import {
DeviceEventEmitter,
NativeAppEventEmitter,
} from 'react-native';

import ReactNativeBlobUtil from "../codegenSpecs/NativeBlobUtils";
import ReactNativeBlobUtil from '../codegenSpecs/NativeBlobUtils';

let sessions = {};

Expand All @@ -29,8 +24,8 @@ export default class ReactNativeBlobUtilSession {

constructor(name:string, list:Array<string>) {
this.name = name;
if(!sessions[name]) {
if(Array.isArray(list))
if (!sessions[name]) {
if (Array.isArray(list))
sessions[name] = list;
else
sessions[name] = [];
Expand All @@ -44,8 +39,8 @@ export default class ReactNativeBlobUtilSession {

remove(path:string):ReactNativeBlobUtilSession {
let list = sessions[this.name];
for(let i of list) {
if(list[i] === path) {
for (let i of list) {
if (list[i] === path) {
sessions[this.name].splice(i, 1);
break;
}
Expand All @@ -60,7 +55,7 @@ export default class ReactNativeBlobUtilSession {
dispose():Promise {
return new Promise((resolve, reject) => {
ReactNativeBlobUtil.removeSession(sessions[this.name], (err) => {
if(err)
if (err)
reject(new Error(err));
else {
delete sessions[this.name];
Expand Down
Loading

0 comments on commit 1ea2e8f

Please sign in to comment.