Skip to content

Commit 890f605

Browse files
Add peers status to the app
1 parent b8364bd commit 890f605

6 files changed

Lines changed: 234 additions & 0 deletions

File tree

android/app/src/main/kotlin/tech/threefold/mycelium/MainActivity.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.flutter.embedding.engine.FlutterEngine
1515
import io.flutter.plugin.common.MethodChannel
1616
import tech.threefold.mycelium.rust.uniffi.mycelmob.addressFromSecretKey
1717
import tech.threefold.mycelium.rust.uniffi.mycelmob.generateSecretKey
18+
import tech.threefold.mycelium.rust.uniffi.mycelmob.getPeerStatus
1819

1920
private const val tag = "[Myceliumflut]"
2021

@@ -56,6 +57,15 @@ class MainActivity: FlutterActivity() {
5657
Log.d(tag, "stopping VPN")
5758
result.success(stopCmdSent)
5859
}
60+
"getPeerStatus" -> {
61+
try {
62+
val peerStatus = getPeerStatus()
63+
result.success(peerStatus)
64+
} catch (e: Exception) {
65+
Log.e(tag, "Error getting peer status: ${e.message}")
66+
result.error("PEER_STATUS_ERROR", e.message, null)
67+
}
68+
}
5969
else -> result.notImplemented()
6070
}
6171
}

android/app/src/main/kotlin/tech/threefold/mycelium/rust/uniffi/mycelmob/mycelmob.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,8 @@ internal interface UniffiForeignFutureCompleteVoid : com.sun.jna.Callback {
720720

721721

722722

723+
724+
723725

724726

725727

@@ -742,6 +744,8 @@ internal interface UniffiLib : Library {
742744
): RustBuffer.ByValue
743745
fun uniffi_mycelmob_fn_func_generate_secret_key(uniffi_out_err: UniffiRustCallStatus,
744746
): RustBuffer.ByValue
747+
fun uniffi_mycelmob_fn_func_get_peer_status(uniffi_out_err: UniffiRustCallStatus,
748+
): RustBuffer.ByValue
745749
fun uniffi_mycelmob_fn_func_hello_int(uniffi_out_err: UniffiRustCallStatus,
746750
): Int
747751
fun uniffi_mycelmob_fn_func_hello_mycelios(uniffi_out_err: UniffiRustCallStatus,
@@ -866,6 +870,8 @@ internal interface UniffiLib : Library {
866870
): Short
867871
fun uniffi_mycelmob_checksum_func_generate_secret_key(
868872
): Short
873+
fun uniffi_mycelmob_checksum_func_get_peer_status(
874+
): Short
869875
fun uniffi_mycelmob_checksum_func_hello_int(
870876
): Short
871877
fun uniffi_mycelmob_checksum_func_hello_mycelios(
@@ -897,6 +903,9 @@ private fun uniffiCheckApiChecksums(lib: UniffiLib) {
897903
if (lib.uniffi_mycelmob_checksum_func_generate_secret_key() != 63601.toShort()) {
898904
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
899905
}
906+
if (lib.uniffi_mycelmob_checksum_func_get_peer_status() != 1198.toShort()) {
907+
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
908+
}
900909
if (lib.uniffi_mycelmob_checksum_func_hello_int() != 31063.toShort()) {
901910
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
902911
}
@@ -1099,6 +1108,15 @@ public object FfiConverterSequenceString: FfiConverterRustBuffer<List<kotlin.Str
10991108
)
11001109
}
11011110

1111+
fun `getPeerStatus`(): List<kotlin.String> {
1112+
return FfiConverterSequenceString.lift(
1113+
uniffiRustCall() { _status ->
1114+
UniffiLib.INSTANCE.uniffi_mycelmob_fn_func_get_peer_status(
1115+
_status)
1116+
}
1117+
)
1118+
}
1119+
11021120
fun `helloInt`(): kotlin.Int {
11031121
return FfiConverterInt.lift(
11041122
uniffiRustCall() { _status ->

lib/main.dart

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class _MyAppState extends State<MyApp> {
5050
String _nodeAddr = '';
5151
var privKey = Uint8List(0);
5252
List<String> peers = [];
53+
List<String> _connectedPeers = [];
5354
late TextEditingController textEditController;
5455
final _flutterDesktopSleepPlugin = FlutterDesktopSleep();
5556
final ScrollController _scrollController = ScrollController();
@@ -366,6 +367,79 @@ class _MyAppState extends State<MyApp> {
366367
),
367368
),
368369
),
370+
// Connected Peers Section
371+
Visibility(
372+
visible: _isStarted && _connectedPeers.isNotEmpty,
373+
child: Column(
374+
children: [
375+
const SizedBox(height: sizedBoxHeight),
376+
const Align(
377+
alignment: Alignment.centerLeft,
378+
child: Text("Connected Peers:",
379+
style: TextStyle(
380+
fontSize: 16,
381+
color: Color(0xFF7D7E7E),
382+
fontWeight: FontWeight.w500)),
383+
),
384+
const SizedBox(height: 10),
385+
Container(
386+
width: double.infinity,
387+
constraints: const BoxConstraints(maxHeight: 200),
388+
decoration: BoxDecoration(
389+
color: const Color.fromARGB(255, 245, 241, 241),
390+
border: Border.all(color: Colors.grey),
391+
borderRadius: BorderRadius.circular(10.0),
392+
),
393+
child: SingleChildScrollView(
394+
padding: const EdgeInsets.all(8.0),
395+
child: Column(
396+
crossAxisAlignment: CrossAxisAlignment.start,
397+
children: _connectedPeers.map((peerInfo) {
398+
// Parse peer info: "protocol,address,connection_state"
399+
final parts = peerInfo.split(',');
400+
if (parts.length >= 3) {
401+
final protocol = parts[0];
402+
final address = parts[1];
403+
final state = parts[2];
404+
return Padding(
405+
padding: const EdgeInsets.symmetric(vertical: 2.0),
406+
child: Row(
407+
children: [
408+
Icon(
409+
state.toLowerCase() == 'connected'
410+
? Icons.check_circle
411+
: Icons.radio_button_unchecked,
412+
color: state.toLowerCase() == 'connected'
413+
? Colors.green
414+
: Colors.orange,
415+
size: 16,
416+
),
417+
const SizedBox(width: 8),
418+
Expanded(
419+
child: Text(
420+
'$protocol://$address ($state)',
421+
style: const TextStyle(fontSize: 12),
422+
),
423+
),
424+
],
425+
),
426+
);
427+
} else {
428+
return Padding(
429+
padding: const EdgeInsets.symmetric(vertical: 2.0),
430+
child: Text(
431+
peerInfo,
432+
style: const TextStyle(fontSize: 12),
433+
),
434+
);
435+
}
436+
}).toList(),
437+
),
438+
),
439+
),
440+
],
441+
),
442+
),
369443
],
370444
),
371445
),
@@ -474,6 +548,7 @@ class _MyAppState extends State<MyApp> {
474548
_startStopButtonColor = colorDarkBlue;
475549
_myceliumStatusColor = colorMycelRed;
476550
isRestartVisible = false;
551+
_connectedPeers.clear(); // Clear connected peers when stopped
477552
});
478553
}
479554

@@ -486,6 +561,45 @@ class _MyAppState extends State<MyApp> {
486561
_myceliumStatusColor = colorDarkBlue;
487562
isRestartVisible = true;
488563
});
564+
// Start periodic peer status updates
565+
_startPeerStatusUpdates();
566+
}
567+
568+
void _startPeerStatusUpdates() {
569+
Timer.periodic(const Duration(seconds: 5), (timer) {
570+
if (!_isStarted) {
571+
timer.cancel();
572+
return;
573+
}
574+
_updatePeerStatus();
575+
});
576+
}
577+
578+
void _updatePeerStatus() async {
579+
if (!_isStarted) return;
580+
581+
try {
582+
List<String> peerStatus;
583+
if (isUseDylib()) {
584+
// Windows platform - use FFI
585+
peerStatus = await myFFGetPeerStatus();
586+
} else {
587+
// Android/iOS platform - use platform channel
588+
final result = await platform.invokeMethod<List<dynamic>>('getPeerStatus');
589+
peerStatus = result?.cast<String>() ?? [];
590+
}
591+
592+
// Filter out the first element if it's "ok" (status indicator)
593+
if (peerStatus.isNotEmpty && peerStatus[0] == "ok") {
594+
peerStatus = peerStatus.sublist(1);
595+
}
596+
597+
setState(() {
598+
_connectedPeers = peerStatus;
599+
});
600+
} catch (e) {
601+
_logger.warning("Failed to get peer status: $e");
602+
}
489603
}
490604
}
491605

lib/myceliumflut_ffi_binding.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,48 @@ Future<bool> myFFStopMycelium() async {
155155
final result = stopMycelium();
156156
return result != 0;
157157
}
158+
159+
typedef FuncRustGetPeerStatus = ffi.Void Function(
160+
ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Int8>>>, ffi.Pointer<ffi.IntPtr>);
161+
typedef FuncDartGetPeerStatus = void Function(
162+
ffi.Pointer<ffi.Pointer<ffi.Pointer<ffi.Int8>>>, ffi.Pointer<ffi.IntPtr>);
163+
typedef FuncRustFreePeerStatus = ffi.Void Function(
164+
ffi.Pointer<ffi.Pointer<ffi.Int8>>, ffi.IntPtr);
165+
typedef FuncDartFreePeerStatus = void Function(
166+
ffi.Pointer<ffi.Pointer<ffi.Int8>>, int);
167+
168+
Future<List<String>> myFFGetPeerStatus() async {
169+
// Load the dynamic library
170+
final dylib = loadDll();
171+
172+
final FuncDartGetPeerStatus getPeerStatus = dylib
173+
.lookup<ffi.NativeFunction<FuncRustGetPeerStatus>>('ff_get_peer_status')
174+
.asFunction();
175+
final FuncDartFreePeerStatus freePeerStatus = dylib
176+
.lookup<ffi.NativeFunction<FuncRustFreePeerStatus>>('free_peer_status')
177+
.asFunction();
178+
179+
final outPtr = malloc<ffi.Pointer<ffi.Pointer<ffi.Int8>>>();
180+
final outLen = malloc<ffi.IntPtr>();
181+
182+
getPeerStatus(outPtr, outLen);
183+
184+
final ptr = outPtr.value;
185+
final len = outLen.value;
186+
187+
List<String> peerStatusList = [];
188+
for (int i = 0; i < len; i++) {
189+
final stringPtr = ptr[i];
190+
if (stringPtr != ffi.nullptr) {
191+
final dartString = stringPtr.cast<Utf8>().toDartString();
192+
peerStatusList.add(dartString);
193+
}
194+
}
195+
196+
// Free the allocated memory
197+
freePeerStatus(ptr, len);
198+
malloc.free(outPtr);
199+
malloc.free(outLen);
200+
201+
return peerStatusList;
202+
}

mycelffi/src/lib.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,45 @@ pub extern "C" fn ff_stop_mycelium() -> bool {
7373
let result = mobile::stop_mycelium();
7474
result == "ok"
7575
}
76+
77+
#[no_mangle]
78+
pub extern "C" fn ff_get_peer_status(out_ptr: *mut *mut *mut c_char, out_len: *mut usize) {
79+
let peer_status = mobile::get_peer_status();
80+
let len = peer_status.len();
81+
82+
// Convert Vec<String> to Vec<*mut c_char>
83+
let c_strings: Vec<*mut c_char> = peer_status
84+
.into_iter()
85+
.map(|s| CString::new(s).unwrap().into_raw())
86+
.collect();
87+
88+
let ptr = c_strings.as_ptr() as *mut *mut c_char;
89+
90+
// Transfer ownership to the caller
91+
std::mem::forget(c_strings);
92+
93+
unsafe {
94+
*out_ptr = ptr;
95+
*out_len = len;
96+
}
97+
}
98+
99+
#[no_mangle]
100+
pub extern "C" fn free_peer_status(ptr: *mut *mut c_char, len: usize) {
101+
unsafe {
102+
if ptr.is_null() {
103+
return;
104+
}
105+
106+
// Free each C string
107+
for i in 0..len {
108+
let c_str_ptr = *ptr.add(i);
109+
if !c_str_ptr.is_null() {
110+
let _ = CString::from_raw(c_str_ptr);
111+
}
112+
}
113+
114+
// Free the array of pointers
115+
Vec::from_raw_parts(ptr, len, len);
116+
}
117+
}

mycelmob/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,9 @@ pub fn generate_secret_key() -> Vec<u8> {
3030
#[uniffi::export]
3131
pub fn address_from_secret_key(data: Vec<u8>) -> String {
3232
mobile::address_from_secret_key(data)
33+
}
34+
35+
#[uniffi::export]
36+
pub fn get_peer_status() -> Vec<String> {
37+
mobile::get_peer_status()
3338
}

0 commit comments

Comments
 (0)