From d7749379bf1e4ec725a3fe0bb0ad6b42f3c428d4 Mon Sep 17 00:00:00 2001 From: Vinicius Cubas Brand Date: Fri, 24 Mar 2023 10:16:34 -0300 Subject: [PATCH] receiveFile refactor * receiveFile now uses Executor, instead of the deprecated AsyncTask; * stopReceivingFile() added: closes the listening port if no file has been received * a new optional parameter has been added to the receiveFile method. If receiveFile receives an object {meta: true}, then the promise result is not a string, but an object with the following format: { fromAddress: "", file: "file absolute path (string)" } Signed-off-by: Vinicius Cubas Brand --- README.MD | 32 ++++++- .../src/main/java/io/wifi/p2p/FileServer.java | 96 +++++++++++++++++++ .../java/io/wifi/p2p/FileServerAsyncTask.java | 83 ---------------- .../main/java/io/wifi/p2p/MessageServer.java | 6 +- .../io/wifi/p2p/WiFiP2PManagerModule.java | 68 +++++++------ index.js | 9 +- 6 files changed, 176 insertions(+), 118 deletions(-) create mode 100644 android/src/main/java/io/wifi/p2p/FileServer.java delete mode 100644 android/src/main/java/io/wifi/p2p/FileServerAsyncTask.java diff --git a/README.MD b/README.MD index 2a80a9f..d43e5fa 100644 --- a/README.MD +++ b/README.MD @@ -360,7 +360,7 @@ _Note_: you cannot send character encoding for string and by default this librar Same function as above but you can specify the address (a peer address in the same group as this device is). -### receiveFile(folder, fileName, forceToScanGallery) +### receiveFile(folder, fileName, forceToScanGallery, extraParams) If you expect, that someone may send you a file - you can call this method in order to receive it. @@ -368,6 +368,17 @@ If you want to save file with the same name as it's on client device, then befor `forceToScanGallery` is an optional parameter, which indicate whether should we scan and detect new files or not in order to show them in Gallery app. By default it's `false`. +`extraParams` is an optional parameter (object) with extra arguments. Current arguments are: + +* **meta**: Metadata. If true, the message received is not a string, but an object: + +```json +{ + "file": "", + "fromAddress": "IP address of the sender" +} +``` + _Note:_ if you expect file to be received you should request permissions for writing to the storage: ```javascript @@ -411,6 +422,25 @@ receiveMessage() .then(message => console.log(`Received message: ${message}`)) ``` +`receiveMessage` accepts an optional parameter object with extra arguments. Current arguments are: + +* **meta**: Metadata. If true, the message received is not a string, but an object: + +```json +{ + "message": "", + "fromAddress": "IP address of the sender" +} +``` + +**Usage example:** + +```javascript +receiveMessage({meta: true}).then(({fromAddress, message}) => { + console.log(`Received message (from IP ${fromAddress}): ${message}`) +}) +``` + ### stopReceivingMessage() If you didn't receive the message from receiveMessage() as expected, stopReceivingMessage() closes the listening port. If the port is already closed, this method does nothing. diff --git a/android/src/main/java/io/wifi/p2p/FileServer.java b/android/src/main/java/io/wifi/p2p/FileServer.java new file mode 100644 index 0000000..dbed4ad --- /dev/null +++ b/android/src/main/java/io/wifi/p2p/FileServer.java @@ -0,0 +1,96 @@ +package io.wifi.p2p; + +import static io.wifi.p2p.Utils.copyBytes; + +import android.os.Bundle; +import android.util.Log; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableMap; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +/** + * Created by kiryl on 18.7.18. Refactor by viniciuscb on 20.03.23 + * + *

A simple server socket that accepts connection and writes some data on the stream. + */ +public class FileServer { + private static final String TAG = "RNWiFiP2P"; + private final Executor executor; + private volatile ServerSocket serverSocket; + + public FileServer() { + this.executor = Executors.newSingleThreadExecutor(); + } + + public void start( + String destination, + ReadableMap extraProps, + CustomDefinedCallback customDefinedCallback, + Callback callback) { + executor.execute( + () -> { + try { + Boolean returnMeta = false; + serverSocket = new ServerSocket(8988); + Log.i(TAG, "Server: Socket opened to receive file"); + + if (extraProps != null) { + Bundle bundle = Arguments.toBundle(extraProps); + returnMeta = bundle.getBoolean("meta"); + } + + Socket client = serverSocket.accept(); + String clientAddress = client.getInetAddress().getHostAddress(); + Log.i(TAG, "Server: connection done (receiveFile)"); + + final File f = new File(destination); + File dirs = new File(f.getParent()); + if (!dirs.exists()) dirs.mkdirs(); + f.createNewFile(); + Log.i(TAG, "Server: copying files " + f.toString()); + InputStream inputstream = client.getInputStream(); + copyBytes(inputstream, new FileOutputStream(f)); + + client.close(); + + String result = f.getAbsolutePath(); + + Log.i(TAG, "File copied - " + result); + + if (returnMeta) { + WritableMap map = Arguments.createMap(); + map.putString("file", result); + map.putString("fromAddress", clientAddress); + callback.invoke(map); + } else { + callback.invoke(result); + } + customDefinedCallback.invoke(null); + + this.stop(); + } catch (IOException e) { + Log.e(TAG, e.getMessage()); + } + }); + } + + public void stop() { + if (serverSocket != null) { + try { + serverSocket.close(); + Log.i(TAG, "Server: Socket closed to receive file"); + } catch (IOException e) { + Log.e(TAG, e.getMessage()); + } + } + } +} diff --git a/android/src/main/java/io/wifi/p2p/FileServerAsyncTask.java b/android/src/main/java/io/wifi/p2p/FileServerAsyncTask.java deleted file mode 100644 index 8d4d55c..0000000 --- a/android/src/main/java/io/wifi/p2p/FileServerAsyncTask.java +++ /dev/null @@ -1,83 +0,0 @@ -package io.wifi.p2p; - -import static io.wifi.p2p.Utils.copyBytes; - -import android.content.Context; -import android.os.AsyncTask; -import android.util.Log; -import com.facebook.react.bridge.Callback; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.ServerSocket; -import java.net.Socket; - -/** - * Created by Kiryl on 18.7.18. - * - *

A simple server socket that accepts connection and writes some data on the stream. - */ -public class FileServerAsyncTask extends AsyncTask { - private static final String TAG = "RNWiFiP2P"; - private Callback callback; - private CustomDefinedCallback customDefinedCallback; - private String destination; - - /** - * @param context - * @param callback - * @param destination - */ - public FileServerAsyncTask( - Context context, - Callback callback, - String destination, - CustomDefinedCallback customDefinedCallback) { - this.callback = callback; - this.destination = destination; - this.customDefinedCallback = customDefinedCallback; - } - - @Override - protected String doInBackground(Void... params) { - try { - ServerSocket serverSocket = new ServerSocket(8988); - Log.i(TAG, "Server: Socket opened"); - Socket client = serverSocket.accept(); - Log.i(TAG, "Server: connection done"); - final File f = new File(destination); - File dirs = new File(f.getParent()); - if (!dirs.exists()) dirs.mkdirs(); - f.createNewFile(); - Log.i(TAG, "Server: copying files " + f.toString()); - InputStream inputstream = client.getInputStream(); - copyBytes(inputstream, new FileOutputStream(f)); - serverSocket.close(); - return f.getAbsolutePath(); - } catch (IOException e) { - Log.e(TAG, e.getMessage()); - return null; - } - } - /* - * (non-Javadoc) - * @see android.os.AsyncTask#onPostExecute(java.lang.Object) - */ - @Override - protected void onPostExecute(String result) { - if (result != null) { - Log.i(TAG, "File copied - " + result); - callback.invoke(result); - customDefinedCallback.invoke(null); - } - } - /* - * (non-Javadoc) - * @see android.os.AsyncTask#onPreExecute() - */ - @Override - protected void onPreExecute() { - Log.i(TAG, "Opening a server socket"); - } -} diff --git a/android/src/main/java/io/wifi/p2p/MessageServer.java b/android/src/main/java/io/wifi/p2p/MessageServer.java index 79edec0..7f9c942 100644 --- a/android/src/main/java/io/wifi/p2p/MessageServer.java +++ b/android/src/main/java/io/wifi/p2p/MessageServer.java @@ -36,7 +36,7 @@ public void start(ReadableMap props, Callback callback) { try { Boolean returnMeta = false; serverSocket = new ServerSocket(8988); - Log.i(TAG, "Server: Socket opened"); + Log.i(TAG, "Server: Socket opened to receive message"); if (props != null) { Bundle bundle = Arguments.toBundle(props); @@ -45,7 +45,7 @@ public void start(ReadableMap props, Callback callback) { Socket client = serverSocket.accept(); String clientAddress = client.getInetAddress().getHostAddress(); - Log.i(TAG, "Server: connection done"); + Log.i(TAG, "Server: connection done (receiveMessage)"); InputStream inputstream = client.getInputStream(); String message = convertStreamToString(inputstream); @@ -71,7 +71,7 @@ public void stop() { if (serverSocket != null) { try { serverSocket.close(); - Log.i(TAG, "Server: Socket closed"); + Log.i(TAG, "Server: Socket closed to receive message"); } catch (IOException e) { Log.e(TAG, e.getMessage()); } diff --git a/android/src/main/java/io/wifi/p2p/WiFiP2PManagerModule.java b/android/src/main/java/io/wifi/p2p/WiFiP2PManagerModule.java index 0fb68f2..f786abe 100644 --- a/android/src/main/java/io/wifi/p2p/WiFiP2PManagerModule.java +++ b/android/src/main/java/io/wifi/p2p/WiFiP2PManagerModule.java @@ -39,6 +39,7 @@ public class WiFiP2PManagerModule extends ReactContextBaseJavaModule private static final String TAG = "RNWiFiP2P"; private WiFiP2PDeviceMapper mapper = new WiFiP2PDeviceMapper(); private MessageServer messageServer; + private FileServer fileServer; public WiFiP2PManagerModule(ReactApplicationContext reactContext) { super(reactContext); @@ -291,7 +292,11 @@ protected void onReceiveResult(int resultCode, Bundle resultData) { @ReactMethod public void receiveFile( - String folder, String fileName, final Boolean forceToScanGallery, final Callback callback) { + String folder, + String fileName, + final Boolean forceToScanGallery, + final ReadableMap extraProps, + final Callback callback) { final String destination = folder + fileName; manager.requestConnectionInfo( channel, @@ -299,34 +304,34 @@ public void receiveFile( @Override public void onConnectionInfoAvailable(WifiP2pInfo info) { if (info.groupFormed) { - new FileServerAsyncTask( - getCurrentActivity(), - callback, - destination, - new CustomDefinedCallback() { - @Override - public void invoke(Object object) { - if (forceToScanGallery) { // fixes: - // https://github.com/kirillzyusko/react-native-wifi-p2p/issues/31 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - final Intent scanIntent = - new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); - final File file = new File(destination); - final Uri contentUri = Uri.fromFile(file); - scanIntent.setData(contentUri); - reactContext.sendBroadcast(scanIntent); - } else { - final Intent intent = - new Intent( - Intent.ACTION_MEDIA_MOUNTED, - Uri.parse( - "file://" + Environment.getExternalStorageDirectory())); - reactContext.sendBroadcast(intent); - } - } + if (fileServer == null) { + fileServer = new FileServer(); + } + CustomDefinedCallback customDefinedCallback = + new CustomDefinedCallback() { + @Override + public void invoke(Object object) { + if (forceToScanGallery) { // fixes: + // https://github.com/kirillzyusko/react-native-wifi-p2p/issues/31 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + final Intent scanIntent = + new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + final File file = new File(destination); + final Uri contentUri = Uri.fromFile(file); + scanIntent.setData(contentUri); + reactContext.sendBroadcast(scanIntent); + } else { + final Intent intent = + new Intent( + Intent.ACTION_MEDIA_MOUNTED, + Uri.parse("file://" + Environment.getExternalStorageDirectory())); + reactContext.sendBroadcast(intent); } - }) - .execute(); + } + } + }; + + fileServer.start(destination, extraProps, customDefinedCallback, callback); } else { Log.i(TAG, "You must be in a group to receive a file"); } @@ -334,6 +339,13 @@ public void invoke(Object object) { }); } + @ReactMethod + public void stopReceivingFile() { + if (fileServer != null) { + fileServer.stop(); + } + } + @ReactMethod public void sendMessage(String message, final Promise promise) { if (wifiP2pInfo.groupOwnerAddress != null) { diff --git a/index.js b/index.js index aff6b9c..8f2a315 100644 --- a/index.js +++ b/index.js @@ -93,17 +93,19 @@ const sendFile = (pathToFile) => WiFiP2PManager.sendFile(pathToFile); const sendFileTo = (pathToFile, address) => WiFiP2PManager.sendFileTo(pathToFile, address); -const receiveFile = (folder, fileName, forceToScanGallery = false) => new Promise((resolve, reject) => { - WiFiP2PManager.receiveFile(folder, fileName, forceToScanGallery, (pathToFile) => { +const receiveFile = (folder, fileName, forceToScanGallery = false, extraProps = {}) => new Promise((resolve, reject) => { + WiFiP2PManager.receiveFile(folder, fileName, forceToScanGallery, extraProps, (pathToFile) => { resolve(pathToFile); }); }); +const stopReceivingFile = () => WiFiP2PManager.stopReceivingFile() + const sendMessage = (message) => WiFiP2PManager.sendMessage(message); const sendMessageTo = (message, address) => WiFiP2PManager.sendMessageTo(message, address); -const receiveMessage = (props) => new Promise((resolve, reject) => { +const receiveMessage = (props = {}) => new Promise((resolve, reject) => { WiFiP2PManager.receiveMessage(props, (message) => { resolve(message); }); @@ -137,6 +139,7 @@ export { sendFile, sendFileTo, receiveFile, + stopReceivingFile, sendMessage, sendMessageTo, receiveMessage,