adding webrtc magic
parent
6ad7c60e32
commit
6272dd1cad
|
@ -6,6 +6,10 @@
|
|||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
|
||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||
|
|
|
@ -16,6 +16,11 @@ allprojects {
|
|||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
rootProject.buildDir = '../build'
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '9.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
project 'Runner', {
|
||||
'Debug' => :debug,
|
||||
'Profile' => :release,
|
||||
'Release' => :release,
|
||||
}
|
||||
|
||||
def parse_KV_file(file, separator='=')
|
||||
file_abs_path = File.expand_path(file)
|
||||
if !File.exists? file_abs_path
|
||||
return [];
|
||||
end
|
||||
pods_ary = []
|
||||
skip_line_start_symbols = ["#", "/"]
|
||||
File.foreach(file_abs_path) { |line|
|
||||
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
|
||||
plugin = line.split(pattern=separator)
|
||||
if plugin.length == 2
|
||||
podname = plugin[0].strip()
|
||||
path = plugin[1].strip()
|
||||
podpath = File.expand_path("#{path}", file_abs_path)
|
||||
pods_ary.push({:name => podname, :path => podpath});
|
||||
else
|
||||
puts "Invalid plugin specification: #{line}"
|
||||
end
|
||||
}
|
||||
return pods_ary
|
||||
end
|
||||
|
||||
target 'Runner' do
|
||||
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
|
||||
# referring to absolute paths on developers' machines.
|
||||
system('rm -rf .symlinks')
|
||||
system('mkdir -p .symlinks/plugins')
|
||||
|
||||
# Flutter Pods
|
||||
generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
|
||||
if generated_xcode_build_settings.empty?
|
||||
puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
|
||||
end
|
||||
generated_xcode_build_settings.map { |p|
|
||||
if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
|
||||
symlink = File.join('.symlinks', 'flutter')
|
||||
File.symlink(File.dirname(p[:path]), symlink)
|
||||
pod 'Flutter', :path => File.join(symlink, File.basename(p[:path]))
|
||||
end
|
||||
}
|
||||
|
||||
# Plugin Pods
|
||||
plugin_pods = parse_KV_file('../.flutter-plugins')
|
||||
plugin_pods.map { |p|
|
||||
symlink = File.join('.symlinks', 'plugins', p[:name])
|
||||
File.symlink(p[:path], symlink)
|
||||
pod p[:name], :path => File.join(symlink, 'ios')
|
||||
}
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['ENABLE_BITCODE'] = 'NO'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -40,6 +40,8 @@
|
|||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<false/>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>$(PRODUCT_NAME) Microphone Usage!</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import "package:flutter/services.dart";
|
||||
import 'routes.dart';
|
||||
|
||||
void main() {
|
||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
|
||||
Routes();
|
||||
}
|
||||
|
|
|
@ -17,5 +17,3 @@ class ContactBloc {
|
|||
_contactsFetcher.close();
|
||||
}
|
||||
}
|
||||
|
||||
final bloc = ContactBloc();
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import "package:flutter_webrtc/webrtc.dart";
|
||||
|
||||
class PeerManager {}
|
|
@ -0,0 +1,88 @@
|
|||
import 'dart:async';
|
||||
import 'package:eventsource/eventsource.dart';
|
||||
import 'package:flutter_webrtc/webrtc.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
enum SignalingResponse { SUCCESSFULL, NO_DEVICE, NO_DATA }
|
||||
|
||||
class PeerConnectionFactory {
|
||||
final String _selfUserId;
|
||||
final String _selfDeviceId;
|
||||
EventSource _signalingServer;
|
||||
|
||||
// could be const
|
||||
Map<String, dynamic> _iceServers = {
|
||||
"iceServers": [
|
||||
{"url": "stun:stun.l.google.com:19302"}
|
||||
]
|
||||
};
|
||||
|
||||
// could be const
|
||||
final Map<String, dynamic> _config = {
|
||||
"mandatory": {},
|
||||
"optional": [
|
||||
{"DtlsSrtpKeyAgreement": true}
|
||||
]
|
||||
};
|
||||
|
||||
// could be const
|
||||
final Map<String, dynamic> _constraints = {
|
||||
"mandatory": {
|
||||
"OfferToReceiveAudio": true,
|
||||
"OfferToReceiveVideo": false,
|
||||
},
|
||||
"optional": []
|
||||
};
|
||||
|
||||
PeerConnectionFactory(this._selfUserId, this._selfDeviceId);
|
||||
|
||||
// initialize() method sets up a subscription to the eventsource and
|
||||
// attaches a callback to it
|
||||
initialize() async {
|
||||
_signalingServer = await EventSource.connect(
|
||||
"localhost:10201/subscribe/$_selfUserId/device/$_selfDeviceId");
|
||||
_signalingServer.listen((event) => print(event.data));
|
||||
}
|
||||
|
||||
Future<RTCPeerConnection> newPeerConnection(
|
||||
String remoteId, String remoteDevice, MediaStream stream) async {
|
||||
RTCPeerConnection connection =
|
||||
await createPeerConnection(_iceServers, _config);
|
||||
connection.addStream(stream);
|
||||
|
||||
// Send candidates to remote (NOT_IMPLEMENTED TO SEND)
|
||||
connection.onIceCandidate =
|
||||
(candidate) => _sendToRemote(remoteId, remoteDevice);
|
||||
|
||||
// Create and send the offer
|
||||
RTCSessionDescription session = await connection.createOffer(_constraints);
|
||||
connection.setLocalDescription(session);
|
||||
_sendToRemote(remoteId, remoteDevice);
|
||||
|
||||
return connection;
|
||||
// NEED TO WAIT FOR ANSWER BEFORE CONNECTION IS ESTABLISHED
|
||||
}
|
||||
|
||||
peerConnectionAnswer(RTCPeerConnection connection, String remoteId,
|
||||
String remoteDevice) async {
|
||||
RTCSessionDescription session = await connection.createAnswer(_constraints);
|
||||
connection.setLocalDescription(session);
|
||||
_sendToRemote(remoteId, remoteDevice);
|
||||
}
|
||||
|
||||
Future<SignalingResponse> _sendToRemote(
|
||||
String remoteId, String remoteDevice) async {
|
||||
var response =
|
||||
await http.post("localhost:10201/user/$remoteId/device/$remoteDevice");
|
||||
switch (response.statusCode) {
|
||||
case 200:
|
||||
return SignalingResponse.SUCCESSFULL;
|
||||
case 400:
|
||||
return SignalingResponse.NO_DATA;
|
||||
case 404:
|
||||
return SignalingResponse.NO_DEVICE;
|
||||
default:
|
||||
return SignalingResponse.NO_DEVICE;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,10 +4,30 @@ import '../../models/user_model.dart';
|
|||
import '../../blocs/contact_bloc.dart';
|
||||
import "contact_item.dart";
|
||||
|
||||
class ContactList extends StatelessWidget {
|
||||
class ContactList extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _ContactListState();
|
||||
}
|
||||
}
|
||||
|
||||
class _ContactListState extends State<ContactList> {
|
||||
final bloc = ContactBloc();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
bloc.fetchAllContacts();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
bloc.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bloc.fetchAllContacts();
|
||||
return StreamBuilder(
|
||||
stream: bloc.allContacts,
|
||||
builder: (context, AsyncSnapshot<List<User>> snapshot) {
|
||||
|
|
|
@ -11,12 +11,14 @@ dependencies:
|
|||
json_annotation: ^2.0.0
|
||||
rxdart: ^0.20.0
|
||||
http: ^0.12.0+1
|
||||
eventsource: ^0.2.1
|
||||
flutter_webrtc: ^00.1.0
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_launcher_icons: "^0.7.0"
|
||||
flutter_launcher_icons: ^0.7.0
|
||||
build_runner: ^1.0.0
|
||||
json_serializable: ^2.0.0
|
||||
|
||||
|
|
Loading…
Reference in New Issue