-
-
Notifications
You must be signed in to change notification settings - Fork 989
Add custom Capacitor plugin-socket for raw TCP support #4471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
haslinghuis
wants to merge
16
commits into
betaflight:master
Choose a base branch
from
haslinghuis:capacitor-plugin-socket
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+340
−1
Open
Changes from 6 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
eb322fa
Add custom Capacitor plugin-socket
haslinghuis acfa06d
Rabbit fixes
haslinghuis e337188
Refactor SocketPlugin.java
haslinghuis bf1924a
Move receive operation to background thread
haslinghuis f55e7be
update isConnected flag in closeResources catch block
haslinghuis cfefb6a
Handle null return from readLine
haslinghuis 39715ab
guard against missing or null port parameter
haslinghuis e777360
fix inconsistent error handling in receive method
haslinghuis 6e8146c
Rabbit keeps nitpicking
haslinghuis 7c25def
Suggested by rabbit
haslinghuis df2a2a4
Add getStatus
haslinghuis 338cb36
Allow new connect attempt
haslinghuis 1758893
Replace file with rabbit suggestion again
haslinghuis 2d9ff82
Add back getStatus
haslinghuis 7717b87
Add getStatus to web.ts too
haslinghuis 6e6dd70
Update message
haslinghuis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
10 changes: 9 additions & 1 deletion
10
android/app/src/main/java/betaflight/configurator/MainActivity.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,13 @@ | ||
package betaflight.configurator; | ||
|
||
import android.os.Bundle; | ||
import com.getcapacitor.BridgeActivity; | ||
import betaflight.configurator.plugin.SocketPlugin; | ||
|
||
public class MainActivity extends BridgeActivity {} | ||
public class MainActivity extends BridgeActivity { | ||
@Override | ||
public void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
registerPlugin(SocketPlugin.class); | ||
} | ||
} |
165 changes: 165 additions & 0 deletions
165
android/app/src/main/java/betaflight/configurator/plugin/SocketPlugin.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package betaflight.configurator.plugin; | ||
|
||
import android.util.Log; | ||
|
||
import com.getcapacitor.JSObject; | ||
import com.getcapacitor.Plugin; | ||
import com.getcapacitor.PluginCall; | ||
import com.getcapacitor.annotation.CapacitorPlugin; | ||
|
||
import java.io.*; | ||
import java.net.Socket; | ||
|
||
/** | ||
* Capacitor plugin that provides raw TCP socket functionality. | ||
* Implements methods to connect, send, receive, and disconnect. | ||
*/ | ||
@CapacitorPlugin(name = "SocketPlugin") | ||
public class SocketPlugin extends Plugin { | ||
private Socket socket; | ||
private BufferedReader reader; | ||
private BufferedWriter writer; | ||
private boolean isConnected = false; | ||
|
||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@PluginMethod | ||
public void connect(PluginCall call) { | ||
String ip = call.getString("ip"); | ||
int port = call.getInt("port"); | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Validate inputs | ||
if (ip == null || ip.isEmpty()) { | ||
call.reject("IP address is required"); | ||
return; | ||
} | ||
|
||
if (port <= 0 || port > 65535) { | ||
call.reject("Invalid port number"); | ||
return; | ||
} | ||
|
||
// Prevent duplicate connections | ||
if (socket != null && !socket.isClosed()) { | ||
call.reject("Already connected; please disconnect first"); | ||
return; | ||
} | ||
|
||
// Run network operations on a background thread | ||
getBridge().getExecutor().execute(() -> { | ||
try { | ||
socket = new Socket(ip, port); | ||
socket.setSoTimeout(30_000); // 30s timeout | ||
reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); | ||
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); | ||
isConnected = true; | ||
JSObject ret = new JSObject(); | ||
ret.put("success", true); | ||
call.resolve(ret); | ||
} catch (Exception e) { | ||
closeResources(); | ||
call.reject("Connection failed: " + e.getMessage()); | ||
} | ||
}); | ||
} | ||
|
||
@PluginMethod | ||
public void send(PluginCall call) { | ||
String data = call.getString("data"); | ||
|
||
// Validate input | ||
if (data == null) { | ||
call.reject("Data is required"); | ||
return; | ||
} | ||
|
||
// Check connection state | ||
if (socket == null || socket.isClosed() || !isConnected || reader == null || writer == null) { | ||
call.reject("Not connected to any server"); | ||
return; | ||
} | ||
|
||
// Run write operation on a background thread and synchronize on writer | ||
getBridge().getExecutor().execute(() -> { | ||
try { | ||
synchronized (writer) { | ||
// Append newline for framing; adjust as needed for your protocol | ||
writer.write(data); | ||
writer.newLine(); | ||
writer.flush(); | ||
} | ||
JSObject ret = new JSObject(); | ||
ret.put("success", true); | ||
call.resolve(ret); | ||
} catch (Exception e) { | ||
closeResources(); | ||
isConnected = false; | ||
call.reject("Send failed: " + e.getMessage()); | ||
} | ||
}); | ||
} | ||
haslinghuis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@PluginMethod | ||
public void receive(PluginCall call) { | ||
// Check connection state | ||
if (socket == null || socket.isClosed() || !isConnected || reader == null) { | ||
call.reject("Not connected to any server"); | ||
return; | ||
} | ||
|
||
// Run read operation on a background thread to avoid blocking the UI | ||
getBridge().getExecutor().execute(() -> { | ||
try { | ||
String data = reader.readLine(); | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (data == null) { | ||
// Stream ended or connection closed by peer | ||
closeResources(); | ||
isConnected = false; | ||
call.reject("Connection closed by peer"); | ||
return; | ||
} | ||
JSObject ret = new JSObject(); | ||
ret.put("data", data); | ||
call.resolve(ret); | ||
} catch (Exception e) { | ||
call.reject("Receive failed: " + e.getMessage()); | ||
} | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
} | ||
|
||
@PluginMethod | ||
public void disconnect(PluginCall call) { | ||
try { | ||
closeResources(); | ||
isConnected = false; | ||
JSObject ret = new JSObject(); | ||
ret.put("success", true); | ||
call.resolve(ret); | ||
} catch (Exception e) { | ||
call.reject("Disconnect failed: " + e.getMessage()); | ||
} | ||
} | ||
|
||
/** | ||
* Helper method to close all resources and clean up state | ||
*/ | ||
private void closeResources() { | ||
try { | ||
if (reader != null) { | ||
reader.close(); | ||
reader = null; | ||
} | ||
if (writer != null) { | ||
writer.close(); | ||
writer = null; | ||
} | ||
if (socket != null) { | ||
socket.close(); | ||
socket = null; | ||
} | ||
} catch (IOException e) { | ||
// Log but continue cleanup | ||
isConnected = false; | ||
getContext().getActivity().runOnUiThread(() -> | ||
Log.e("SocketPlugin", "Error closing resources", e)); | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"name": "capacitor-plugin-socket", | ||
"version": "1.0.0", | ||
"description": "A Capacitor plugin for handling raw TCP sockets.", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"files": ["dist/*", "package.json", "README.md"], | ||
"scripts": { | ||
"clean": "rimraf dist", | ||
"build": "npm run clean && tsc -p tsconfig.json" | ||
}, | ||
"keywords": ["capacitor", "plugin", "tcp", "socket"], | ||
"author": "Betaflight <dev.betaflight.com>", | ||
"license": "MIT", | ||
"peerDependencies": { | ||
"@capacitor/core": "^5.0.0" | ||
}, | ||
"devDependencies": { | ||
"typescript": "^5.0.0" | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export interface SocketPlugin { | ||
connect(options: { ip: string; port: number }): Promise<{ success: boolean }>; | ||
send(options: { data: string }): Promise<{ success: boolean }>; | ||
receive(): Promise<{ data: string }>; | ||
disconnect(): Promise<{ success: boolean }>; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { registerPlugin } from '@capacitor/core'; | ||
|
||
const SocketPlugin = registerPlugin('SocketPlugin', { | ||
web: () => import('./web').then(m => new m.SocketPluginWeb()), | ||
}); | ||
|
||
export * from './definitions'; | ||
export { SocketPlugin }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { WebPlugin } from '@capacitor/core'; | ||
import { SocketPlugin } from './definitions'; | ||
|
||
export class SocketPluginWeb extends WebPlugin implements SocketPlugin { | ||
async connect(options: { ip: string; port: number }): Promise<{ success: boolean }> { | ||
console.log('Web implementation does not support raw TCP sockets.', options); | ||
return { success: false }; | ||
} | ||
|
||
async send(options: { data: string }): Promise<{ success: boolean }> { | ||
console.log('Web implementation does not support raw TCP sockets.', options); | ||
return { success: false }; | ||
} | ||
|
||
async receive(): Promise<{ data: string }> { | ||
console.log('Web implementation does not support raw TCP sockets.'); | ||
return { data: '' }; | ||
} | ||
|
||
async disconnect(): Promise<{ success: boolean }> { | ||
console.log('Web implementation does not support raw TCP sockets.'); | ||
return { success: false }; | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.