Skip to content

Commit 372c72e

Browse files
author
Ron Radtke
committed
adding new flag for downloadmanager to download to mediastore
1 parent f98cfcb commit 372c72e

File tree

4 files changed

+99
-74
lines changed

4 files changed

+99
-74
lines changed

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.callback.invoke("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.callback.invoke("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.callback.invoke(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.callback.invoke("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
}

class/ReactNativeBlobUtilReadStream.js

Lines changed: 65 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,75 +6,80 @@ import {NativeEventEmitter} from 'react-native';
66
import UUID from '../utils/uuid';
77

88
import ReactNativeBlobUtil from "../codegenSpecs/NativeBlobUtils";
9-
const emitter = new NativeEventEmitter(ReactNativeBlobUtil);
9+
10+
const emitter = new NativeEventEmitter(ReactNativeBlobUtil);
1011

1112
export default class ReactNativeBlobUtilReadStream {
1213

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

19-
constructor(path:string, encoding:string, bufferSize?:?number, tick:number) {
20-
if(!path)
21-
throw Error('ReactNativeBlobUtil could not open file stream with empty `path`');
22-
this.encoding = encoding || 'utf8';
23-
this.bufferSize = bufferSize;
24-
this.path = path;
25-
this.closed = false;
26-
this.tick = tick;
27-
this._onData = () => {};
28-
this._onEnd = () => {};
29-
this._onError = () => {};
30-
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();
3135

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

59-
}
64+
}
6065

61-
open() {
62-
if(!this.closed)
63-
ReactNativeBlobUtil.readStream(this.path, this.encoding, this.bufferSize || 10240 , this.tick || -1, this.streamId);
64-
else
65-
throw new Error('Stream closed');
66-
}
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+
}
6772

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

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

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

8085
}

fetch.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {ReactNativeBlobUtilConfig} from 'types';
22
import URIUtil from './utils/uri';
33
import fs from './fs';
44
import getUUID from './utils/uuid';
5-
import {DeviceEventEmitter, NativeEventEmitter} from 'react-native';
5+
import {NativeEventEmitter} from 'react-native';
66
import {FetchBlobResponse} from './class/ReactNativeBlobUtilBlobResponse';
77
import CanceledFetchError from './class/ReactNativeBlobUtilCanceledFetchError';
88
import ReactNativeBlobUtil from './codegenSpecs/NativeBlobUtils';
@@ -11,7 +11,7 @@ const eventEmitter = new NativeEventEmitter(ReactNativeBlobUtil);
1111

1212
// register message channel event handler.
1313
eventEmitter.addListener('ReactNativeBlobUtilMessage', (e) => {
14-
e = JSON.parse(e);
14+
if (typeof e === 'string') e = JSON.parse(e);
1515

1616
console.log('add listener')
1717
if (e.event === 'warn') {
@@ -191,36 +191,35 @@ export function fetch(...args: any): Promise {
191191

192192
// on progress event listener
193193
subscription = eventEmitter.addListener('ReactNativeBlobUtilProgress', (e) => {
194-
e = JSON.parse(e);
194+
if (typeof e === 'string') e = JSON.parse(e);
195195
if (e.taskId === taskId && promise.onProgress) {
196196
promise.onProgress(e.written, e.total, e.chunk);
197197
}
198198
});
199199

200200
subscriptionUpload = eventEmitter.addListener('ReactNativeBlobUtilProgress-upload', (e) => {
201-
e = JSON.parse(e);
201+
if (typeof e === 'string') e = JSON.parse(e);
202202
if (e.taskId === taskId && promise.onUploadProgress) {
203203
promise.onUploadProgress(e.written, e.total);
204204
}
205205
});
206206

207207
stateEvent = eventEmitter.addListener('ReactNativeBlobUtilState', (e) => {
208-
e = JSON.parse(e);
209-
console.log('state', e, typeof e, taskId, e.taskId)
208+
if (typeof e === 'string') e = JSON.parse(e);
210209
if (e.taskId === taskId)
211210
respInfo = e;
212211
promise.onStateChange && promise.onStateChange(e);
213212
});
214213

215214
subscription = eventEmitter.addListener('ReactNativeBlobUtilExpire', (e) => {
216-
e = JSON.parse(e);
215+
if (typeof e === 'string') e = JSON.parse(e);
217216
if (e.taskId === taskId && promise.onExpire) {
218217
promise.onExpire(e);
219218
}
220219
});
221220

222221
partEvent = eventEmitter.addListener('ReactNativeBlobUtilServerPush', (e) => {
223-
e = JSON.parse(e);
222+
if (typeof e === 'string') e = JSON.parse(e);
224223
if (e.taskId === taskId && promise.onPartData) {
225224
promise.onPartData(e.chunk);
226225
}

index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,10 @@ export interface AddAndroidDownloads {
782782
* (https://developer.android.com/reference/android/app/DownloadManager.html#addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean))
783783
*/
784784
mediaScannable?: boolean;
785+
/**
786+
* Only for Android >= Q; Enforces the file being stored to the MediaCollection Downloads. This might overwrite any value given in "path"
787+
*/
788+
storeInDownloads?: boolean;
785789
/**
786790
* A boolean value decide whether show a notification when download complete.
787791
*/

0 commit comments

Comments
 (0)