diff --git a/flutter_app/android/app/src/main/kotlin/com/nxg/openclawproot/MainActivity.kt b/flutter_app/android/app/src/main/kotlin/com/nxg/openclawproot/MainActivity.kt index e62e58a..e73d93a 100644 --- a/flutter_app/android/app/src/main/kotlin/com/nxg/openclawproot/MainActivity.kt +++ b/flutter_app/android/app/src/main/kotlin/com/nxg/openclawproot/MainActivity.kt @@ -7,10 +7,12 @@ import android.app.PendingIntent import android.content.ClipData import android.content.ClipboardManager import android.content.Intent +import android.content.IntentFilter import android.net.Uri import android.Manifest import android.content.pm.PackageManager import android.os.Build +import android.os.BatteryManager import android.os.PowerManager import android.provider.Settings import androidx.core.app.ActivityCompat @@ -232,6 +234,60 @@ class MainActivity : FlutterActivity() { val pm = getSystemService(POWER_SERVICE) as PowerManager result.success(!pm.isIgnoringBatteryOptimizations(packageName)) } + "getBatteryStatus" -> { + try { + val batteryIntent = + registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) + if (batteryIntent == null) { + result.error("BATTERY_ERROR", "Battery status unavailable", null) + return@setMethodCallHandler + } + + val level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) + val scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1) + val temperature = + batteryIntent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1) + val voltage = batteryIntent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1) + val status = batteryIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1) + val plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) + + val percentage = + if (level >= 0 && scale > 0) ((level * 100f) / scale).toInt() else -1 + + val statusText = when (status) { + BatteryManager.BATTERY_STATUS_CHARGING -> "CHARGING" + BatteryManager.BATTERY_STATUS_DISCHARGING -> "DISCHARGING" + BatteryManager.BATTERY_STATUS_FULL -> "FULL" + BatteryManager.BATTERY_STATUS_NOT_CHARGING -> "NOT_CHARGING" + else -> "UNKNOWN" + } + + val pluggedText = when { + (plugged and BatteryManager.BATTERY_PLUGGED_AC) != 0 -> "AC" + (plugged and BatteryManager.BATTERY_PLUGGED_USB) != 0 -> "USB" + (plugged and BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 -> "WIRELESS" + else -> "UNPLUGGED" + } + + val data = hashMapOf( + "percentage" to percentage, + "level" to level, + "scale" to scale, + "status" to statusText, + "plugged" to pluggedText, + "isCharging" to ( + status == BatteryManager.BATTERY_STATUS_CHARGING || + status == BatteryManager.BATTERY_STATUS_FULL + ), + "temperatureC" to if (temperature >= 0) temperature / 10.0 else -1.0, + "voltageMv" to voltage, + ) + + result.success(data) + } catch (e: Exception) { + result.error("BATTERY_ERROR", e.message, null) + } + } "setupDirs" -> { Thread { try { diff --git a/flutter_app/lib/providers/node_provider.dart b/flutter_app/lib/providers/node_provider.dart index 2848636..79126d9 100644 --- a/flutter_app/lib/providers/node_provider.dart +++ b/flutter_app/lib/providers/node_provider.dart @@ -5,6 +5,7 @@ import '../models/gateway_state.dart'; import '../models/node_state.dart'; import '../services/capabilities/camera_capability.dart'; import '../services/capabilities/canvas_capability.dart'; +import '../services/capabilities/battery_capability.dart'; import '../services/capabilities/flash_capability.dart'; import '../services/capabilities/location_capability.dart'; import '../services/capabilities/screen_capability.dart'; @@ -26,6 +27,7 @@ class NodeProvider extends ChangeNotifier with WidgetsBindingObserver { // Capabilities final _cameraCapability = CameraCapability(); final _canvasCapability = CanvasCapability(); + final _batteryCapability = BatteryCapability(); final _flashCapability = FlashCapability(); final _locationCapability = LocationCapability(); final _screenCapability = ScreenCapability(); @@ -135,6 +137,11 @@ class NodeProvider extends ChangeNotifier with WidgetsBindingObserver { _canvasCapability.commands.map((c) => '${_canvasCapability.name}.$c').toList(), (cmd, params) => _canvasCapability.handle(cmd, params), ); + _nodeService.registerCapability( + _batteryCapability.name, + _batteryCapability.commands.map((c) => '${_batteryCapability.name}.$c').toList(), + (cmd, params) => _batteryCapability.handle(cmd, params), + ); _nodeService.registerCapability( _locationCapability.name, _locationCapability.commands.map((c) => '${_locationCapability.name}.$c').toList(), diff --git a/flutter_app/lib/services/capabilities/battery_capability.dart b/flutter_app/lib/services/capabilities/battery_capability.dart new file mode 100644 index 0000000..52d04e9 --- /dev/null +++ b/flutter_app/lib/services/capabilities/battery_capability.dart @@ -0,0 +1,42 @@ +import '../../models/node_frame.dart'; +import '../native_bridge.dart'; +import 'capability_handler.dart'; + +class BatteryCapability extends CapabilityHandler { + @override + String get name => 'battery'; + + @override + List get commands => ['status']; + + @override + Future checkPermission() async => true; + + @override + Future requestPermission() async => true; + + @override + Future handle(String command, Map params) async { + switch (command) { + case 'battery.status': + return _status(); + default: + return NodeFrame.response('', error: { + 'code': 'UNKNOWN_COMMAND', + 'message': 'Unknown battery command: $command', + }); + } + } + + Future _status() async { + try { + final data = await NativeBridge.getBatteryStatus(); + return NodeFrame.response('', payload: Map.from(data)); + } catch (e) { + return NodeFrame.response('', error: { + 'code': 'BATTERY_ERROR', + 'message': '$e', + }); + } + } +} diff --git a/flutter_app/lib/services/gateway_service.dart b/flutter_app/lib/services/gateway_service.dart index a51097f..2c61d6c 100644 --- a/flutter_app/lib/services/gateway_service.dart +++ b/flutter_app/lib/services/gateway_service.dart @@ -127,6 +127,7 @@ class GatewayService { 'canvas.navigate', 'canvas.eval', 'canvas.snapshot', 'flash.on', 'flash.off', 'flash.toggle', 'flash.status', 'location.get', + 'battery.status', 'screen.record', 'sensor.read', 'sensor.list', 'haptic.vibrate', diff --git a/flutter_app/lib/services/native_bridge.dart b/flutter_app/lib/services/native_bridge.dart index 70b0cbe..08c1f58 100644 --- a/flutter_app/lib/services/native_bridge.dart +++ b/flutter_app/lib/services/native_bridge.dart @@ -98,6 +98,11 @@ class NativeBridge { return await _channel.invokeMethod('isNodeServiceRunning'); } + static Future> getBatteryStatus() async { + final result = await _channel.invokeMethod('getBatteryStatus'); + return Map.from(result as Map); + } + static Future updateNodeNotification(String text) async { return await _channel.invokeMethod('updateNodeNotification', {'text': text}); }