@cappitolian/network-discovery 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CappitolianNetworkDiscovery.podspec +17 -0
- package/Package.swift +28 -0
- package/README.md +365 -0
- package/android/build.gradle +58 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/cappitolian/plugins/networkdiscovery/NetworkDiscovery.java +218 -0
- package/android/src/main/java/com/cappitolian/plugins/networkdiscovery/NetworkDiscoveryPlugin.java +116 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +264 -0
- package/dist/esm/definitions.d.ts +62 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +14 -0
- package/dist/esm/web.js +18 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +32 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +35 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/NetworkDiscoveryPlugin/NetworkDiscovery.swift +158 -0
- package/ios/Sources/NetworkDiscoveryPlugin/NetworkDiscoveryPlugin.swift +75 -0
- package/ios/Tests/NetworkDiscoveryPluginTests/NetworkDiscoveryTests.swift +15 -0
- package/package.json +86 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'CappitolianNetworkDiscovery'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.license = package['license']
|
|
10
|
+
s.homepage = package['repository']['url']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
|
|
13
|
+
s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
|
+
s.ios.deployment_target = '14.0'
|
|
15
|
+
s.dependency 'Capacitor'
|
|
16
|
+
s.swift_version = '5.1'
|
|
17
|
+
end
|
package/Package.swift
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "CappitolianNetworkDiscovery",
|
|
6
|
+
platforms: [.iOS(.v15)],
|
|
7
|
+
products: [
|
|
8
|
+
.library(
|
|
9
|
+
name: "CappitolianNetworkDiscovery",
|
|
10
|
+
targets: ["NetworkDiscoveryPlugin"])
|
|
11
|
+
],
|
|
12
|
+
dependencies: [
|
|
13
|
+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0")
|
|
14
|
+
],
|
|
15
|
+
targets: [
|
|
16
|
+
.target(
|
|
17
|
+
name: "NetworkDiscoveryPlugin",
|
|
18
|
+
dependencies: [
|
|
19
|
+
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
20
|
+
.product(name: "Cordova", package: "capacitor-swift-pm")
|
|
21
|
+
],
|
|
22
|
+
path: "ios/Sources/NetworkDiscoveryPlugin"),
|
|
23
|
+
.testTarget(
|
|
24
|
+
name: "NetworkDiscoveryPluginTests",
|
|
25
|
+
dependencies: ["NetworkDiscoveryPlugin"],
|
|
26
|
+
path: "ios/Tests/NetworkDiscoveryPluginTests")
|
|
27
|
+
]
|
|
28
|
+
)
|
package/README.md
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# @cappitolian/network-discovery
|
|
2
|
+
|
|
3
|
+
A Capacitor plugin for network service discovery using mDNS/Bonjour. Allows automatic server-client connection without manual IP configuration.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Advertise services** on the local network (server mode)
|
|
10
|
+
- **Discover services** automatically (client mode)
|
|
11
|
+
- Pass custom data via TXT records (like IP addresses, ports, etc.)
|
|
12
|
+
- Real-time service found/lost events
|
|
13
|
+
- Works on **iOS** (Bonjour) and **Android** (NSD)
|
|
14
|
+
- Tested with **Capacitor 7** and **Ionic 8**
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
```bash
|
|
20
|
+
npm install @cappitolian/network-discovery
|
|
21
|
+
npx cap sync
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Import
|
|
29
|
+
```typescript
|
|
30
|
+
import { NetworkDiscovery } from '@cappitolian/network-discovery';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Server Mode - Advertise Your Service
|
|
34
|
+
```typescript
|
|
35
|
+
// Start advertising your server
|
|
36
|
+
await NetworkDiscovery.startAdvertising({
|
|
37
|
+
serviceName: 'MyAppServer',
|
|
38
|
+
serviceType: '_http._tcp', // or '_myapp._tcp' for custom service
|
|
39
|
+
port: 8080,
|
|
40
|
+
txtRecord: {
|
|
41
|
+
ip: '192.168.1.100', // Your server IP
|
|
42
|
+
version: '1.0.0' // Any custom data
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
console.log('Server is now discoverable on the network');
|
|
47
|
+
|
|
48
|
+
// Stop advertising when needed
|
|
49
|
+
await NetworkDiscovery.stopAdvertising();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Client Mode - Discover Services
|
|
53
|
+
```typescript
|
|
54
|
+
// Listen for discovered services
|
|
55
|
+
NetworkDiscovery.addListener('serviceFound', (service) => {
|
|
56
|
+
console.log('Service discovered:', service);
|
|
57
|
+
console.log('Server IP:', service.txtRecord?.ip);
|
|
58
|
+
console.log('Server Port:', service.port);
|
|
59
|
+
console.log('Server Addresses:', service.addresses);
|
|
60
|
+
|
|
61
|
+
// Connect to your server
|
|
62
|
+
connectToServer(service.addresses[0], service.port);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Listen for lost services
|
|
66
|
+
NetworkDiscovery.addListener('serviceLost', (service) => {
|
|
67
|
+
console.log('Service lost:', service.serviceName);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Start discovery
|
|
71
|
+
await NetworkDiscovery.startDiscovery({
|
|
72
|
+
serviceType: '_http._tcp', // Must match the server's serviceType
|
|
73
|
+
domain: 'local.' // Optional, defaults to 'local.'
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Stop discovery when needed
|
|
77
|
+
await NetworkDiscovery.stopDiscovery();
|
|
78
|
+
|
|
79
|
+
// Clean up listeners
|
|
80
|
+
await NetworkDiscovery.removeAllListeners();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Complete Example - Auto-Connect Flow
|
|
84
|
+
|
|
85
|
+
**Server Side:**
|
|
86
|
+
```typescript
|
|
87
|
+
import { NetworkDiscovery } from '@cappitolian/network-discovery';
|
|
88
|
+
import { Network } from '@capacitor/network';
|
|
89
|
+
|
|
90
|
+
async startServer() {
|
|
91
|
+
// Get device IP
|
|
92
|
+
const ip = await this.getLocalIP();
|
|
93
|
+
|
|
94
|
+
// Advertise server
|
|
95
|
+
await NetworkDiscovery.startAdvertising({
|
|
96
|
+
serviceName: 'MyAppServer',
|
|
97
|
+
serviceType: '_myapp._tcp',
|
|
98
|
+
port: 8080,
|
|
99
|
+
txtRecord: {
|
|
100
|
+
ip: ip,
|
|
101
|
+
serverName: 'Production Server'
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
console.log('Server advertising on network');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async getLocalIP(): Promise<string> {
|
|
109
|
+
const status = await Network.getStatus();
|
|
110
|
+
// Your IP extraction logic here
|
|
111
|
+
return '192.168.1.100';
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Client Side:**
|
|
116
|
+
```typescript
|
|
117
|
+
import { NetworkDiscovery } from '@cappitolian/network-discovery';
|
|
118
|
+
|
|
119
|
+
async findAndConnectToServer() {
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
|
|
122
|
+
// Listen for server
|
|
123
|
+
NetworkDiscovery.addListener('serviceFound', async (service) => {
|
|
124
|
+
if (service.serviceName === 'MyAppServer') {
|
|
125
|
+
console.log('Server found!');
|
|
126
|
+
|
|
127
|
+
const serverIP = service.txtRecord?.ip || service.addresses[0];
|
|
128
|
+
const serverPort = service.port;
|
|
129
|
+
|
|
130
|
+
// Stop discovery
|
|
131
|
+
await NetworkDiscovery.stopDiscovery();
|
|
132
|
+
await NetworkDiscovery.removeAllListeners();
|
|
133
|
+
|
|
134
|
+
// Connect to server
|
|
135
|
+
resolve({ ip: serverIP, port: serverPort });
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Start discovery
|
|
140
|
+
NetworkDiscovery.startDiscovery({
|
|
141
|
+
serviceType: '_myapp._tcp'
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Timeout after 10 seconds
|
|
145
|
+
setTimeout(() => {
|
|
146
|
+
reject(new Error('Server not found'));
|
|
147
|
+
}, 10000);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Usage
|
|
152
|
+
async onLoginClick() {
|
|
153
|
+
try {
|
|
154
|
+
const server = await this.findAndConnectToServer();
|
|
155
|
+
console.log(`Connecting to ${server.ip}:${server.port}`);
|
|
156
|
+
// Your connection logic here
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error('Could not find server:', error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## API Reference
|
|
166
|
+
|
|
167
|
+
### Methods
|
|
168
|
+
|
|
169
|
+
#### `startAdvertising(options: AdvertisingOptions)`
|
|
170
|
+
|
|
171
|
+
Publishes a service on the local network.
|
|
172
|
+
|
|
173
|
+
**Parameters:**
|
|
174
|
+
- `serviceName` (string): Name of your service (e.g., "MyAppServer")
|
|
175
|
+
- `serviceType` (string): Service type (e.g., "_http._tcp", "_myapp._tcp")
|
|
176
|
+
- `port` (number): Port number your server is listening on
|
|
177
|
+
- `txtRecord` (object, optional): Key-value pairs to broadcast (e.g., IP, version)
|
|
178
|
+
|
|
179
|
+
**Returns:** `Promise<{ success: boolean }>`
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
#### `stopAdvertising()`
|
|
184
|
+
|
|
185
|
+
Stops advertising the service.
|
|
186
|
+
|
|
187
|
+
**Returns:** `Promise<{ success: boolean }>`
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
#### `startDiscovery(options: DiscoveryOptions)`
|
|
192
|
+
|
|
193
|
+
Starts searching for services on the local network.
|
|
194
|
+
|
|
195
|
+
**Parameters:**
|
|
196
|
+
- `serviceType` (string): Type of service to search for (must match server's type)
|
|
197
|
+
- `domain` (string, optional): Domain to search in (default: "local.")
|
|
198
|
+
|
|
199
|
+
**Returns:** `Promise<void>`
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
#### `stopDiscovery()`
|
|
204
|
+
|
|
205
|
+
Stops the service discovery.
|
|
206
|
+
|
|
207
|
+
**Returns:** `Promise<{ success: boolean }>`
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### Events
|
|
212
|
+
|
|
213
|
+
#### `serviceFound`
|
|
214
|
+
|
|
215
|
+
Fired when a service is discovered.
|
|
216
|
+
|
|
217
|
+
**Payload:**
|
|
218
|
+
```typescript
|
|
219
|
+
{
|
|
220
|
+
serviceName: string;
|
|
221
|
+
serviceType: string;
|
|
222
|
+
domain: string;
|
|
223
|
+
hostName: string;
|
|
224
|
+
port: number;
|
|
225
|
+
addresses: string[]; // Array of IP addresses
|
|
226
|
+
txtRecord?: { // Custom data from server
|
|
227
|
+
[key: string]: string;
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
#### `serviceLost`
|
|
235
|
+
|
|
236
|
+
Fired when a previously discovered service is no longer available.
|
|
237
|
+
|
|
238
|
+
**Payload:**
|
|
239
|
+
```typescript
|
|
240
|
+
{
|
|
241
|
+
serviceName: string;
|
|
242
|
+
serviceType: string;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Service Type Format
|
|
249
|
+
|
|
250
|
+
Service types must follow the format: `_name._protocol`
|
|
251
|
+
|
|
252
|
+
**Common examples:**
|
|
253
|
+
- `_http._tcp` - HTTP service
|
|
254
|
+
- `_https._tcp` - HTTPS service
|
|
255
|
+
- `_myapp._tcp` - Custom app service
|
|
256
|
+
- `_ssh._tcp` - SSH service
|
|
257
|
+
|
|
258
|
+
**Recommended for apps:** Use a custom service type like `_myapp._tcp` to avoid conflicts with other services.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Permissions
|
|
263
|
+
|
|
264
|
+
### Android
|
|
265
|
+
|
|
266
|
+
Add to `AndroidManifest.xml`:
|
|
267
|
+
```xml
|
|
268
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
269
|
+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
270
|
+
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
|
271
|
+
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### iOS
|
|
275
|
+
|
|
276
|
+
No additional permissions required. Bonjour is enabled by default.
|
|
277
|
+
|
|
278
|
+
Optionally, add to `Info.plist` to declare your service:
|
|
279
|
+
```xml
|
|
280
|
+
<key>NSBonjourServices</key>
|
|
281
|
+
<array>
|
|
282
|
+
<string>_myapp._tcp</string>
|
|
283
|
+
</array>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Platforms
|
|
289
|
+
|
|
290
|
+
- **iOS** (Bonjour/NetService)
|
|
291
|
+
- **Android** (Network Service Discovery)
|
|
292
|
+
- **Web** (Not implemented - throws unimplemented error)
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Requirements
|
|
297
|
+
|
|
298
|
+
- [Capacitor 7+](https://capacitorjs.com/)
|
|
299
|
+
- [Ionic 8+](https://ionicframework.com/) (optional, but tested)
|
|
300
|
+
- iOS 12.0+
|
|
301
|
+
- Android API 16+ (Android 4.1+)
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Troubleshooting
|
|
306
|
+
|
|
307
|
+
### Services not being discovered
|
|
308
|
+
|
|
309
|
+
1. **Check both devices are on the same WiFi network**
|
|
310
|
+
2. **Verify service types match** exactly between server and client
|
|
311
|
+
3. **Check firewall settings** - some networks block mDNS
|
|
312
|
+
4. **Android:** Ensure multicast is enabled on your network
|
|
313
|
+
5. **iOS:** Make sure device is not in Low Power Mode
|
|
314
|
+
|
|
315
|
+
### Server not advertising
|
|
316
|
+
|
|
317
|
+
1. **Verify port is not in use** by another service
|
|
318
|
+
2. **Check network permissions** are granted
|
|
319
|
+
3. **Restart the app** after installing the plugin
|
|
320
|
+
|
|
321
|
+
### General debugging
|
|
322
|
+
|
|
323
|
+
Enable verbose logging:
|
|
324
|
+
```typescript
|
|
325
|
+
// Check plugin is loaded
|
|
326
|
+
console.log('Plugin available:', NetworkDiscovery);
|
|
327
|
+
|
|
328
|
+
// Log all events
|
|
329
|
+
NetworkDiscovery.addListener('serviceFound', (s) => console.log('Found:', s));
|
|
330
|
+
NetworkDiscovery.addListener('serviceLost', (s) => console.log('Lost:', s));
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Use Cases
|
|
336
|
+
|
|
337
|
+
- **Auto-connect apps** - Client automatically finds and connects to server
|
|
338
|
+
- **Local multiplayer games** - Discover game hosts on LAN
|
|
339
|
+
- **IoT device discovery** - Find smart devices without configuration
|
|
340
|
+
- **File sharing apps** - Discover peers for file transfer
|
|
341
|
+
- **Remote control apps** - Find controllable devices automatically
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## License
|
|
346
|
+
|
|
347
|
+
MIT
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## Support
|
|
352
|
+
|
|
353
|
+
If you encounter any issues or have feature requests, please open an issue on the [GitHub repository](https://github.com/alessandrycruz1987/network-discovery).
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Contributing
|
|
358
|
+
|
|
359
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## Credits
|
|
364
|
+
|
|
365
|
+
Created for use with Capacitor 7 and Ionic 8 applications requiring automatic network service discovery.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
ext {
|
|
2
|
+
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
|
|
4
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
|
|
5
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
buildscript {
|
|
9
|
+
repositories {
|
|
10
|
+
google()
|
|
11
|
+
mavenCentral()
|
|
12
|
+
}
|
|
13
|
+
dependencies {
|
|
14
|
+
classpath 'com.android.tools.build:gradle:8.13.0'
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
apply plugin: 'com.android.library'
|
|
19
|
+
|
|
20
|
+
android {
|
|
21
|
+
namespace = "com.cappitolian.plugins.networkdiscovery"
|
|
22
|
+
compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
23
|
+
defaultConfig {
|
|
24
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
25
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
|
|
26
|
+
versionCode 1
|
|
27
|
+
versionName "1.0"
|
|
28
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
29
|
+
}
|
|
30
|
+
buildTypes {
|
|
31
|
+
release {
|
|
32
|
+
minifyEnabled false
|
|
33
|
+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
lintOptions {
|
|
37
|
+
abortOnError = false
|
|
38
|
+
}
|
|
39
|
+
compileOptions {
|
|
40
|
+
sourceCompatibility JavaVersion.VERSION_21
|
|
41
|
+
targetCompatibility JavaVersion.VERSION_21
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
repositories {
|
|
46
|
+
google()
|
|
47
|
+
mavenCentral()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
dependencies {
|
|
52
|
+
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
53
|
+
implementation project(':capacitor-android')
|
|
54
|
+
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
55
|
+
testImplementation "junit:junit:$junitVersion"
|
|
56
|
+
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
57
|
+
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
58
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
package com.cappitolian.plugins.networkdiscovery;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.net.nsd.NsdManager;
|
|
5
|
+
import android.net.nsd.NsdServiceInfo;
|
|
6
|
+
import android.util.Log;
|
|
7
|
+
|
|
8
|
+
import com.getcapacitor.JSObject;
|
|
9
|
+
import com.getcapacitor.Plugin;
|
|
10
|
+
|
|
11
|
+
import org.json.JSONArray;
|
|
12
|
+
import org.json.JSONException;
|
|
13
|
+
|
|
14
|
+
import java.net.InetAddress;
|
|
15
|
+
import java.util.Map;
|
|
16
|
+
|
|
17
|
+
public class NetworkDiscovery {
|
|
18
|
+
private static final String TAG = "NetworkDiscovery";
|
|
19
|
+
private NsdManager nsdManager;
|
|
20
|
+
private NsdManager.RegistrationListener registrationListener;
|
|
21
|
+
private NsdManager.DiscoveryListener discoveryListener;
|
|
22
|
+
private NsdServiceInfo serviceInfo;
|
|
23
|
+
private Plugin plugin;
|
|
24
|
+
|
|
25
|
+
public NetworkDiscovery(Plugin plugin, Context context) {
|
|
26
|
+
this.plugin = plugin;
|
|
27
|
+
this.nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public void startAdvertising(
|
|
31
|
+
String serviceName,
|
|
32
|
+
String serviceType,
|
|
33
|
+
int port,
|
|
34
|
+
JSObject txtRecord,
|
|
35
|
+
AdvertisingCallback callback
|
|
36
|
+
) {
|
|
37
|
+
serviceInfo = new NsdServiceInfo();
|
|
38
|
+
serviceInfo.setServiceName(serviceName);
|
|
39
|
+
serviceInfo.setServiceType(serviceType);
|
|
40
|
+
serviceInfo.setPort(port);
|
|
41
|
+
|
|
42
|
+
// Agregar TXT records
|
|
43
|
+
if (txtRecord != null) {
|
|
44
|
+
for (String key : txtRecord.keys()) {
|
|
45
|
+
try {
|
|
46
|
+
String value = txtRecord.getString(key);
|
|
47
|
+
serviceInfo.setAttribute(key, value);
|
|
48
|
+
} catch (Exception e) {
|
|
49
|
+
Log.e(TAG, "Error setting attribute: " + key, e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
registrationListener = new NsdManager.RegistrationListener() {
|
|
55
|
+
@Override
|
|
56
|
+
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
|
57
|
+
Log.e(TAG, "Service registration failed: " + errorCode);
|
|
58
|
+
callback.onError("Registration failed with error code: " + errorCode);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@Override
|
|
62
|
+
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
|
63
|
+
Log.e(TAG, "Service unregistration failed: " + errorCode);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Override
|
|
67
|
+
public void onServiceRegistered(NsdServiceInfo serviceInfo) {
|
|
68
|
+
Log.d(TAG, "Service registered: " + serviceInfo.getServiceName());
|
|
69
|
+
callback.onSuccess();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Override
|
|
73
|
+
public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
|
|
74
|
+
Log.d(TAG, "Service unregistered");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public void stopAdvertising(StopCallback callback) {
|
|
82
|
+
if (registrationListener != null) {
|
|
83
|
+
try {
|
|
84
|
+
nsdManager.unregisterService(registrationListener);
|
|
85
|
+
registrationListener = null;
|
|
86
|
+
callback.onSuccess();
|
|
87
|
+
} catch (Exception e) {
|
|
88
|
+
callback.onError("Error stopping advertising: " + e.getMessage());
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
callback.onError("No active advertising to stop");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public void startDiscovery(String serviceType, DiscoveryCallback callback) {
|
|
96
|
+
discoveryListener = new NsdManager.DiscoveryListener() {
|
|
97
|
+
@Override
|
|
98
|
+
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
|
|
99
|
+
Log.e(TAG, "Discovery start failed: " + errorCode);
|
|
100
|
+
nsdManager.stopServiceDiscovery(this);
|
|
101
|
+
callback.onError("Discovery failed with error code: " + errorCode);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@Override
|
|
105
|
+
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
|
|
106
|
+
Log.e(TAG, "Discovery stop failed: " + errorCode);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@Override
|
|
110
|
+
public void onDiscoveryStarted(String serviceType) {
|
|
111
|
+
Log.d(TAG, "Service discovery started");
|
|
112
|
+
callback.onDiscoveryStarted();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@Override
|
|
116
|
+
public void onDiscoveryStopped(String serviceType) {
|
|
117
|
+
Log.d(TAG, "Service discovery stopped");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@Override
|
|
121
|
+
public void onServiceFound(NsdServiceInfo service) {
|
|
122
|
+
Log.d(TAG, "Service found: " + service.getServiceName());
|
|
123
|
+
|
|
124
|
+
nsdManager.resolveService(service, new NsdManager.ResolveListener() {
|
|
125
|
+
@Override
|
|
126
|
+
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
|
127
|
+
Log.e(TAG, "Resolve failed: " + errorCode);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@Override
|
|
131
|
+
public void onServiceResolved(NsdServiceInfo serviceInfo) {
|
|
132
|
+
Log.d(TAG, "Service resolved: " + serviceInfo);
|
|
133
|
+
|
|
134
|
+
JSObject serviceData = buildServiceObject(serviceInfo);
|
|
135
|
+
callback.onServiceFound(serviceData);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@Override
|
|
141
|
+
public void onServiceLost(NsdServiceInfo service) {
|
|
142
|
+
Log.d(TAG, "Service lost: " + service.getServiceName());
|
|
143
|
+
|
|
144
|
+
JSObject serviceData = new JSObject();
|
|
145
|
+
serviceData.put("serviceName", service.getServiceName());
|
|
146
|
+
serviceData.put("serviceType", service.getServiceType());
|
|
147
|
+
|
|
148
|
+
callback.onServiceLost(serviceData);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryListener);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public void stopDiscovery(StopCallback callback) {
|
|
156
|
+
if (discoveryListener != null) {
|
|
157
|
+
try {
|
|
158
|
+
nsdManager.stopServiceDiscovery(discoveryListener);
|
|
159
|
+
discoveryListener = null;
|
|
160
|
+
callback.onSuccess();
|
|
161
|
+
} catch (Exception e) {
|
|
162
|
+
callback.onError("Error stopping discovery: " + e.getMessage());
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
callback.onError("No active discovery to stop");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private JSObject buildServiceObject(NsdServiceInfo serviceInfo) {
|
|
170
|
+
JSObject serviceData = new JSObject();
|
|
171
|
+
serviceData.put("serviceName", serviceInfo.getServiceName());
|
|
172
|
+
serviceData.put("serviceType", serviceInfo.getServiceType());
|
|
173
|
+
serviceData.put("hostName", serviceInfo.getHost() != null ? serviceInfo.getHost().getHostName() : "");
|
|
174
|
+
serviceData.put("port", serviceInfo.getPort());
|
|
175
|
+
|
|
176
|
+
// Agregar direcciones IP
|
|
177
|
+
InetAddress host = serviceInfo.getHost();
|
|
178
|
+
if (host != null) {
|
|
179
|
+
try {
|
|
180
|
+
JSONArray addresses = new JSONArray();
|
|
181
|
+
addresses.put(host.getHostAddress());
|
|
182
|
+
serviceData.put("addresses", addresses);
|
|
183
|
+
} catch (JSONException e) {
|
|
184
|
+
Log.e(TAG, "Error adding addresses", e);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Agregar TXT records
|
|
189
|
+
Map<String, byte[]> attributes = serviceInfo.getAttributes();
|
|
190
|
+
if (attributes != null && !attributes.isEmpty()) {
|
|
191
|
+
JSObject txtRecordObj = new JSObject();
|
|
192
|
+
for (Map.Entry<String, byte[]> entry : attributes.entrySet()) {
|
|
193
|
+
txtRecordObj.put(entry.getKey(), new String(entry.getValue()));
|
|
194
|
+
}
|
|
195
|
+
serviceData.put("txtRecord", txtRecordObj);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return serviceData;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Callbacks interfaces
|
|
202
|
+
public interface AdvertisingCallback {
|
|
203
|
+
void onSuccess();
|
|
204
|
+
void onError(String error);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public interface StopCallback {
|
|
208
|
+
void onSuccess();
|
|
209
|
+
void onError(String error);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
public interface DiscoveryCallback {
|
|
213
|
+
void onDiscoveryStarted();
|
|
214
|
+
void onServiceFound(JSObject service);
|
|
215
|
+
void onServiceLost(JSObject service);
|
|
216
|
+
void onError(String error);
|
|
217
|
+
}
|
|
218
|
+
}
|