Commit 1af6d307 authored by Inomjon's avatar Inomjon

Call cannection to app

parent aa970a55
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
......@@ -12,6 +6,11 @@ if (localPropertiesFile.exists()) {
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
......@@ -22,33 +21,28 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
android {
namespace "uz.technounitgroup.vmeeting"
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
kotlinOptions {
jvmTarget = '1.8'
}
android {
compileSdkVersion 33
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "uz.technounitgroup.vmeeting"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
minSdkVersion 21
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
......@@ -56,12 +50,29 @@ android {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
flutter {
source '../..'
}
dependencies {}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
apply plugin: 'com.google.gms.google-services'
{
"project_info": {
"project_number": "130671191292",
"project_id": "connectycubevideocall",
"storage_bucket": "connectycubevideocall.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:130671191292:android:52bb65faf37241024ba379",
"android_client_info": {
"package_name": "uz.technounitgroup.vmeeting"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyD59k2Vh3nVTxnuRLMZW-AUHPmjN9Yr1uA"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
\ No newline at end of file
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="uz.technounitgroup.vmeeting">
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<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" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:label="vmeeting"
android:name="${applicationName}"
......
......@@ -6,8 +6,9 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
classpath 'com.android.tools.build:gradle:7.3.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.15'
}
}
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>AIzaSyCF92QjQTaDqzw4iVhvSmNEEc9aJSiRa-0</string>
<key>GCM_SENDER_ID</key>
<string>130671191292</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>uz.technounitgroup.vmeeting</string>
<key>PROJECT_ID</key>
<string>connectycubevideocall</string>
<key>STORAGE_BUCKET</key>
<string>connectycubevideocall.appspot.com</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:130671191292:ios:03706795dbbaa44b4ba379</string>
</dict>
</plist>
\ No newline at end of file
{
"file_generated_by": "FlutterFire CLI",
"purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory",
"GOOGLE_APP_ID": "1:130671191292:ios:03706795dbbaa44b4ba379",
"FIREBASE_PROJECT_ID": "connectycubevideocall",
"GCM_SENDER_ID": "130671191292"
}
\ No newline at end of file
// File generated by FlutterFire CLI.
// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
return macos;
case TargetPlatform.windows:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for windows - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions web = FirebaseOptions(
apiKey: 'AIzaSyAzcw2Ts_AoBaqWukPKQxPriza3Dexhcbg',
appId: '1:130671191292:web:4dedaaa6a1c367744ba379',
messagingSenderId: '130671191292',
projectId: 'connectycubevideocall',
authDomain: 'connectycubevideocall.firebaseapp.com',
storageBucket: 'connectycubevideocall.appspot.com',
measurementId: 'G-FHPHE2G7VV',
);
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyD59k2Vh3nVTxnuRLMZW-AUHPmjN9Yr1uA',
appId: '1:130671191292:android:52bb65faf37241024ba379',
messagingSenderId: '130671191292',
projectId: 'connectycubevideocall',
storageBucket: 'connectycubevideocall.appspot.com',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyCF92QjQTaDqzw4iVhvSmNEEc9aJSiRa-0',
appId: '1:130671191292:ios:03706795dbbaa44b4ba379',
messagingSenderId: '130671191292',
projectId: 'connectycubevideocall',
storageBucket: 'connectycubevideocall.appspot.com',
iosBundleId: 'uz.technounitgroup.vmeeting',
);
static const FirebaseOptions macos = FirebaseOptions(
apiKey: 'AIzaSyCF92QjQTaDqzw4iVhvSmNEEc9aJSiRa-0',
appId: '1:130671191292:ios:608d88189b2e4fd64ba379',
messagingSenderId: '130671191292',
projectId: 'connectycubevideocall',
storageBucket: 'connectycubevideocall.appspot.com',
iosBundleId: 'uz.technounitgroup.vmeeting.RunnerTests',
);
}
import 'package:connectycube_flutter_call_kit/connectycube_flutter_call_kit.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'package:flutter/services.dart';
import 'package:vmeeting/src/login_screen.dart';
import 'package:vmeeting/src/utils/pref_util.dart';
import 'firebase_options.dart';
import 'src/utils/configs.dart' as config;
void main() {
runApp(const MyApp());
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
));
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
runApp(App());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
class App extends StatefulWidget {
const App({super.key});
// This widget is the root of your application.
@override
State<StatefulWidget> createState() {
return _AppState();
}
}
class _AppState extends State<App> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// TRY THIS: Try running your application with "flutter run". You'll see
// the application has a blue toolbar. Then, without quitting the app,
// try changing the seedColor in the colorScheme below to Colors.green
// and then invoke "hot reload" (save your changes or press the "hot
// reload" button in a Flutter-supported IDE, or press "r" if you used
// the command line to start the app).
//
// Notice that the counter didn't reset back to zero; the application
// state is not lost during the reload. To reset the state, use hot
// restart instead.
//
// This works for code too, not just values: Most code changes can be
// tested with just a hot reload.
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
primarySwatch: Colors.green,
),
home: Builder(
builder: (context) {
return LoginScreen();
},
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
@override
void initState() {
super.initState();
final String title;
ConnectycubeFlutterCallKit.instance.init();
@override
State<MyHomePage> createState() => _MyHomePageState();
initConnectycube();
}
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
initConnectycube() {
init(
config.APP_ID,
config.AUTH_KEY,
config.AUTH_SECRET,
onSessionRestore: () {
return SharedPrefs.getUser().then((savedUser) {
return createSession(savedUser);
});
},
);
}
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
initConnectycubeContextLess() {
CubeSettings.instance.applicationId = config.APP_ID;
CubeSettings.instance.authorizationKey = config.AUTH_KEY;
CubeSettings.instance.authorizationSecret = config.AUTH_SECRET;
CubeSettings.instance.onSessionRestore = () {
return SharedPrefs.getUser().then((savedUser) {
return createSession(savedUser);
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// TRY THIS: Try changing the color here to a specific color (to
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
// change color while the other colors stay the same.
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
//
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
// action in the IDE, or press "p" in the console), to see the
// wireframe for each widget.
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
};
}
This diff is collapsed.
import 'package:flutter/material.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'managers/call_manager.dart';
class IncomingCallScreen extends StatelessWidget {
static const String TAG = "IncomingCallScreen";
final P2PSession _callSession;
IncomingCallScreen(this._callSession);
@override
Widget build(BuildContext context) {
_callSession.onSessionClosed = (callSession) {
log("_onSessionClosed", TAG);
Navigator.pop(context);
};
return WillPopScope(
onWillPop: () => _onBackPressed(context),
child: Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: EdgeInsets.all(36),
child: Text(_getCallTitle(), style: TextStyle(fontSize: 28)),
),
Padding(
padding: EdgeInsets.only(top: 36, bottom: 8),
child: Text("Members:", style: TextStyle(fontSize: 20)),
),
Padding(
padding: EdgeInsets.only(bottom: 86),
child: Text(_callSession.opponentsIds.join(", "),
style: TextStyle(fontSize: 18)),
),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 36),
child: FloatingActionButton(
heroTag: "RejectCall",
child: Icon(
Icons.call_end,
color: Colors.white,
),
backgroundColor: Colors.red,
onPressed: () => _rejectCall(context, _callSession),
),
),
Padding(
padding: EdgeInsets.only(left: 36),
child: FloatingActionButton(
heroTag: "AcceptCall",
child: Icon(
Icons.call,
color: Colors.white,
),
backgroundColor: Colors.green,
onPressed: () => _acceptCall(context, _callSession),
),
),
],
),
],
),
)));
}
_getCallTitle() {
var callType;
switch (_callSession.callType) {
case CallType.VIDEO_CALL:
callType = "Video";
break;
case CallType.AUDIO_CALL:
callType = "Audio";
break;
}
return "Incoming $callType call";
}
void _acceptCall(BuildContext context, P2PSession callSession) {
CallManager.instance.acceptCall(callSession.sessionId, false);
}
void _rejectCall(BuildContext context, P2PSession callSession) {
CallManager.instance.reject(callSession.sessionId, false);
}
Future<bool> _onBackPressed(BuildContext context) {
return Future.value(false);
}
}
import 'package:flutter/material.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'select_opponents_screen.dart';
import 'utils/configs.dart' as utils;
import 'utils/pref_util.dart';
class LoginScreen extends StatelessWidget {
static const String TAG = "LoginScreen";
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:
AppBar(automaticallyImplyLeading: false, title: Text('P2P calls')),
body: BodyLayout(),
);
}
}
class BodyLayout extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return BodyState();
}
}
class BodyState extends State<BodyLayout> {
static const String TAG = "LoginScreen.BodyState";
bool _isLoginContinues = false;
int? _selectedUserId;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(48),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Select user to login:",
style: TextStyle(
fontSize: 22,
),
),
Expanded(
child: _getUsersList(context),
),
],
),
);
}
@override
void initState() {
super.initState();
SharedPrefs.getUser().then((loggedUser) {
if (loggedUser != null) {
_loginToCC(context, loggedUser);
}
});
}
Widget _getUsersList(BuildContext context) {
final users = utils.users;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return Card(
color: _isLoginContinues ? Colors.white70 : Colors.white,
child: ListTile(
title: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
users[index].fullName!,
style: TextStyle(
color: _isLoginContinues
? Colors.black26
: Colors.black87),
),
Container(
margin: EdgeInsets.only(left: 8),
height: 18,
width: 18,
child: Visibility(
visible: _isLoginContinues &&
users[index].id == _selectedUserId,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
),
],
),
),
onTap: () => _loginToCC(
context,
users[index],
),
),
);
},
);
}
_loginToCC(BuildContext context, CubeUser user) {
if (_isLoginContinues) return;
setState(() {
_isLoginContinues = true;
_selectedUserId = user.id;
});
if (CubeSessionManager.instance.isActiveSessionValid() &&
CubeSessionManager.instance.activeSession!.user != null) {
if (CubeChatConnection.instance.isAuthenticated()) {
setState(() {
_isLoginContinues = false;
_selectedUserId = 0;
});
_goSelectOpponentsScreen(context, user);
} else {
_loginToCubeChat(context, user);
}
} else {
createSession(user).then((cubeSession) {
_loginToCubeChat(context, user);
}).catchError((exception) {
_processLoginError(exception);
});
}
}
void _loginToCubeChat(BuildContext context, CubeUser user) {
CubeChatConnection.instance.login(user).then((cubeUser) {
SharedPrefs.saveNewUser(user);
setState(() {
_isLoginContinues = false;
_selectedUserId = 0;
});
_goSelectOpponentsScreen(context, cubeUser);
}).catchError((exception) {
_processLoginError(exception);
});
}
void _processLoginError(exception) {
log("Login error $exception", TAG);
setState(() {
_isLoginContinues = false;
_selectedUserId = 0;
});
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Login Error"),
content: Text("Something went wrong during login to ConnectyCube"),
actions: <Widget>[
TextButton(
child: Text("OK"),
onPressed: () => Navigator.of(context).pop(),
)
],
);
});
}
void _goSelectOpponentsScreen(BuildContext context, CubeUser cubeUser) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => SelectOpponentsScreen(cubeUser),
),
);
}
}
import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'package:universal_io/io.dart';
import 'package:connectycube_flutter_call_kit/connectycube_flutter_call_kit.dart';
class CallKitManager {
static CallKitManager get instance => _getInstance();
static CallKitManager? _instance;
static String TAG = "CallKitManager";
static CallKitManager _getInstance() {
return _instance ??= CallKitManager._internal();
}
factory CallKitManager() => _getInstance();
CallKitManager._internal();
late Function(String uuid) onCallAccepted;
late Function(String uuid) onCallEnded;
late Function(bool mute, String uuid) onMuteCall;
init({
required onCallAccepted(uuid),
required onCallEnded(uuid),
required onMuteCall(mute, uuid),
}) {
this.onCallAccepted = onCallAccepted;
this.onCallEnded = onCallEnded;
this.onMuteCall = onMuteCall;
ConnectycubeFlutterCallKit.instance.init(
onCallAccepted: _onCallAccepted,
onCallRejected: _onCallRejected,
icon: Platform.isAndroid ? 'default_avatar' : 'CallkitIcon',
notificationIcon: 'ic_notification',
color: '#07711e',
ringtone:
Platform.isAndroid ? 'custom_ringtone' : 'custom_ringtone.caf');
if (Platform.isIOS) {
ConnectycubeFlutterCallKit.onCallMuted = _onCallMuted;
}
}
Future<void> processCallFinished(String uuid) async {
if(Platform.isAndroid || Platform.isIOS) {
ConnectycubeFlutterCallKit.reportCallEnded(sessionId: uuid);
ConnectycubeFlutterCallKit.setOnLockScreenVisibility(isVisible: false);
}
}
/// Event Listener Callbacks for 'connectycube_flutter_call_kit'
///
Future<void> _onCallMuted(bool mute, String uuid) async {
onMuteCall.call(mute, uuid);
}
void muteCall(String sessionId, bool mute) {
ConnectycubeFlutterCallKit.reportCallMuted(sessionId: sessionId, muted: mute);
}
Future<void> _onCallAccepted(CallEvent callEvent) async {
onCallAccepted.call(callEvent.sessionId);
}
Future<void> _onCallRejected(CallEvent callEvent) async {
if (!CubeChatConnection.instance.isAuthenticated()) {
rejectCall(
callEvent.sessionId, {...callEvent.opponentsIds, callEvent.callerId});
}
onCallEnded.call(callEvent.sessionId);
}
}
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:universal_io/io.dart';
import 'package:connectycube_flutter_call_kit/connectycube_flutter_call_kit.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'call_kit_manager.dart';
import '../conversation_screen.dart';
import '../incoming_call_screen.dart';
import '../utils/configs.dart';
import '../utils/consts.dart';
class CallManager {
static String TAG = "CallManager";
static CallManager get instance => _getInstance();
static CallManager? _instance;
static CallManager _getInstance() {
return _instance ??= CallManager._internal();
}
factory CallManager() => _getInstance();
CallManager._internal();
P2PClient? _callClient;
P2PSession? _currentCall;
BuildContext? context;
MediaStream? localMediaStream;
Map<int, MediaStream> remoteStreams = {};
Function(bool, String)? onMicMuted;
init(BuildContext context) {
this.context = context;
_initCustomMediaConfigs();
if (CubeChatConnection.instance.isAuthenticated()) {
_initCalls();
} else {
_initChatConnectionStateListener();
}
_initCallKit();
}
destroy() {
_callClient?.destroy();
_callClient = null;
}
void _initCustomMediaConfigs() {
RTCMediaConfig mediaConfig = RTCMediaConfig.instance;
mediaConfig.minHeight = 340;
mediaConfig.minWidth = 480;
mediaConfig.minFrameRate = 25;
RTCConfig.instance.statsReportsInterval = 200;
}
void _initCalls() {
if (_callClient == null) {
_callClient = P2PClient.instance;
_callClient!.init();
}
_callClient!.onReceiveNewSession = (callSession) async {
if (_currentCall != null &&
_currentCall!.sessionId != callSession.sessionId) {
callSession.reject();
return;
}
_currentCall = callSession;
var callState = await _getCallState(_currentCall!.sessionId);
if (callState == CallState.REJECTED) {
reject(_currentCall!.sessionId, false);
} else if (callState == CallState.ACCEPTED) {
acceptCall(_currentCall!.sessionId, false);
} else if (callState == CallState.UNKNOWN ||
callState == CallState.PENDING) {
if (callState == CallState.UNKNOWN &&
(Platform.isIOS || Platform.isAndroid)) {
ConnectycubeFlutterCallKit.setCallState(
sessionId: _currentCall!.sessionId, callState: CallState.PENDING);
}
_showIncomingCallScreen(_currentCall!);
}
_currentCall?.onLocalStreamReceived = (localStream) {
localMediaStream = localStream;
};
_currentCall?.onRemoteStreamReceived = (session, userId, stream) {
remoteStreams[userId] = stream;
};
_currentCall?.onRemoteStreamRemoved = (session, userId, stream) {
remoteStreams.remove(userId);
};
};
_callClient!.onSessionClosed = (callSession) async {
if (_currentCall != null &&
_currentCall!.sessionId == callSession.sessionId) {
_currentCall = null;
localMediaStream?.getTracks().forEach((track) async {
await track.stop();
});
await localMediaStream?.dispose();
localMediaStream = null;
remoteStreams.forEach((key, value) async {
await value.dispose();
});
remoteStreams.clear();
CallKitManager.instance.processCallFinished(callSession.sessionId);
}
};
}
void startNewCall(
BuildContext context, int callType, Set<int> opponents) async {
if (opponents.isEmpty) return;
Helper.setAppleAudioIOMode(AppleAudioIOMode.localAndRemote);
P2PSession callSession =
_callClient!.createCallSession(callType, opponents);
_currentCall = callSession;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConversationCallScreen(callSession, false),
),
);
_sendStartCallSignalForOffliners(_currentCall!);
}
void _showIncomingCallScreen(P2PSession callSession) {
if (context != null) {
Navigator.push(
context!,
MaterialPageRoute(
builder: (context) => IncomingCallScreen(callSession),
),
);
}
}
void acceptCall(String sessionId, bool fromCallkit) {
log('acceptCall, from callKit: $fromCallkit', TAG);
ConnectycubeFlutterCallKit.setOnLockScreenVisibility(isVisible: true);
if (_currentCall != null) {
if (context != null) {
if (AppLifecycleState.resumed !=
WidgetsBinding.instance.lifecycleState) {
_currentCall?.acceptCall();
}
if (!fromCallkit) {
ConnectycubeFlutterCallKit.reportCallAccepted(sessionId: sessionId);
}
Navigator.pushReplacement(
context!,
MaterialPageRoute(
builder: (context) => ConversationCallScreen(_currentCall!, true),
),
);
}
Helper.setAppleAudioIOMode(AppleAudioIOMode.localAndRemote);
}
}
void reject(String sessionId, bool fromCallkit) {
if (_currentCall != null) {
if (fromCallkit) {
ConnectycubeFlutterCallKit.setOnLockScreenVisibility(isVisible: false);
} else {
CallKitManager.instance.processCallFinished(_currentCall!.sessionId);
}
_currentCall!.reject();
_sendEndCallSignalForOffliners(_currentCall);
}
}
void hungUp() {
if (_currentCall != null) {
CallKitManager.instance.processCallFinished(_currentCall!.sessionId);
_currentCall!.hungUp();
_sendEndCallSignalForOffliners(_currentCall);
}
}
CreateEventParams _getCallEventParameters(P2PSession currentCall) {
String? callerName = users
.where((cubeUser) => cubeUser.id == currentCall.callerId)
.first
.fullName;
CreateEventParams params = CreateEventParams();
params.parameters = {
'message':
"Incoming ${currentCall.callType == CallType.VIDEO_CALL ? "Video" : "Audio"} call",
PARAM_CALL_TYPE: currentCall.callType,
PARAM_SESSION_ID: currentCall.sessionId,
PARAM_CALLER_ID: currentCall.callerId,
PARAM_CALLER_NAME: callerName,
PARAM_CALL_OPPONENTS: currentCall.opponentsIds.join(','),
};
params.notificationType = NotificationType.PUSH;
params.environment =
kReleaseMode ? CubeEnvironment.PRODUCTION : CubeEnvironment.DEVELOPMENT;
params.usersIds = currentCall.opponentsIds.toList();
return params;
}
void _sendStartCallSignalForOffliners(P2PSession currentCall) {
CreateEventParams params = _getCallEventParameters(currentCall);
params.parameters[PARAM_SIGNAL_TYPE] = SIGNAL_TYPE_START_CALL;
params.parameters[PARAM_IOS_VOIP] = 1;
params.parameters[PARAM_EXPIRATION] = 0;
params.parameters['ios_push_type'] = 'voip';
createEvent(params.getEventForRequest()).then((cubeEvent) {
log("Event for offliners created: $cubeEvent");
}).catchError((error) {
log("ERROR occurs during create event");
});
}
void _sendEndCallSignalForOffliners(P2PSession? currentCall) {
if (currentCall == null) return;
CubeUser? currentUser = CubeChatConnection.instance.currentUser;
if (currentUser == null || currentUser.id != currentCall.callerId) return;
CreateEventParams params = _getCallEventParameters(currentCall);
params.parameters[PARAM_SIGNAL_TYPE] = SIGNAL_TYPE_END_CALL;
createEvent(params.getEventForRequest()).then((cubeEvent) {
log("Event for offliners created");
}).catchError((error) {
log("ERROR occurs during create event");
});
}
void _initCallKit() {
CallKitManager.instance.init(
onCallAccepted: (uuid) {
acceptCall(uuid, true);
},
onCallEnded: (uuid) {
reject(uuid, true);
},
onMuteCall: (mute, uuid) {
onMicMuted?.call(mute, uuid);
},
);
}
void _initChatConnectionStateListener() {
CubeChatConnection.instance.connectionStateStream.listen((state) {
if (CubeChatConnectionState.Ready == state) {
_initCalls();
}
});
}
Future<String> _getCallState(String sessionId) async {
if (Platform.isAndroid || Platform.isIOS) {
var callState =
ConnectycubeFlutterCallKit.getCallState(sessionId: sessionId);
return callState;
}
return Future.value(CallState.UNKNOWN);
}
void muteCall(String sessionId, bool mute) {
CallKitManager.instance.muteCall(sessionId, mute);
}
}
import 'dart:convert';
import 'package:connectycube_flutter_call_kit/connectycube_flutter_call_kit.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:universal_io/io.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import '../../../main.dart';
import '../utils/consts.dart';
import '../utils/pref_util.dart';
class PushNotificationsManager {
static const TAG = "PushNotificationsManager";
static PushNotificationsManager? _instance;
PushNotificationsManager._internal();
static PushNotificationsManager _getInstance() {
return _instance ??= PushNotificationsManager._internal();
}
factory PushNotificationsManager() => _getInstance();
BuildContext? applicationContext;
static PushNotificationsManager get instance => _getInstance();
init() async {
ConnectycubeFlutterCallKit.initEventsHandler();
ConnectycubeFlutterCallKit.onTokenRefreshed = (token) {
log('[onTokenRefresh] VoIP token: $token', TAG);
subscribe(token);
};
ConnectycubeFlutterCallKit.getToken().then((token) {
log('[getToken] VoIP token: $token', TAG);
if (token != null) {
subscribe(token);
}
});
ConnectycubeFlutterCallKit.onCallRejectedWhenTerminated =
onCallRejectedWhenTerminated;
}
subscribe(String token) async {
log('[subscribe] token: $token', PushNotificationsManager.TAG);
var savedToken = await SharedPrefs.getSubscriptionToken();
if (token == savedToken) {
log('[subscribe] skip subscription for same token',
PushNotificationsManager.TAG);
return;
}
CreateSubscriptionParameters parameters = CreateSubscriptionParameters();
parameters.pushToken = token;
parameters.environment =
kReleaseMode ? CubeEnvironment.PRODUCTION : CubeEnvironment.DEVELOPMENT;
if (Platform.isAndroid) {
parameters.channel = NotificationsChannels.GCM;
parameters.platform = CubePlatform.ANDROID;
} else if (Platform.isIOS) {
parameters.channel = NotificationsChannels.APNS_VOIP;
parameters.platform = CubePlatform.IOS;
}
var deviceInfoPlugin = DeviceInfoPlugin();
var deviceId;
if (kIsWeb) {
var webBrowserInfo = await deviceInfoPlugin.webBrowserInfo;
deviceId = base64Encode(utf8.encode(webBrowserInfo.userAgent ?? ''));
} else if (Platform.isAndroid) {
var androidInfo = await deviceInfoPlugin.androidInfo;
deviceId = androidInfo.id;
} else if (Platform.isIOS) {
var iosInfo = await deviceInfoPlugin.iosInfo;
deviceId = iosInfo.identifierForVendor;
} else if (Platform.isMacOS) {
var macOsInfo = await deviceInfoPlugin.macOsInfo;
deviceId = macOsInfo.computerName;
}
parameters.udid = deviceId;
var packageInfo = await PackageInfo.fromPlatform();
parameters.bundleIdentifier = packageInfo.packageName;
createSubscription(parameters.getRequestParameters())
.then((cubeSubscriptions) {
log('[subscribe] subscription SUCCESS', PushNotificationsManager.TAG);
SharedPrefs.saveSubscriptionToken(token);
cubeSubscriptions.forEach((subscription) {
if (subscription.device!.clientIdentificationSequence == token) {
SharedPrefs.saveSubscriptionId(subscription.id!);
}
});
}).catchError((error) {
log('[subscribe] subscription ERROR: $error',
PushNotificationsManager.TAG);
});
}
Future<void> unsubscribe() {
return SharedPrefs.getSubscriptionId().then((subscriptionId) async {
if (subscriptionId != 0) {
return deleteSubscription(subscriptionId).then((voidResult) {
SharedPrefs.saveSubscriptionId(0);
});
} else {
return Future.value();
}
}).catchError((onError) {
log('[unsubscribe] ERROR: $onError', PushNotificationsManager.TAG);
});
}
}
@pragma('vm:entry-point')
Future<void> onCallRejectedWhenTerminated(CallEvent callEvent) async {
print(
'[PushNotificationsManager][onCallRejectedWhenTerminated] callEvent: $callEvent');
var currentUser = await SharedPrefs.getUser();
initConnectycubeContextLess();
var sendOfflineReject = rejectCall(callEvent.sessionId, {
...callEvent.opponentsIds.where((userId) => currentUser!.id != userId),
callEvent.callerId
});
var sendPushAboutReject = sendPushAboutRejectFromKilledState({
PARAM_CALL_TYPE: callEvent.callType,
PARAM_SESSION_ID: callEvent.sessionId,
PARAM_CALLER_ID: callEvent.callerId,
PARAM_CALLER_NAME: callEvent.callerName,
PARAM_CALL_OPPONENTS: callEvent.opponentsIds.join(','),
}, callEvent.callerId);
return Future.wait([sendOfflineReject, sendPushAboutReject]).then((result) {
return Future.value();
});
}
Future<void> sendPushAboutRejectFromKilledState(
Map<String, dynamic> parameters,
int callerId,
) {
CreateEventParams params = CreateEventParams();
params.parameters = parameters;
params.parameters['message'] = "Reject call";
params.parameters[PARAM_SIGNAL_TYPE] = SIGNAL_TYPE_REJECT_CALL;
// params.parameters[PARAM_IOS_VOIP] = 1;
params.notificationType = NotificationType.PUSH;
params.environment =
kReleaseMode ? CubeEnvironment.PRODUCTION : CubeEnvironment.DEVELOPMENT;
params.usersIds = [callerId];
return createEvent(params.getEventForRequest());
}
import 'package:flutter/material.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'login_screen.dart';
import 'managers/call_manager.dart';
import 'managers/push_notifications_manager.dart';
import 'utils/configs.dart' as utils;
import 'utils/platform_utils.dart';
import 'utils/pref_util.dart';
class SelectOpponentsScreen extends StatelessWidget {
final CubeUser currentUser;
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _onBackPressed(),
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text(
'Logged in as ${CubeChatConnection.instance.currentUser!.fullName}',
),
actions: <Widget>[
IconButton(
onPressed: () => _logOut(context),
icon: Icon(
Icons.exit_to_app,
color: Colors.white,
),
),
],
),
body: BodyLayout(currentUser),
),
);
}
Future<bool> _onBackPressed() {
return Future.value(false);
}
_logOut(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Logout"),
content: Text("Are you sure you want logout current user"),
actions: <Widget>[
TextButton(
child: Text("CANCEL"),
onPressed: () {
Navigator.pop(context);
},
),
TextButton(
child: Text("OK"),
onPressed: () async {
CallManager.instance.destroy();
CubeChatConnection.instance.destroy();
await PushNotificationsManager.instance.unsubscribe();
await SharedPrefs.deleteUserData();
await signOut();
Navigator.pop(context); // cancel current Dialog
_navigateToLoginScreen(context);
},
),
],
);
},
);
}
_navigateToLoginScreen(BuildContext context) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => LoginScreen(),
),
(r) => false,
);
}
SelectOpponentsScreen(this.currentUser);
}
class BodyLayout extends StatefulWidget {
final CubeUser currentUser;
@override
State<StatefulWidget> createState() {
return _BodyLayoutState(currentUser);
}
BodyLayout(this.currentUser);
}
class _BodyLayoutState extends State<BodyLayout> {
final CubeUser currentUser;
late Set<int> _selectedUsers;
_BodyLayoutState(this.currentUser);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 48, left: 48, right: 48, bottom: 12),
child: Column(
children: [
Text(
"Select users to call:",
style: TextStyle(fontSize: 22),
),
Expanded(
child: _getOpponentsList(context),
),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: EdgeInsets.all(12),
child: FloatingActionButton(
heroTag: "VideoCall",
child: Icon(
Icons.videocam,
color: Colors.white,
),
backgroundColor: Colors.blue,
onPressed: () => CallManager.instance.startNewCall(
context, CallType.VIDEO_CALL, _selectedUsers),
),
),
Padding(
padding: EdgeInsets.all(12),
child: FloatingActionButton(
heroTag: "AudioCall",
child: Icon(
Icons.call,
color: Colors.white,
),
backgroundColor: Colors.green,
onPressed: () => CallManager.instance.startNewCall(
context, CallType.AUDIO_CALL, _selectedUsers),
),
),
],
),
],
));
}
Widget _getOpponentsList(BuildContext context) {
CubeUser? currentUser = CubeChatConnection.instance.currentUser;
final users =
utils.users.where((user) => user.id != currentUser!.id).toList();
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return Card(
child: CheckboxListTile(
title: Center(
child: Text(
users[index].fullName!,
),
),
value: _selectedUsers.contains(users[index].id),
onChanged: ((checked) {
setState(() {
if (checked!) {
_selectedUsers.add(users[index].id!);
} else {
_selectedUsers.remove(users[index].id);
}
});
}),
),
);
},
);
}
@override
void initState() {
super.initState();
initForegroundService();
_selectedUsers = {};
checkSystemAlertWindowPermission(context);
requestNotificationsPermission();
CallManager.instance.init(context);
PushNotificationsManager.instance.init();
}
}
import 'package:connectycube_sdk/connectycube_sdk.dart';
const String APP_ID = "7236";
const String AUTH_KEY = "JFPtjOKO5Y979D7";
const String AUTH_SECRET = "zvsfjsUTngTwbqB";
const String DEFAULT_PASS = "Flutter2000";
List<CubeUser> users = [
CubeUser(
id: 11253684,
login: "Flutter2000",
fullName: "Inomjon",
password: DEFAULT_PASS,
),
];
final String PARAM_SESSION_ID = 'session_id';
final String PARAM_CALL_TYPE = 'call_type';
final String PARAM_CALLER_ID = 'caller_id';
final String PARAM_CALLER_NAME = 'caller_name';
final String PARAM_CALL_OPPONENTS = 'call_opponents';
final String PARAM_IOS_VOIP = 'ios_voip';
final String PARAM_SIGNAL_TYPE = 'signal_type';
final String PARAM_EXPIRATION = 'expiration';
final String SIGNAL_TYPE_START_CALL = "startCall";
final String SIGNAL_TYPE_END_CALL = "endCall";
final String SIGNAL_TYPE_REJECT_CALL = "rejectCall";
import 'package:connectycube_sdk/connectycube_core.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_background/flutter_background.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:universal_io/io.dart';
Future<bool> initForegroundService() async {
if (Platform.isAndroid) {
final androidConfig = FlutterBackgroundAndroidConfig(
notificationTitle: 'P2P Calls sample',
notificationText: 'Screen sharing is in progress',
notificationImportance: AndroidNotificationImportance.Default,
notificationIcon:
AndroidResource(name: 'ic_launcher_foreground', defType: 'drawable'),
);
return FlutterBackground.initialize(androidConfig: androidConfig);
} else {
return Future.value(true);
}
}
Future<bool> startBackgroundExecution() async {
if (Platform.isAndroid) {
return initForegroundService().then((_) {
return FlutterBackground.enableBackgroundExecution();
});
} else {
return Future.value(true);
}
}
Future<bool> stopBackgroundExecution() async {
if (Platform.isAndroid && FlutterBackground.isBackgroundExecutionEnabled) {
return FlutterBackground.disableBackgroundExecution();
} else {
return Future.value(true);
}
}
Future<bool> hasBackgroundExecutionPermissions() async {
if (Platform.isAndroid) {
return FlutterBackground.hasPermissions;
} else {
return Future.value(true);
}
}
Future<void> checkSystemAlertWindowPermission(BuildContext context) async {
if (Platform.isAndroid) {
var androidInfo = await DeviceInfoPlugin().androidInfo;
var sdkInt = androidInfo.version.sdkInt!;
if (sdkInt >= 31) {
if (await Permission.systemAlertWindow.isDenied) {
showDialog(
context: context,
builder: (BuildContext context) {
return Expanded(
child: AlertDialog(
title: Text('Permission required'),
content: Text(
'For accepting the calls in the background you should provide access to show System Alerts from the background. Would you like to do it now?'),
actions: [
TextButton(
onPressed: () {
Permission.systemAlertWindow.request().then((status) {
if (status.isGranted) {
Navigator.of(context).pop();
}
});
},
child: Text(
'Allow',
),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
'Later',
),
),
],
),
);
},
);
}
}
}
}
requestNotificationsPermission() async {
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS || Platform.isWindows)) {
var isPermissionGranted = await Permission.notification.isGranted;
log('isPermissionGranted = $isPermissionGranted', 'platform_utils');
if (!isPermissionGranted) {
await Permission.notification.request();
}
}
}
import 'dart:async';
import 'package:connectycube_sdk/connectycube_chat.dart';
import 'package:shared_preferences/shared_preferences.dart';
const String prefUserLogin = "pref_user_login";
const String prefUserPsw = "pref_user_psw";
const String prefUserName = "pref_user_name";
const String prefUserId = "pref_user_id";
const String prefUserAvatar = "pref_user_avatar";
const String prefSubscriptionToken = "pref_subscription_token";
const String prefSubscriptionId = "pref_subscription_id";
class SharedPrefs {
static SharedPreferences? _prefs;
static Future<SharedPreferences> getPrefs() async {
Completer<SharedPreferences> completer = Completer();
if (_prefs != null) {
completer.complete(_prefs);
} else {
_prefs = await SharedPreferences.getInstance();
completer.complete(_prefs);
}
return completer.future;
}
static Future<bool> saveNewUser(CubeUser cubeUser) {
return getPrefs().then((prefs) {
prefs.clear();
prefs.setString(prefUserLogin, cubeUser.login!);
prefs.setString(prefUserPsw, cubeUser.password!);
prefs.setString(prefUserName, cubeUser.fullName!);
prefs.setInt(prefUserId, cubeUser.id!);
if (cubeUser.avatar != null)
prefs.setString(prefUserAvatar, cubeUser.avatar!);
return Future.value(true);
});
}
static Future<bool> updateUser(CubeUser cubeUser) {
return getPrefs().then((prefs) {
if (cubeUser.password != null)
prefs.setString(prefUserPsw, cubeUser.password!);
if (cubeUser.login != null)
prefs.setString(prefUserLogin, cubeUser.login!);
if (cubeUser.fullName != null)
prefs.setString(prefUserName, cubeUser.fullName!);
if (cubeUser.avatar != null)
prefs.setString(prefUserAvatar, cubeUser.avatar!);
return Future.value(true);
});
}
static Future<CubeUser?> getUser() {
return getPrefs().then((prefs) {
if (prefs.getString(prefUserLogin) == null) return Future.value();
var user = CubeUser();
user.login = prefs.getString(prefUserLogin);
user.password = prefs.getString(prefUserPsw);
user.fullName = prefs.getString(prefUserName);
user.id = prefs.getInt(prefUserId);
user.avatar = prefs.getString(prefUserAvatar);
return Future.value(user);
});
}
static Future<bool> deleteUserData() {
return getPrefs().then((prefs) {
return prefs.clear();
});
}
static Future<bool> saveSubscriptionToken(String token) {
return getPrefs().then((prefs) {
return prefs.setString(prefSubscriptionToken, token);
});
}
static Future<String> getSubscriptionToken() {
return getPrefs().then((prefs) {
return Future.value(prefs.getString(prefSubscriptionToken) ?? "");
});
}
static Future<bool> saveSubscriptionId(int id) {
return getPrefs().then((prefs) {
return prefs.setInt(prefSubscriptionId, id);
});
}
static Future<int> getSubscriptionId() {
return getPrefs().then((prefs) {
return Future.value(prefs.getInt(prefSubscriptionId) ?? 0);
});
}
}
......@@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h"
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_webrtc_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterWebRTCPlugin");
flutter_web_r_t_c_plugin_register_with_registrar(flutter_webrtc_registrar);
}
......@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
flutter_webrtc
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
......
......@@ -5,6 +5,18 @@
import FlutterMacOS
import Foundation
import device_info_plus
import firebase_core
import flutter_webrtc
import package_info_plus
import path_provider_foundation
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>AIzaSyCF92QjQTaDqzw4iVhvSmNEEc9aJSiRa-0</string>
<key>GCM_SENDER_ID</key>
<string>130671191292</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>uz.technounitgroup.vmeeting.RunnerTests</string>
<key>PROJECT_ID</key>
<string>connectycubevideocall</string>
<key>STORAGE_BUCKET</key>
<string>connectycubevideocall.appspot.com</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:130671191292:ios:608d88189b2e4fd64ba379</string>
</dict>
</plist>
\ No newline at end of file
{
"file_generated_by": "FlutterFire CLI",
"purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory",
"GOOGLE_APP_ID": "1:130671191292:ios:608d88189b2e4fd64ba379",
"FIREBASE_PROJECT_ID": "connectycubevideocall",
"GCM_SENDER_ID": "130671191292"
}
\ No newline at end of file
This diff is collapsed.
......@@ -35,6 +35,17 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
connectycube_sdk: ^2.10.0
shared_preferences: ^2.2.2
connectycube_flutter_call_kit: ^2.3.0
universal_io: ^2.2.2
web_browser_detect: ^2.0.3
flutter_background: ^1.2.0
flutter_speed_dial: ^6.2.0
permission_handler: ^10.0.1
device_info_plus: ^9.0.0
package_info_plus: ^4.0.0
firebase_core: ^2.20.0
dev_dependencies:
flutter_test:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment