diff --git a/LICENSE b/LICENSE index 41e04e1..ba75c69 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1 @@ -MIT License - -Copyright (c) 2025-2026 Andrew 'nuark' G. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +TODO: Add your license here. diff --git a/README.md b/README.md index dd33b52..6ea60f0 100644 --- a/README.md +++ b/README.md @@ -1,237 +1,8 @@ # liblinphone_flutter -A Flutter plugin that provides integration with the Linphone library for VoIP calls (audio and video) on Android and iOS platforms. +libLinPhone integration library for Flutter apps -## Third-Party Licenses +## Getting Started -This project depends on the Linphone SDK, developed by Belledonne Communications, which is dual-licensed under the GNU Affero General Public License v3 (AGPLv3) and a proprietary commercial license. +Better docs will be available some day -This wrapper library itself is licensed under the MIT License. However, it is designed to work with the Linphone SDK, and therefore **does not remove or replace the licensing requirements of Linphone**. - -### Important - -Any application using this plugin and linking against the Linphone SDK is subject to the terms of the AGPLv3, unless a commercial license for Linphone is obtained. - -In practice, this means that applications using this plugin must comply with AGPLv3 requirements, which may include releasing the application's source code under a compatible license. - -For proprietary or closed-source applications, you must obtain a commercial license for the Linphone SDK from Belledonne Communications. - -## Description - -This plugin wraps the native Linphone SDK (liblinphone) and exposes a Dart API for making SIP-based voice and video calls in Flutter applications. - -## Features - -- SIP account registration -- Audio and video calls -- Video streaming with local preview and remote view -- Call management (answer, hangup, toggle video/microphone) -- Registration and call state event streams - -## Platform Support - -| Platform | Status | -| -------- | --------- | -| Android | Supported | -| iOS | Supported | - -## Requirements - -- Flutter SDK >= 3.3.0 -- Dart SDK >= 3.9.0-333.2.beta -- Linphone SDK (native dependency) - -## Installation - -Add this to your `pubspec.yaml`: - -```yaml -dependencies: - liblinphone_flutter: ^0.0.3 -``` - -Or if you want to go with git: - -```yaml -dependencies: - liblinphone_flutter: - git: - url: https://git.nuark.xyz/nuark/liblinphone_flutter.git - ref: commit-hash -``` - -Then run: - -```bash -flutter pub get -``` - -### Platform-Specific Setup - -#### Android - -Ensure you have the necessary permissions in your `AndroidManifest.xml`: - -```xml - - - - -``` - -#### iOS - -The plugin requires access to the camera and microphone. Add the following to your `Info.plist`: - -```xml -NSCameraUsageDescription -Camera access is required for video calls -NSMicrophoneUsageDescription -Microphone access is required for audio and video calls -``` - -## Usage - -### Basic Example - -```dart -import 'package:liblinphone_flutter/liblinphone_flutter.dart'; - -final liblinphone = LiblinphoneFlutter(); - -// Initialize -await liblinphone.checkPermissions(); -await liblinphone.initialize(); - -// Register to SIP server -await liblinphone.register( - 'username', - 'password', - 'sip.server.com', - 5060, -); - -// Listen to registration events -liblinphone.registrationEvents.listen((state) { - print('Registration state: $state'); -}); - -// Listen to call events -liblinphone.callEvents.listen((state) { - print('Call state: $state'); -}); - -// Make a video call -await liblinphone.makeCall('sip:recipient@sip.server.com', true); - -// Answer incoming call -await liblinphone.answerCall(); - -// Toggle video during call -await liblinphone.toggleVideo(); - -// Toggle microphone -await liblinphone.toggleMicrophone(); - -// Hangup -await liblinphone.hangupCall(); - -// Unregister and cleanup -await liblinphone.unregister(); -await liblinphone.stop(); -``` - -### Video Views - -For video calls, use the provided widgets to display local and remote video streams: - -```dart -import 'package:liblinphone_flutter/widgets/local_view.dart'; -import 'package:liblinphone_flutter/widgets/remote_view.dart'; - -// In your widget tree -Column( - children: [ - // Remote video (full screen or large area) - Expanded( - child: RemoteView(), - ), - // Local video preview (picture-in-picture) - SizedBox( - height: 150, - width: 100, - child: LocalView(), - ), - ], -) -``` - -## API Reference - -### Main Class: `LiblinphoneFlutter` - -#### Methods - -| Method | Description | -| ----------------------------------------------------------------- | -------------------------------------------------- | -| `Future checkPermissions()` | Checks and requests camera/microphone permissions | -| `Future initialize()` | Initializes the Linphone core | -| `Future register(username, password, serverIp, serverPort)` | Registers to a SIP server | -| `Future unregister()` | Unregisters from the SIP server | -| `Future makeCall(callTo, isVideoEnabled)` | Makes an outgoing call | -| `Future answerCall()` | Answers an incoming call | -| `Future hangupCall()` | Hangs up the current call | -| `Future inCall()` | Returns true if there is an active call | -| `Future callType()` | Returns the type of the current call (audio/video) | -| `Future toggleVideo()` | Toggles video enabled state during a call | -| `Future toggleMicrophone()` | Toggles microphone muted state | -| `Future stop()` | Stops the Linphone core | -| `Future syncCurrentState()` | Forces synchronization of current state | - -#### Event Streams - -| Stream | Type | Description | -| -------------------- | --------------------------- | -------------------------------- | -| `registrationEvents` | `Stream` | Emits registration state changes | -| `callEvents` | `Stream` | Emits call state changes | - -### Enums - -#### `RegistrationState` - -- `None` - Not registered -- `Progress` - Registration in progress -- `Ok` - Successfully registered -- `Cleared` - Registration cleared -- `Failed` - Registration failed - -#### `CallState` - -- `Idle` - No active call -- `IncomingReceived` - Incoming call received -- `OutgoingInit` - Outgoing call initialized -- `OutgoingProgress` - Outgoing call in progress -- `OutgoingRinging` - Remote party ringing -- `Connected` - Call connected -- `StreamsRunning` - Media streams running -- `Pausing` - Call pausing -- `Paused` - Call paused -- `Resuming` - Call resuming -- `Error` - Call error -- `End` - Call ended -- And other states... - -#### `CallType` - -- `Audio` - Audio-only call -- `Video` - Video call -- `Unknown` - Call type unknown - -## License - -This project is licensed under the MIT License. - -## Links - -- [Homepage](https://git.nuark.xyz/nuark/liblinphone_flutter) -- [Linphone Project](https://www.linphone.org/) diff --git a/test/liblinphone_flutter_method_channel_test.dart b/test/liblinphone_flutter_method_channel_test.dart index c743500..352823a 100644 --- a/test/liblinphone_flutter_method_channel_test.dart +++ b/test/liblinphone_flutter_method_channel_test.dart @@ -1,10 +1,27 @@ -// import 'package:flutter/services.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -// import 'package:liblinphone_flutter/liblinphone_flutter_method_channel.dart'; +import 'package:liblinphone_flutter/liblinphone_flutter_method_channel.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - // MethodChannelLiblinphoneFlutter platform = MethodChannelLiblinphoneFlutter(); - // const MethodChannel channel = MethodChannel('liblinphone_flutter'); + MethodChannelLiblinphoneFlutter platform = MethodChannelLiblinphoneFlutter(); + const MethodChannel channel = MethodChannel('liblinphone_flutter'); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( + channel, + (MethodCall methodCall) async { + return '42'; + }, + ); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); } diff --git a/test/liblinphone_flutter_test.dart b/test/liblinphone_flutter_test.dart index 43fda77..f8d21e4 100644 --- a/test/liblinphone_flutter_test.dart +++ b/test/liblinphone_flutter_test.dart @@ -1,84 +1,29 @@ import 'package:flutter_test/flutter_test.dart'; -// import 'package:liblinphone_flutter/liblinphone_flutter.dart'; +import 'package:liblinphone_flutter/liblinphone_flutter.dart'; import 'package:liblinphone_flutter/liblinphone_flutter_platform_interface.dart'; -// import 'package:liblinphone_flutter/liblinphone_flutter_method_channel.dart'; -import 'package:liblinphone_flutter/models/call_type.dart'; +import 'package:liblinphone_flutter/liblinphone_flutter_method_channel.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; class MockLiblinphoneFlutterPlatform with MockPlatformInterfaceMixin implements LiblinphoneFlutterPlatform { - @override - Future answerCall() { - throw UnimplementedError(); - } @override - Future callType() { - throw UnimplementedError(); - } - - @override - Future checkPermissions() { - throw UnimplementedError(); - } - - @override - Future hangupCall() { - throw UnimplementedError(); - } - - @override - Future inCall() { - throw UnimplementedError(); - } - - @override - Future initialize() { - throw UnimplementedError(); - } - - @override - Future makeCall(String callTo, bool isVideoEnabled) { - throw UnimplementedError(); - } - - @override - Future register( - String username, - String password, - String serverIp, - int serverPort, - ) { - throw UnimplementedError(); - } - - @override - Future stop() { - throw UnimplementedError(); - } - - @override - Future syncCurrentState() { - throw UnimplementedError(); - } - - @override - Future toggleMicrophone() { - throw UnimplementedError(); - } - - @override - Future toggleVideo() { - throw UnimplementedError(); - } - - @override - Future unregister() { - throw UnimplementedError(); - } + Future getPlatformVersion() => Future.value('42'); } void main() { - // final LiblinphoneFlutterPlatform initialPlatform = LiblinphoneFlutterPlatform.instance; + final LiblinphoneFlutterPlatform initialPlatform = LiblinphoneFlutterPlatform.instance; + + test('$MethodChannelLiblinphoneFlutter is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + LiblinphoneFlutter liblinphoneFlutterPlugin = LiblinphoneFlutter(); + MockLiblinphoneFlutterPlatform fakePlatform = MockLiblinphoneFlutterPlatform(); + LiblinphoneFlutterPlatform.instance = fakePlatform; + + expect(await liblinphoneFlutterPlugin.getPlatformVersion(), '42'); + }); }