Skip to content

Commit 9402e3a

Browse files
author
Ron Radtke
committed
* Rework event system
* 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'
2 parents f2d7d8f + 0a44109 commit 9402e3a

27 files changed

+4497
-4196
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ DerivedData
2727
*.ipa
2828
*.xcuserstate
2929
project.xcworkspace
30+
/iOS/pods
31+
Podfile*
32+
contents.xcworkspacedata
33+
34+
# VSCode
35+
#
36+
.vscode
3037

3138
# Android/IJ
3239
#
@@ -44,3 +51,4 @@ buck-out/
4451
\.buckd/
4552
android/app/libs
4653
android/keystores/debug.keystore
54+
/ios/ReactNativeBlobUtil.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConst.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ public class ReactNativeBlobUtilConst {
66
public static final String EVENT_PROGRESS = "ReactNativeBlobUtilProgress";
77
public static final String EVENT_HTTP_STATE = "ReactNativeBlobUtilState";
88
public static final String EVENT_MESSAGE = "ReactNativeBlobUtilMessage";
9+
public static final String EVENT_FILESYSTEM = "ReactNativeBlobUtilFilesystem";
910
public static final String FILE_PREFIX = "ReactNativeBlobUtil-file://";
1011
public static final String CONTENT_PREFIX = "ReactNativeBlobUtil-content://";
1112
public static final String FILE_PREFIX_BUNDLE_ASSET = "bundle-assets://";

android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.facebook.react.bridge.WritableArray;
2020
import com.facebook.react.bridge.WritableMap;
2121
import com.facebook.react.modules.core.DeviceEventManagerModule;
22+
import com.facebook.react.uimanager.UIManagerHelper;
23+
import com.facebook.react.uimanager.events.EventDispatcher;
2224

2325
import java.io.File;
2426
import java.io.FileInputStream;
@@ -37,9 +39,6 @@ class ReactNativeBlobUtilFS {
3739

3840
private ReactApplicationContext mCtx;
3941
private DeviceEventManagerModule.RCTDeviceEventEmitter emitter;
40-
private String encoding = "base64";
41-
private OutputStream writeStreamInstance = null;
42-
private static HashMap<String, ReactNativeBlobUtilFS> fileStreams = new HashMap<>();
4342

4443
ReactNativeBlobUtilFS(ReactApplicationContext ctx) {
4544
this.mCtx = ctx;

android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import android.net.Uri;
1414
import android.os.Build;
1515
import android.os.Bundle;
16+
import android.os.Environment;
1617
import android.os.Handler;
1718
import android.os.Message;
1819
import android.util.Base64;
@@ -49,6 +50,8 @@
4950
import java.util.ArrayList;
5051
import java.util.HashMap;
5152

53+
import java.util.UUID;
54+
5255
import java.util.List;
5356
import java.util.Locale;
5457
import java.util.concurrent.Executors;
@@ -252,19 +255,29 @@ public void run() {
252255
if (options.addAndroidDownloads.hasKey("path")) {
253256
req.setDestinationUri(Uri.parse("file://" + options.addAndroidDownloads.getString("path")));
254257
}
255-
// #391 Add MIME type to the request
256258
if (options.addAndroidDownloads.hasKey("mime")) {
257259
req.setMimeType(options.addAndroidDownloads.getString("mime"));
258260
}
259261
if (options.addAndroidDownloads.hasKey("mediaScannable") && options.addAndroidDownloads.getBoolean("mediaScannable")) {
260262
req.allowScanningByMediaScanner();
261263
}
264+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.addAndroidDownloads.hasKey("storeInDownloads") && options.addAndroidDownloads.getBoolean("storeInDownloads")) {
265+
String t = options.addAndroidDownloads.getString("title");
266+
if(t == null || t.isEmpty())
267+
t = UUID.randomUUID().toString();
268+
if(this.options.appendExt != null && !this.options.appendExt.isEmpty())
269+
t += "." + this.options.appendExt;
270+
271+
req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, t);
272+
}
273+
262274
// set headers
263275
ReadableMapKeySetIterator it = headers.keySetIterator();
264276
while (it.hasNextKey()) {
265277
String key = it.nextKey();
266278
req.addRequestHeader(key, headers.getString(key));
267279
}
280+
268281
// Attempt to add cookie, if it exists
269282
URL urlObj;
270283
try {
@@ -862,7 +875,6 @@ public void onReceive(Context context, Intent intent) {
862875
DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
863876
dm.query(query);
864877
Cursor c = dm.query(query);
865-
// #236 unhandled null check for DownloadManager.query() return value
866878
if (c == null) {
867879
this.invoke_callback("Download manager failed to download from " + this.url + ". Query was unsuccessful ", null, null);
868880
return;
@@ -872,7 +884,6 @@ public void onReceive(Context context, Intent intent) {
872884
try {
873885
// the file exists in media content database
874886
if (c.moveToFirst()) {
875-
// #297 handle failed request
876887
int statusCode = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
877888
if (statusCode == DownloadManager.STATUS_FAILED) {
878889
this.invoke_callback("Download manager failed to download from " + this.url + ". Status Code = " + statusCode, null, null);
@@ -910,7 +921,15 @@ public void onReceive(Context context, Intent intent) {
910921
ex.printStackTrace();
911922
this.invoke_callback(ex.getLocalizedMessage(), null);
912923
}
913-
} else {
924+
}
925+
else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.addAndroidDownloads.hasKey("storeInDownloads") && options.addAndroidDownloads.getBoolean("storeInDownloads")){
926+
Uri downloadeduri = dm.getUriForDownloadedFile(downloadManagerId);
927+
if(downloadeduri == null)
928+
this.callback.invoke("Download manager could not resolve downloaded file uri.", ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, null);
929+
else
930+
this.callback.invoke(null, ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, downloadeduri.toString());
931+
}
932+
else {
914933
if (filePath == null)
915934
this.invoke_callback("Download manager could not resolve downloaded file path.", ReactNativeBlobUtilConst.RNFB_RESPONSE_PATH, null);
916935
else
@@ -946,6 +965,4 @@ public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder
946965

947966
return client;
948967
}
949-
950-
951968
}

android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilStream.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.ReactNativeBlobUtil;
22

3+
import static com.ReactNativeBlobUtil.ReactNativeBlobUtilConst.EVENT_FILESYSTEM;
4+
35
import android.net.Uri;
46
import android.os.SystemClock;
57
import android.util.Base64;
@@ -249,15 +251,17 @@ private void emitStreamEvent(String streamName, String event, String data) {
249251
WritableMap eventData = Arguments.createMap();
250252
eventData.putString("event", event);
251253
eventData.putString("detail", data);
252-
this.emitter.emit(streamName, eventData);
254+
eventData.putString("streamId", streamName);
255+
this.emitter.emit(EVENT_FILESYSTEM, eventData);
253256
}
254257

255258
// "event" always is "data"...
256259
private void emitStreamEvent(String streamName, String event, WritableArray data) {
257260
WritableMap eventData = Arguments.createMap();
258261
eventData.putString("event", event);
259262
eventData.putArray("detail", data);
260-
this.emitter.emit(streamName, eventData);
263+
eventData.putString("streamId", streamName);
264+
this.emitter.emit(EVENT_FILESYSTEM, eventData);
261265
}
262266

263267
// "event" always is "error"...
@@ -266,7 +270,8 @@ private void emitStreamEvent(String streamName, String event, String code, Strin
266270
eventData.putString("event", event);
267271
eventData.putString("code", code);
268272
eventData.putString("detail", message);
269-
this.emitter.emit(streamName, eventData);
273+
eventData.putString("streamId", streamName);
274+
this.emitter.emit(EVENT_FILESYSTEM, eventData);
270275
}
271276

272277
/**

android/src/newarch/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ public ReactNativeBlobUtil(ReactApplicationContext reactContext) {
2626
delegate = new ReactNativeBlobUtilImpl(reactContext);
2727
}
2828

29+
// Required for rn built in EventEmitter Calls.
30+
@ReactMethod
31+
public void addListener(String eventName) {
32+
33+
}
34+
35+
@ReactMethod
36+
public void removeListeners(Integer count) {
37+
38+
}
39+
2940
@Override
3041
protected Map<String, Object> getTypedExportedConstants() {
3142
Map<String, Object> res = new HashMap<>();

android/src/oldarch/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ public ReactNativeBlobUtil(ReactApplicationContext reactContext) {
2727
delegate = new ReactNativeBlobUtilImpl(reactContext);
2828
}
2929

30+
// Required for rn built in EventEmitter Calls.
31+
@ReactMethod
32+
public void addListener(String eventName) {
33+
34+
}
35+
36+
@ReactMethod
37+
public void removeListeners(Integer count) {
38+
39+
}
40+
3041
@NonNull
3142
@Override
3243
public String getName() {

class/ReactNativeBlobUtilReadStream.js

Lines changed: 66 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,80 +2,84 @@
22
// Use of this source code is governed by a MIT-style license that can be
33
// found in the LICENSE file.
44

5-
import {
6-
DeviceEventEmitter,
7-
NativeAppEventEmitter,
8-
} from 'react-native';
5+
import {NativeEventEmitter} from 'react-native';
96
import UUID from '../utils/uuid';
107

118
import ReactNativeBlobUtil from "../codegenSpecs/NativeBlobUtils";
12-
const emitter = DeviceEventEmitter;
9+
10+
const emitter = new NativeEventEmitter(ReactNativeBlobUtil);
1311

1412
export default class ReactNativeBlobUtilReadStream {
1513

16-
path : string;
17-
encoding : 'utf8' | 'ascii' | 'base64';
18-
bufferSize : ?number;
19-
closed : boolean;
20-
tick : number = 10;
14+
path: string;
15+
encoding: 'utf8' | 'ascii' | 'base64';
16+
bufferSize: ?number;
17+
closed: boolean;
18+
tick: number = 10;
2119

22-
constructor(path:string, encoding:string, bufferSize?:?number, tick:number) {
23-
if(!path)
24-
throw Error('ReactNativeBlobUtil could not open file stream with empty `path`');
25-
this.encoding = encoding || 'utf8';
26-
this.bufferSize = bufferSize;
27-
this.path = path;
28-
this.closed = false;
29-
this.tick = tick;
30-
this._onData = () => {};
31-
this._onEnd = () => {};
32-
this._onError = () => {};
33-
this.streamId = 'RNFBRS'+ UUID();
20+
constructor(path: string, encoding: string, bufferSize?: ?number, tick: number) {
21+
if (!path)
22+
throw Error('ReactNativeBlobUtil could not open file stream with empty `path`');
23+
this.encoding = encoding || 'utf8';
24+
this.bufferSize = bufferSize;
25+
this.path = path;
26+
this.closed = false;
27+
this.tick = tick;
28+
this._onData = () => {
29+
};
30+
this._onEnd = () => {
31+
};
32+
this._onError = () => {
33+
};
34+
this.streamId = 'RNFBRS' + UUID();
3435

35-
// register for file stream event
36-
let subscription = emitter.addListener(this.streamId, (e) => {
37-
let {event, code, detail} = e;
38-
if(this._onData && event === 'data') {
39-
this._onData(detail);
40-
return;
41-
}
42-
else if (this._onEnd && event === 'end') {
43-
this._onEnd(detail);
44-
}
45-
else {
46-
const err = new Error(detail);
47-
err.code = code || 'EUNSPECIFIED';
48-
if(this._onError)
49-
this._onError(err);
50-
else
51-
throw err;
52-
}
53-
// when stream closed or error, remove event handler
54-
if (event === 'error' || event === 'end') {
55-
subscription.remove();
56-
this.closed = true;
57-
}
58-
});
36+
// register for file stream event
37+
let subscription = emitter.addListener('ReactNativeBlobUtilFilesystem', (e) => {
38+
if (typeof e === 'string') e = JSON.parse(e);
39+
console.log(e, this.streamId, e.streamId, e.streamId === this.streamId)
40+
if (e.streamId !== this.streamId) return; // wrong stream
41+
let {event, code, detail} = e;
42+
if (this._onData && event === 'data') {
43+
this._onData(detail);
44+
return;
45+
}
46+
else if (this._onEnd && event === 'end') {
47+
this._onEnd(detail);
48+
}
49+
else {
50+
const err = new Error(detail);
51+
err.code = code || 'EUNSPECIFIED';
52+
if (this._onError)
53+
this._onError(err);
54+
else
55+
throw err;
56+
}
57+
// when stream closed or error, remove event handler
58+
if (event === 'error' || event === 'end') {
59+
subscription.remove();
60+
this.closed = true;
61+
}
62+
});
5963

60-
}
64+
}
6165

62-
open() {
63-
if(!this.closed)
64-
ReactNativeBlobUtil.readStream(this.path, this.encoding, this.bufferSize || 10240 , this.tick || -1, this.streamId);
65-
else
66-
throw new Error('Stream closed');
67-
}
66+
open() {
67+
if (!this.closed)
68+
ReactNativeBlobUtil.readStream(this.path, this.encoding, this.bufferSize || 10240, this.tick || -1, this.streamId);
69+
else
70+
throw new Error('Stream closed');
71+
}
6872

69-
onData(fn:() => void) {
70-
this._onData = fn;
71-
}
73+
onData(fn: () => void) {
74+
this._onData = fn;
75+
}
7276

73-
onError(fn) {
74-
this._onError = fn;
75-
}
77+
onError(fn) {
78+
this._onError = fn;
79+
}
7680

77-
onEnd (fn) {
78-
this._onEnd = fn;
79-
}
81+
onEnd(fn) {
82+
this._onEnd = fn;
83+
}
8084

8185
}

class/ReactNativeBlobUtilSession.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@
22
// Use of this source code is governed by a MIT-style license that can be
33
// found in the LICENSE file.
44

5-
import {
6-
DeviceEventEmitter,
7-
NativeAppEventEmitter,
8-
} from 'react-native';
9-
10-
import ReactNativeBlobUtil from "../codegenSpecs/NativeBlobUtils";
5+
import ReactNativeBlobUtil from '../codegenSpecs/NativeBlobUtils';
116

127
let sessions = {};
138

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

3025
constructor(name:string, list:Array<string>) {
3126
this.name = name;
32-
if(!sessions[name]) {
33-
if(Array.isArray(list))
27+
if (!sessions[name]) {
28+
if (Array.isArray(list))
3429
sessions[name] = list;
3530
else
3631
sessions[name] = [];
@@ -44,8 +39,8 @@ export default class ReactNativeBlobUtilSession {
4439

4540
remove(path:string):ReactNativeBlobUtilSession {
4641
let list = sessions[this.name];
47-
for(let i of list) {
48-
if(list[i] === path) {
42+
for (let i of list) {
43+
if (list[i] === path) {
4944
sessions[this.name].splice(i, 1);
5045
break;
5146
}
@@ -60,7 +55,7 @@ export default class ReactNativeBlobUtilSession {
6055
dispose():Promise {
6156
return new Promise((resolve, reject) => {
6257
ReactNativeBlobUtil.removeSession(sessions[this.name], (err) => {
63-
if(err)
58+
if (err)
6459
reject(new Error(err));
6560
else {
6661
delete sessions[this.name];

0 commit comments

Comments
 (0)