From 90c7dd944449d8395b6726e8a5227558bb9e325e Mon Sep 17 00:00:00 2001 From: YuliaGrigorieva Date: Thu, 20 Apr 2023 21:22:19 +0300 Subject: [PATCH] [audio_call] - null safety update --- .gitignore | 1 + audio_call/.gitignore | 56 +-- audio_call/.metadata | 10 - audio_call/analysis_options.yaml | 29 ++ audio_call/android/.gitignore | 13 + audio_call/android/app/build.gradle | 36 +- .../android/app/src/debug/AndroidManifest.xml | 3 +- .../android/app/src/main/AndroidManifest.xml | 29 +- .../android/app/src/main/ic_launcher-web.png | Bin 22896 -> 0 bytes .../flutter/audiocall/Application.java | 28 -- .../flutter/audiocall/MainActivity.java | 3 +- .../res/drawable-mdpi/ic_vox_notification.png | Bin 454 -> 0 bytes .../ic_notification.png} | Bin .../res/drawable-v21/launch_background.xml | 12 + .../drawable-xhdpi/ic_vox_notification.png | Bin 1025 -> 0 bytes .../drawable-xxhdpi/ic_vox_notification.png | Bin 1810 -> 0 bytes .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../app/src/main/res/values-night/styles.xml | 18 + .../res/values/ic_launcher_background.xml | 4 - .../app/src/main/res/values/styles.xml | 14 +- .../app/src/profile/AndroidManifest.xml | 3 +- audio_call/android/build.gradle | 11 +- audio_call/android/gradle.properties | 1 - .../gradle/wrapper/gradle-wrapper.properties | 5 + audio_call/android/settings.gradle | 18 +- audio_call/ios/.gitignore | 34 ++ audio_call/ios/Flutter/AppFrameworkInfo.plist | 4 +- audio_call/ios/Flutter/Debug.xcconfig | 2 +- audio_call/ios/Flutter/Release.xcconfig | 2 +- audio_call/ios/Podfile | 62 +-- audio_call/ios/Podfile.lock | 178 ++++--- .../ios/Runner.xcodeproj/project.pbxproj | 96 ++-- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 8 +- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + audio_call/ios/Runner/AppDelegate.swift | 126 +++-- .../ios/Runner/Assets.xcassets/Contents.json | 6 - .../ios/Runner/GoogleService-Info.plist | 34 -- audio_call/ios/Runner/Info.plist | 72 +-- audio_call/lib/main.dart | 27 +- .../screens/active_call/active_call_page.dart | 57 ++- .../active_call_page_arguments.dart | 4 +- .../active_call/bloc/active_call_bloc.dart | 264 ++++++----- .../active_call/bloc/active_call_event.dart | 29 +- .../active_call/bloc/active_call_state.dart | 34 +- .../screens/call_failed/call_failed_page.dart | 39 +- .../call_failed_page_arguments.dart | 4 +- .../bloc/incoming_call_bloc.dart | 50 +- .../bloc/incoming_call_event.dart | 15 +- .../incoming_call/incoming_call_page.dart | 15 +- .../incoming_call_page_arguments.dart | 2 +- .../lib/screens/login/bloc/login_bloc.dart | 64 +-- .../lib/screens/login/bloc/login_event.dart | 2 +- .../lib/screens/login/bloc/login_state.dart | 8 +- audio_call/lib/screens/login/login_page.dart | 30 +- .../lib/screens/main/bloc/main_bloc.dart | 102 +++-- .../lib/screens/main/bloc/main_event.dart | 4 +- .../lib/screens/main/bloc/main_state.dart | 14 +- audio_call/lib/screens/main/main_page.dart | 31 +- audio_call/lib/services/auth_service.dart | 95 ++-- .../lib/services/call/audio_device_event.dart | 4 +- audio_call/lib/services/call/call_event.dart | 18 +- .../lib/services/call/call_service.dart | 106 ++--- .../lib/services/call/callkit_service.dart | 169 ++++--- .../services/push/push_service_android.dart | 86 ++-- .../lib/services/push/push_service_ios.dart | 59 +-- audio_call/lib/theme/voximplant_theme.dart | 16 +- audio_call/lib/utils/navigation_helper.dart | 36 +- audio_call/lib/utils/notification_helper.dart | 36 +- audio_call/lib/utils/permissions_helper.dart | 4 +- audio_call/lib/widgets/widgets.dart | 41 +- audio_call/pubspec.lock | 433 ++++++++++++------ audio_call/pubspec.yaml | 79 +--- audio_call/test/widget_test.dart | 28 +- 77 files changed, 1562 insertions(+), 1403 deletions(-) delete mode 100644 audio_call/.metadata create mode 100644 audio_call/analysis_options.yaml create mode 100644 audio_call/android/.gitignore delete mode 100644 audio_call/android/app/src/main/ic_launcher-web.png delete mode 100644 audio_call/android/app/src/main/java/com/voximplant/flutter/audiocall/Application.java delete mode 100644 audio_call/android/app/src/main/res/drawable-mdpi/ic_vox_notification.png rename audio_call/android/app/src/main/res/{drawable-hdpi/ic_vox_notification.png => drawable-v21/ic_notification.png} (100%) create mode 100644 audio_call/android/app/src/main/res/drawable-v21/launch_background.xml delete mode 100644 audio_call/android/app/src/main/res/drawable-xhdpi/ic_vox_notification.png delete mode 100644 audio_call/android/app/src/main/res/drawable-xxhdpi/ic_vox_notification.png delete mode 100644 audio_call/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 audio_call/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 audio_call/android/app/src/main/res/values-night/styles.xml delete mode 100644 audio_call/android/app/src/main/res/values/ic_launcher_background.xml create mode 100644 audio_call/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 audio_call/ios/.gitignore create mode 100644 audio_call/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 audio_call/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 audio_call/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 audio_call/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 audio_call/ios/Runner/Assets.xcassets/Contents.json delete mode 100644 audio_call/ios/Runner/GoogleService-Info.plist diff --git a/.gitignore b/.gitignore index 48c3273..81fc631 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .idea/ *.last_build_id +**/google-services.json diff --git a/audio_call/.gitignore b/audio_call/.gitignore index 8f72d2d..55f97e6 100644 --- a/audio_call/.gitignore +++ b/audio_call/.gitignore @@ -8,6 +8,7 @@ .buildlog/ .history .svn/ +migrate_working_dir/ # IntelliJ related *.iml @@ -22,6 +23,7 @@ # Flutter/Dart/Pub related **/doc/api/ +**/ios/Flutter/.last_build_id .dart_tool/ .flutter-plugins .flutter-plugins-dependencies @@ -30,51 +32,15 @@ .pub/ /build/ -# Android related -**/android/**/gradle-wrapper.jar -**/android/.gradle -**/android/captures/ -**/android/gradlew -**/android/gradlew.bat -**/android/local.properties -**/android/**/GeneratedPluginRegistrant.java -**/android/gradle/ +# Symbolication related +app.*.symbols -# iOS/XCode related -**/ios/**/*.mode1v3 -**/ios/**/*.mode2v3 -**/ios/**/*.moved-aside -**/ios/**/*.pbxuser -**/ios/**/*.perspectivev3 -**/ios/**/*sync/ -**/ios/**/.sconsign.dblite -**/ios/**/.tags* -**/ios/**/.vagrant/ -**/ios/**/DerivedData/ -**/ios/**/Icon? -**/ios/**/Pods/ -**/ios/**/.symlinks/ -**/ios/**/profile -**/ios/**/xcuserdata -**/ios/.generated/ -**/ios/Flutter/App.framework -**/ios/Flutter/Flutter.framework -**/ios/Flutter/Generated.xcconfig -**/ios/Flutter/app.flx -**/ios/Flutter/app.zip -**/ios/Flutter/flutter_assets/ -**/ios/ServiceDefinitions.json -**/ios/Runner/GeneratedPluginRegistrant.* -**/ios/Flutter/flutter_export_environment.sh -**/ios/Runner.xcworkspace/xcshareddata/** +# Obfuscation related +app.*.map.json -**/ios/Flutter/Flutter.podspec +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release -# Exceptions to above rules. -!**/ios/**/default.mode1v3 -!**/ios/**/default.mode2v3 -!**/ios/**/default.pbxuser -!**/ios/**/default.perspectivev3 -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages - -**/android/app/google-services.json +**/android/google-services.json diff --git a/audio_call/.metadata b/audio_call/.metadata deleted file mode 100644 index e023651..0000000 --- a/audio_call/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 20e59316b8b8474554b38493b8ca888794b0234a - channel: stable - -project_type: app diff --git a/audio_call/analysis_options.yaml b/audio_call/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/audio_call/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/audio_call/android/.gitignore b/audio_call/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/audio_call/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/audio_call/android/app/build.gradle b/audio_call/android/app/build.gradle index 1640ece..cf2c694 100644 --- a/audio_call/android/app/build.gradle +++ b/audio_call/android/app/build.gradle @@ -22,50 +22,38 @@ if (flutterVersionName == null) { } apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" +apply plugin: 'com.google.gms.google-services' android { - compileSdkVersion 30 + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion - lintOptions { - disable 'InvalidPackage' + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.voximplant.flutter.audiocall" - minSdkVersion 16 - targetSdkVersion 30 + // set minSdk to 21 since multidex is enabled by default since this version + minSdkVersion 21 + targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { + // 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 } } - - packagingOptions { - exclude 'META-INF/proguard/androidx-annotations.pro' - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } } flutter { source '../..' } - -dependencies { - implementation 'com.google.firebase:firebase-messaging:20.3.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' -} - -apply plugin: 'com.google.gms.google-services' diff --git a/audio_call/android/app/src/debug/AndroidManifest.xml b/audio_call/android/app/src/debug/AndroidManifest.xml index 411da15..d675ce0 100644 --- a/audio_call/android/app/src/debug/AndroidManifest.xml +++ b/audio_call/android/app/src/debug/AndroidManifest.xml @@ -1,6 +1,7 @@ - diff --git a/audio_call/android/app/src/main/AndroidManifest.xml b/audio_call/android/app/src/main/AndroidManifest.xml index 9b87d2b..46ab9d1 100644 --- a/audio_call/android/app/src/main/AndroidManifest.xml +++ b/audio_call/android/app/src/main/AndroidManifest.xml @@ -1,26 +1,25 @@ - - - + - + + diff --git a/audio_call/android/app/src/main/ic_launcher-web.png b/audio_call/android/app/src/main/ic_launcher-web.png deleted file mode 100644 index 09bb2b392d69d6e562f27274f8132f4856b0be57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22896 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 diff --git a/audio_call/android/app/src/main/java/com/voximplant/flutter/audiocall/Application.java b/audio_call/android/app/src/main/java/com/voximplant/flutter/audiocall/Application.java deleted file mode 100644 index fe15323..0000000 --- a/audio_call/android/app/src/main/java/com/voximplant/flutter/audiocall/Application.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.voximplant.flutter.audiocall; - -import com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin; -import com.example.flutter_voip_push_notification.FlutterVoipPushNotificationPlugin; - -import io.flutter.app.FlutterApplication; -import io.flutter.plugin.common.PluginRegistry; -import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin; -import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService; -import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin; - -public class Application extends FlutterApplication implements PluginRegistry.PluginRegistrantCallback { - @Override - public void onCreate() { - super.onCreate(); - FlutterFirebaseMessagingService.setPluginRegistrant(this); - } - - @Override - public void registerWith(PluginRegistry registry) { - FlutterVoipPushNotificationPlugin.registerWith(registry.registrarFor("com.example.flutter_voip_push_notification.FlutterVoipPushNotificationPlugin")); - com.voximplant.flutter_voximplant.VoximplantPlugin.registerWith(registry.registrarFor("com.voximplant.flutter_voximplant.VoximplantPlugin")); - com.baseflow.permissionhandler.PermissionHandlerPlugin.registerWith(registry.registrarFor("com.baseflow.permissionhandler.PermissionHandlerPlugin")); - FlutterLocalNotificationsPlugin.registerWith(registry.registrarFor("com.dexterous.flutterlocalnotifications.FlutterLocalNotificationsPlugin")); - SharedPreferencesPlugin.registerWith(registry.registrarFor("io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin")); - FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin")); - } -} diff --git a/audio_call/android/app/src/main/java/com/voximplant/flutter/audiocall/MainActivity.java b/audio_call/android/app/src/main/java/com/voximplant/flutter/audiocall/MainActivity.java index d26411d..ec27ebe 100644 --- a/audio_call/android/app/src/main/java/com/voximplant/flutter/audiocall/MainActivity.java +++ b/audio_call/android/app/src/main/java/com/voximplant/flutter/audiocall/MainActivity.java @@ -2,4 +2,5 @@ import io.flutter.embedding.android.FlutterActivity; -public class MainActivity extends FlutterActivity { } +public class MainActivity extends FlutterActivity { +} diff --git a/audio_call/android/app/src/main/res/drawable-mdpi/ic_vox_notification.png b/audio_call/android/app/src/main/res/drawable-mdpi/ic_vox_notification.png deleted file mode 100644 index ad0e6e40af4d64a54786fa6ea6d04ec4dd0877b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 454 zcmV;%0XhDOP)Wr9V<=Q+FL0bN?s-7 zZ7_5FPknN_-FuyyZjDpFn(k@7?>xTyUCYXsVcT{kDuN2$k29R3C5QxZ`*$n`kzf=b z$f(`q5@-?$EFq$f17!%~9W5xu{8kcBDOmKcP)_`RA^V(s|v{mdm{+N~?r0?ueT8DmAanl4d*ug5M(2Xjo@su>XMpUB*(^$h?uI+kQ wR;(_x?_Yj!2BnRtMi&n1yR6zs$jZ0z4PRcsOM$2FN&o-=07*qoM6N<$f@|{62mk;8 diff --git a/audio_call/android/app/src/main/res/drawable-hdpi/ic_vox_notification.png b/audio_call/android/app/src/main/res/drawable-v21/ic_notification.png similarity index 100% rename from audio_call/android/app/src/main/res/drawable-hdpi/ic_vox_notification.png rename to audio_call/android/app/src/main/res/drawable-v21/ic_notification.png diff --git a/audio_call/android/app/src/main/res/drawable-v21/launch_background.xml b/audio_call/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/audio_call/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/audio_call/android/app/src/main/res/drawable-xhdpi/ic_vox_notification.png b/audio_call/android/app/src/main/res/drawable-xhdpi/ic_vox_notification.png deleted file mode 100644 index 5bb929c88dcf7b13e3e0f61a39af11e6b5013584..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1025 zcmV+c1pfPpP)Y|+fZHYr*JOAxA;GU_1^v4tdj34<0w$`*=fp{JmS40?+e ztf+@*p)4aR+h{Y*cC*dAIsFg(g=v`a&NqFgLmv2Xhs&M0=gget|9uk@(rKO6am+A` zu3!X614BTV-<&T1Rp2$a1#&>BADj;b^TB)LPrC;a{c`uq4SQSF;hzEnIPVZZVTZ2; zaS#-N9~S2&r6&2Mi2H&ICg+>ML3uju;Y=4W6D$JLz%UT$;=D~DzyRcPxr^eD0;55O z9P0`o9#n!~pb2~h_rXc91LR6-fZ@FJ;>1L7(~?5R!BEf(#DXL`ng{l?ZXSb1@I`K# zt^mZDT8E+xFratf1*ia5!7B!Awx8f3xB_nTHy^-vrkL4kWbqzqC6bMppn^ zjDK$zwbTGE5&_Eo(wSzakBS`y-E{;okooG>q&7;xEKq9dO2MocgzE@k6pPb~ed9oj zP>{8A$!9i>87Nl+$igFWEAo`+-5qQUoUq=ESX~L|)7ZIvH%V(floTPPzzd zsDcw*rE6`b0+1Bm&8hwX90Dmyk{myd1L+{z@_i3*z`=!e1T0CqBf?QIZ%Je^DxC%aKShTF*OVuu_)%oHkA z7+z)*A^4U;&uXHK6Wlr<4Yq>I{7mI{@obY%9ULfU5lg|{^$SyK^$>7%}okAe+VhM4^qQnS5wjUr0c)syUtD@c!d_?@WZPVx z`$;y$bQS4zUeJr30KHAuDzI7PI8E_%T>+%FvKk9!4b(poyjHe?n9I=>Kq6&i*#MIiXl0X1McCCVw$r^@wqPj6_O-*3@|2sc8@EBM4Wc~T!mfcnx&p|u z%mS-5vQyQuUaB1Ab`!T!WKZcCC+%m4x{&L{=?Xx0;vTUD2Bm!3)J!4x1#v&KW00!x9v;}G;SFx9I*C~moU?p&`q%tZAx&b&8^Dtpx zAJ7Ig1788}0QJB;-gp&P+z{qo!-N0|$MxuK@1l|Qc0a}3lamLui)ha9DJ^p4B zS63T-*S3${Z;2+ zoi41NXC(VJn6EeqwQZ(>$_D~LVLs@A6hiYhX7gE`BZtZ94B!C=L}Ix~NhFJvfRJNt zB@A>aCk^Y~{!V7@%YY?714DIpf;rQd(Y6ljOp>9mj^^zt~0@C6Tnl>bg`&r>J!5RTi=!TFffo_I%+8 zC${3Vu2)G=rAe(hmY{qRwCo5IB-?wg%F@Vek`ONyctTn`++y`EZm~oqM~&SF57McY z#p{>j+6llL7MZ_Xdy$SmlM&JBQc6D^a4T0xI8I_KdEY5^>p|6Q@krU@E^edT7y^g$ z+KvPsKnn3E(eaO^rV2Yq&Su`r#ccmH8bnHIP`(9BHj2v05Hy+Pj$Vf74u;Np3NLBl z+(v!Wo}kc^g4QUGp9|as{OS-%vb}GiMw1nK3~)Md9p%MGY)#$HmzwQHsu5<2(Zd@` zwi4LFv?8i@3-}_g1)j1=P>V^b5n@V{$Cr}rl;fSp<>;jK(oyFd%s0g5O4Vjrm!8Uil6|+;vOx1 ztXXxapXpMlcgv)YH(ez`6|B73UHZu$gbM4BjL9jqI+X-Td31S#h4pBj*2T)A{utg` zc1_!~!{-Ujidb7&SN=jf{K=I3Za4j5yxgyYQ87~$L7D6T^qEABP8ObIm91gaTg2qE znp$cUI|vp0d!q?S{S4C+nT5+q)^aU!inflK`Vv+Tnn+|jN$b~%MHwScRkY-x zk|3!ePh!8Rf^T*hn{qkHE@!R^BSE-6g(w?h-KUgQPr0`eP4(wg+-d=bq(dza8W{De z@-nlHYrUu2OvpuHt2c!n%Fd@i>J?mUM46&F}IY5vLt8Q&W5BM zhZ&UN+kc-~NqhJT3di#`m0t<^=9_Q6`Ii3n57@qJFZADErT_o{07*qoM6N<$g7CRs A5C8xG diff --git a/audio_call/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/audio_call/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 036d09b..0000000 --- a/audio_call/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/audio_call/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/audio_call/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 036d09b..0000000 --- a/audio_call/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/audio_call/android/app/src/main/res/values-night/styles.xml b/audio_call/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/audio_call/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/audio_call/android/app/src/main/res/values/ic_launcher_background.xml b/audio_call/android/app/src/main/res/values/ic_launcher_background.xml deleted file mode 100644 index c5d5899..0000000 --- a/audio_call/android/app/src/main/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #FFFFFF - \ No newline at end of file diff --git a/audio_call/android/app/src/main/res/values/styles.xml b/audio_call/android/app/src/main/res/values/styles.xml index 00fa441..cb1ef88 100644 --- a/audio_call/android/app/src/main/res/values/styles.xml +++ b/audio_call/android/app/src/main/res/values/styles.xml @@ -1,8 +1,18 @@ - + + diff --git a/audio_call/android/app/src/profile/AndroidManifest.xml b/audio_call/android/app/src/profile/AndroidManifest.xml index 411da15..d675ce0 100644 --- a/audio_call/android/app/src/profile/AndroidManifest.xml +++ b/audio_call/android/app/src/profile/AndroidManifest.xml @@ -1,6 +1,7 @@ - diff --git a/audio_call/android/build.gradle b/audio_call/android/build.gradle index a4546b8..13c7096 100644 --- a/audio_call/android/build.gradle +++ b/audio_call/android/build.gradle @@ -1,20 +1,21 @@ buildscript { + ext.kotlin_version = '1.7.10' repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.2' - classpath 'com.google.gms:google-services:4.3.4' - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10' + classpath 'com.android.tools.build:gradle:7.2.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.google.gms:google-services:4.3.8' } } allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/audio_call/android/gradle.properties b/audio_call/android/gradle.properties index 38c8d45..94adc3a 100644 --- a/audio_call/android/gradle.properties +++ b/audio_call/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M -android.enableR8=true android.useAndroidX=true android.enableJetifier=true diff --git a/audio_call/android/gradle/wrapper/gradle-wrapper.properties b/audio_call/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3c472b9 --- /dev/null +++ b/audio_call/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/audio_call/android/settings.gradle b/audio_call/android/settings.gradle index 5a2f14f..44e62bc 100644 --- a/audio_call/android/settings.gradle +++ b/audio_call/android/settings.gradle @@ -1,15 +1,11 @@ include ':app' -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/audio_call/ios/.gitignore b/audio_call/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/audio_call/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/audio_call/ios/Flutter/AppFrameworkInfo.plist b/audio_call/ios/Flutter/AppFrameworkInfo.plist index f2872cf..9625e10 100644 --- a/audio_call/ios/Flutter/AppFrameworkInfo.plist +++ b/audio_call/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) + en CFBundleExecutable App CFBundleIdentifier @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/audio_call/ios/Flutter/Debug.xcconfig b/audio_call/ios/Flutter/Debug.xcconfig index e8efba1..ec97fc6 100644 --- a/audio_call/ios/Flutter/Debug.xcconfig +++ b/audio_call/ios/Flutter/Debug.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/audio_call/ios/Flutter/Release.xcconfig b/audio_call/ios/Flutter/Release.xcconfig index 399e934..c4855bf 100644 --- a/audio_call/ios/Flutter/Release.xcconfig +++ b/audio_call/ios/Flutter/Release.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/audio_call/ios/Podfile b/audio_call/ios/Podfile index 894c815..88359b2 100644 --- a/audio_call/ios/Podfile +++ b/audio_call/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -15,7 +15,7 @@ def flutter_root unless File.exist?(generated_xcode_build_settings_path) raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" end - + File.foreach(generated_xcode_build_settings_path) do |line| matches = line.match(/FLUTTER_ROOT\=(.*)/) return matches[1].strip if matches @@ -30,68 +30,12 @@ flutter_ios_podfile_setup target 'Runner' do use_frameworks! use_modular_headers! - + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end post_install do |installer| installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['ENABLE_BITCODE'] = 'NO' - # You can enable the permissions needed here. For example to enable camera - # permission, just remove the `#` character in front so it looks like this: - # - # ## dart: PermissionGroup.camera - # 'PERMISSION_CAMERA=1' - # - # Preprocessor definitions can be found in: https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h - config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ - '$(inherited)', - - ## dart: PermissionGroup.calendar - # 'PERMISSION_EVENTS=1', - - ## dart: PermissionGroup.reminders - # 'PERMISSION_REMINDERS=1', - - ## dart: PermissionGroup.contacts - # 'PERMISSION_CONTACTS=1', - - ## dart: PermissionGroup.camera - # 'PERMISSION_CAMERA=1', - - ##dart: PermissionGroup.microphone - 'PERMISSION_MICROPHONE=1', - - ## dart: PermissionGroup.speech - # 'PERMISSION_SPEECH_RECOGNIZER=1', - - ## dart: PermissionGroup.photos - # 'PERMISSION_PHOTOS=1', - - ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse] - # 'PERMISSION_LOCATION=1', - - ## dart: PermissionGroup.notification - # 'PERMISSION_NOTIFICATIONS=1', - - ## dart: PermissionGroup.mediaLibrary - # 'PERMISSION_MEDIA_LIBRARY=1', - - ## dart: PermissionGroup.sensors - # 'PERMISSION_SENSORS=1', - - ## dart: PermissionGroup.bluetooth - # 'PERMISSION_BLUETOOTH=1', - - ## dart: PermissionGroup.appTrackingTransparency - # 'PERMISSION_APP_TRACKING_TRANSPARENCY=1', - - ## dart: PermissionGroup.criticalAlerts - # 'PERMISSION_CRITICAL_ALERTS=1' - ] - # End of the permission_handler configuration - end flutter_additional_ios_build_settings(target) end end diff --git a/audio_call/ios/Podfile.lock b/audio_call/ios/Podfile.lock index 7b3ae25..a8e18d7 100644 --- a/audio_call/ios/Podfile.lock +++ b/audio_call/ios/Podfile.lock @@ -1,87 +1,79 @@ PODS: - - Firebase/CoreOnly (6.33.0): - - FirebaseCore (= 6.10.3) - - Firebase/Messaging (6.33.0): + - Firebase/CoreOnly (10.6.0): + - FirebaseCore (= 10.6.0) + - Firebase/Messaging (10.6.0): - Firebase/CoreOnly - - FirebaseMessaging (~> 4.7.0) - - firebase_core (0.5.3): - - Firebase/CoreOnly (~> 6.33.0) + - FirebaseMessaging (~> 10.6.0) + - firebase_core (2.8.0): + - Firebase/CoreOnly (= 10.6.0) - Flutter - - firebase_messaging (7.0.3): - - Firebase/CoreOnly (~> 6.33.0) - - Firebase/Messaging (~> 6.33.0) + - firebase_messaging (14.3.0): + - Firebase/Messaging (= 10.6.0) - firebase_core - Flutter - - FirebaseCore (6.10.3): - - FirebaseCoreDiagnostics (~> 1.6) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Logger (~> 6.7) - - FirebaseCoreDiagnostics (1.7.0): - - GoogleDataTransport (~> 7.4) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Logger (~> 6.7) - - nanopb (~> 1.30906.0) - - FirebaseInstallations (1.7.0): - - FirebaseCore (~> 6.10) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/UserDefaults (~> 6.7) - - PromisesObjC (~> 1.2) - - FirebaseInstanceID (4.8.0): - - FirebaseCore (~> 6.10) - - FirebaseInstallations (~> 1.6) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/UserDefaults (~> 6.7) - - FirebaseMessaging (4.7.1): - - FirebaseCore (~> 6.10) - - FirebaseInstanceID (~> 4.7) - - GoogleUtilities/AppDelegateSwizzler (~> 6.7) - - GoogleUtilities/Environment (~> 6.7) - - GoogleUtilities/Reachability (~> 6.7) - - GoogleUtilities/UserDefaults (~> 6.7) - - Protobuf (>= 3.9.2, ~> 3.9) + - FirebaseCore (10.6.0): + - FirebaseCoreInternal (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/Logger (~> 7.8) + - FirebaseCoreInternal (10.8.0): + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseInstallations (10.8.0): + - FirebaseCore (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) + - PromisesObjC (~> 2.1) + - FirebaseMessaging (10.6.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleDataTransport (~> 9.2) + - GoogleUtilities/AppDelegateSwizzler (~> 7.8) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/Reachability (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) + - nanopb (< 2.30910.0, >= 2.30908.0) - Flutter (1.0.0) - - "flutter_callkit_voximplant (1.2.0+3)": + - flutter_callkit_voximplant (2.1.0): - Flutter - flutter_local_notifications (0.0.1): - Flutter - - flutter_voip_push_notification (0.0.1): + - flutter_voximplant (3.8.0): - Flutter - - flutter_voximplant (3.5.1): - - Flutter - - VoxImplantSDK (= 2.46.10) - - GoogleDataTransport (7.5.1): - - nanopb (~> 1.30906.0) - - GoogleUtilities/AppDelegateSwizzler (6.7.2): + - VoxImplantSDK (= 2.46.12) + - GoogleDataTransport (9.2.2): + - GoogleUtilities/Environment (~> 7.7) + - nanopb (< 2.30910.0, >= 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/AppDelegateSwizzler (7.11.1): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (6.7.2): - - PromisesObjC (~> 1.2) - - GoogleUtilities/Logger (6.7.2): + - GoogleUtilities/Environment (7.11.1): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.11.1): - GoogleUtilities/Environment - - GoogleUtilities/Network (6.7.2): + - GoogleUtilities/Network (7.11.1): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.7.2)" - - GoogleUtilities/Reachability (6.7.2): + - "GoogleUtilities/NSData+zlib (7.11.1)" + - GoogleUtilities/Reachability (7.11.1): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.7.2): + - GoogleUtilities/UserDefaults (7.11.1): - GoogleUtilities/Logger - - nanopb (1.30906.0): - - nanopb/decode (= 1.30906.0) - - nanopb/encode (= 1.30906.0) - - nanopb/decode (1.30906.0) - - nanopb/encode (1.30906.0) - - "permission_handler (5.0.1+1)": + - nanopb (2.30909.0): + - nanopb/decode (= 2.30909.0) + - nanopb/encode (= 2.30909.0) + - nanopb/decode (2.30909.0) + - nanopb/encode (2.30909.0) + - permission_handler_apple (9.0.4): - Flutter - - PromisesObjC (1.2.12) - - Protobuf (3.21.5) - - shared_preferences (0.0.1): + - PromisesObjC (2.2.0) + - shared_preferences_foundation (0.0.1): - Flutter - - VoxImplantSDK (2.46.10): - - VoxImplantSDK/Core (= 2.46.10) - - VoxImplantSDK/Core (2.46.10): + - FlutterMacOS + - VoxImplantSDK (2.46.12): + - VoxImplantSDK/Core (= 2.46.12) + - VoxImplantSDK/Core (2.46.12): - VoxImplantWebRTC (= 93.0.0) - VoxImplantWebRTC (93.0.0) @@ -91,24 +83,21 @@ DEPENDENCIES: - Flutter (from `Flutter`) - flutter_callkit_voximplant (from `.symlinks/plugins/flutter_callkit_voximplant/ios`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - - flutter_voip_push_notification (from `.symlinks/plugins/flutter_voip_push_notification/ios`) - flutter_voximplant (from `.symlinks/plugins/flutter_voximplant/ios`) - - permission_handler (from `.symlinks/plugins/permission_handler/ios`) - - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) + - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) SPEC REPOS: trunk: - Firebase - FirebaseCore - - FirebaseCoreDiagnostics + - FirebaseCoreInternal - FirebaseInstallations - - FirebaseInstanceID - FirebaseMessaging - GoogleDataTransport - GoogleUtilities - nanopb - PromisesObjC - - Protobuf - VoxImplantSDK - VoxImplantWebRTC @@ -123,39 +112,34 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_callkit_voximplant/ios" flutter_local_notifications: :path: ".symlinks/plugins/flutter_local_notifications/ios" - flutter_voip_push_notification: - :path: ".symlinks/plugins/flutter_voip_push_notification/ios" flutter_voximplant: :path: ".symlinks/plugins/flutter_voximplant/ios" - permission_handler: - :path: ".symlinks/plugins/permission_handler/ios" - shared_preferences: - :path: ".symlinks/plugins/shared_preferences/ios" + permission_handler_apple: + :path: ".symlinks/plugins/permission_handler_apple/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/ios" SPEC CHECKSUMS: - Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5 - firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659 - firebase_messaging: 0aea2cd5885b65e19ede58ee3507f485c992cc75 - FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd - FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 - FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2 - FirebaseInstanceID: bd3ffc24367f901a43c063b36c640b345a4a5dd1 - FirebaseMessaging: 5eca4ef173de76253352511aafef774caa1cba2a - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - flutter_callkit_voximplant: 3a3645b8265af3d61050087056ba1440ef9a5eaf + Firebase: f13680471b021937f2230ea8503c7809d8c29806 + firebase_core: 58542d7399889ebdbb034baa72d081e54c5c814d + firebase_messaging: 01a8db2962f81ea190d08db767aba2e7e805e647 + FirebaseCore: fa80ad16a62d52f67274b5b88304c3a318bbf9a4 + FirebaseCoreInternal: fa2899eb1f340054858d289e5a0fb935a0a74e52 + FirebaseInstallations: b2a05a3fe707df764345d68685534d07d0664af3 + FirebaseMessaging: fd93783258c53ae5cdb9b41bf0c51606a677f2d5 + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + flutter_callkit_voximplant: 9e1128135757ea8a21ae7bbc06ff4a37e906ccc5 flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 - flutter_voip_push_notification: f28f3ce1c6b835a3fa9e833f37f33dc1291ab888 - flutter_voximplant: 5e54116ff1f9cd0fd973f62950e61b39bac3bd57 - GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 - GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 - nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc - permission_handler: eac8e15b4a1a3fba55b761d19f3f4e6b005d15b6 - PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 - Protobuf: 7504b04fffcf6662ad629694db8231f5e744327f - shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d - VoxImplantSDK: db660ef67c89e3df0e07ca270369c496effab6e5 + flutter_voximplant: 40c3110cc9943047f36fb9847f8803350237480a + GoogleDataTransport: 8378d1fa8ac49753ea6ce70d65a7cb70ce5f66e6 + GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749 + nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 + permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce + PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef + shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 + VoxImplantSDK: 577259786bc3da6ed3cf3d4ee1a8e08245c19ce6 VoxImplantWebRTC: 8ddd8a63d0c20afa604fec22a2552c32b0371974 -PODFILE CHECKSUM: 20d5202926f5ac9150a51c202a7ded0a3b174026 +PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.0 diff --git a/audio_call/ios/Runner.xcodeproj/project.pbxproj b/audio_call/ios/Runner.xcodeproj/project.pbxproj index a066bda..eab0823 100644 --- a/audio_call/ios/Runner.xcodeproj/project.pbxproj +++ b/audio_call/ios/Runner.xcodeproj/project.pbxproj @@ -3,19 +3,17 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 7EA25A1D25377B0B00A806B2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EA25A1C25377B0B00A806B2 /* AppDelegate.swift */; }; - 920FECDC255462D5005D467B /* fennelliott_beeping.wav in Resources */ = {isa = PBXBuildFile; fileRef = 920FECDB255462D5005D467B /* fennelliott_beeping.wav */; }; - 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - B20899292382FA590097FF08 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B20899282382FA580097FF08 /* GoogleService-Info.plist */; }; + F2E7AB6029F1B4D600B9DCA1 /* fennelliott_beeping.wav in Resources */ = {isa = PBXBuildFile; fileRef = F2E7AB5F29F1B4D600B9DCA1 /* fennelliott_beeping.wav */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -35,11 +33,9 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7EA25A1B25377B0B00A806B2 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 7EA25A1C25377B0B00A806B2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 8EF31FC2236B1B9500682CB7 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; - 920FECDB255462D5005D467B /* fennelliott_beeping.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = fennelliott_beeping.wav; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -47,7 +43,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B20899282382FA580097FF08 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + F2E7AB5E29F192B300B9DCA1 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + F2E7AB5F29F1B4D600B9DCA1 /* fennelliott_beeping.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = fennelliott_beeping.wav; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -61,13 +58,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 525C86CF0F5831913430637B /* Pods */ = { - isa = PBXGroup; - children = ( - ); - path = Pods; - sourceTree = ""; - }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -85,7 +75,7 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 525C86CF0F5831913430637B /* Pods */, + AB711A1BF76F92D99C0EA15C /* Pods */, ); sourceTree = ""; }; @@ -100,27 +90,25 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 920FECDB255462D5005D467B /* fennelliott_beeping.wav */, - B20899282382FA580097FF08 /* GoogleService-Info.plist */, - 8EF31FC2236B1B9500682CB7 /* Runner.entitlements */, + F2E7AB5F29F1B4D600B9DCA1 /* fennelliott_beeping.wav */, + F2E7AB5E29F192B300B9DCA1 /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 7EA25A1C25377B0B00A806B2 /* AppDelegate.swift */, - 7EA25A1B25377B0B00A806B2 /* Runner-Bridging-Header.h */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, ); path = Runner; sourceTree = ""; }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { + AB711A1BF76F92D99C0EA15C /* Pods */ = { isa = PBXGroup; children = ( ); - name = "Supporting Files"; + path = Pods; sourceTree = ""; }; /* End PBXGroup section */ @@ -153,17 +141,16 @@ isa = PBXProject; attributes = { LastUpgradeCheck = 1300; - ORGANIZATIONNAME = "The Chromium Authors"; + ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = W9BHJBL635; - LastSwiftMigration = 1200; + LastSwiftMigration = 1100; }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; + compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -187,11 +174,9 @@ files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, - 920FECDC255462D5005D467B /* fennelliott_beeping.wav in Resources */, + F2E7AB6029F1B4D600B9DCA1 /* fennelliott_beeping.wav in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - B20899292382FA590097FF08 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -200,6 +185,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -210,10 +196,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -224,7 +211,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; /* End PBXShellScriptBuildPhase section */ @@ -233,7 +220,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7EA25A1D25377B0B00A806B2 /* AppDelegate.swift in Sources */, + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -301,9 +288,10 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -319,20 +307,11 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = W9BHJBL635; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); PRODUCT_BUNDLE_IDENTIFIER = com.voximplant.flutter.audioCall; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -388,7 +367,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -437,9 +416,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -455,20 +437,11 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = W9BHJBL635; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); PRODUCT_BUNDLE_IDENTIFIER = com.voximplant.flutter.audioCall; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -488,20 +461,11 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = W9BHJBL635; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); PRODUCT_BUNDLE_IDENTIFIER = com.voximplant.flutter.audioCall; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/audio_call/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/audio_call/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/audio_call/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/audio_call/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/audio_call/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/audio_call/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/audio_call/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/audio_call/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3db53b6..c87d15a 100644 --- a/audio_call/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/audio_call/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -27,8 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + - - + + + + IDEDidComputeMac32BitWarning + + + diff --git a/audio_call/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/audio_call/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/audio_call/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/audio_call/ios/Runner/AppDelegate.swift b/audio_call/ios/Runner/AppDelegate.swift index aa7c7e3..4bb1feb 100644 --- a/audio_call/ios/Runner/AppDelegate.swift +++ b/audio_call/ios/Runner/AppDelegate.swift @@ -1,56 +1,46 @@ +import UIKit import Flutter import PushKit -import CallKit import flutter_callkit_voximplant -import flutter_voip_push_notification -import shared_preferences import flutter_voximplant -import permission_handler @UIApplicationMain -final class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate { +@objc class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate { + private let voipRegistry: PKPushRegistry = PKPushRegistry(queue: .main) override func application( _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - // Registering plugins manually, because on iOS FLTFirebaseMessagingPlugin and FlutterLocalNotificationsPlugin - // are not used and should not be registered - FlutterCallkitPlugin.register(with: registrar(forPlugin: "FlutterCallkitPlugin")!) - FlutterVoipPushNotificationPlugin.register(with: registrar(forPlugin: "FlutterVoipPushNotificationPlugin")!) - VoximplantPlugin.register(with: registrar(forPlugin: "VoximplantPlugin")!) - PermissionHandlerPlugin.register(with: registrar(forPlugin: "PermissionHandlerPlugin")!) - FLTSharedPreferencesPlugin.register(with: registrar(forPlugin: "FLTSharedPreferencesPlugin")!) + GeneratedPluginRegistrant.register(with: self) + PushKitPlugin.register(with: self.registrar(forPlugin: "PushKitPlugin")!) + + PushKitPlugin.shared.setPKPushRegistry(voipRegistry) + voipRegistry.delegate = self + voipRegistry.desiredPushTypes = [.voIP] + return super.application(application, didFinishLaunchingWithOptions: launchOptions) } - // MARK: - PKPushRegistryDelegate - - func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) { - FlutterVoipPushNotificationPlugin.didUpdate(pushCredentials, forType: type.rawValue) - } - func pushRegistry(_ registry: PKPushRegistry, - didReceiveIncomingPushWith payload: PKPushPayload, - for type: PKPushType - ) { - processPush(with: payload, type: type, and: nil) + func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) { + PushKitPlugin.shared.updatePushCredentials(pushCredentials, for: type) } func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, - completion: @escaping () -> Void - ) { + completion: @escaping () -> Void) { processPush(with: payload, type: type, and: completion) } private func processPush(with payload: PKPushPayload, type: PKPushType, and completion: (() -> Void)?) { print("Push received: \(payload)") - - FlutterVoipPushNotificationPlugin.didReceiveIncomingPush(with: payload, forType: type.rawValue) - + + PushKitPlugin.shared.reportIncomingPushWith(payload: payload, for: type) + let callKitPlugin = FlutterCallkitPlugin.sharedInstance - + guard let content = payload.dictionaryPayload.content, UIApplication.shared.applicationState != .active, @@ -83,6 +73,7 @@ final class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate { } } + fileprivate extension Dictionary where Key == AnyHashable { var content: [String: Any]? { self["voximplant"] as? [String:Any] @@ -98,3 +89,82 @@ fileprivate extension Dictionary where Key == String { self["userid"] as! String } } + +class PushKitPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { + static let shared = PushKitPlugin() + private var pushKitEventChannel: FlutterEventChannel? + private var eventSink: FlutterEventSink? + private var voipRegistry: PKPushRegistry? + + public static func register(with registrar: FlutterPluginRegistrar) { + print("YULIA: register") + let channel = FlutterMethodChannel(name: "plugins.voximplant.com/pushkit", binaryMessenger: registrar.messenger()) + let instance = PushKitPlugin.shared + instance.setup(with: registrar) + registrar.addMethodCallDelegate(instance, channel: channel) + } + + private override init() { + super.init() + } + + private func setup(with registrar: FlutterPluginRegistrar) { + pushKitEventChannel = FlutterEventChannel(name: "plugins.voximplant.com/pushkitevents", binaryMessenger: registrar.messenger()) + pushKitEventChannel?.setStreamHandler(self) + } + + public func setPKPushRegistry(_ registry: PKPushRegistry) { + self.voipRegistry = registry + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + if (call.method == "voipToken") { + guard let voipToken = voipRegistry?.pushToken(for: .voIP) else { + result(nil) + return + } + let token = convertTokenToString(data: voipToken) + result(token) + } + } + + public func updatePushCredentials(_ pushCredentials: PKPushCredentials, for type: PKPushType) { + guard let eventSink = self.eventSink else { + print("[PushKitPlugin]: updatePushCredentials: eventSink is not initialized") + return + } + let token = self.convertTokenToString(data: pushCredentials.token) + eventSink(["event": "didUpdatePushCredentials", "token": token]) + } + + public func reportIncomingPushWith(payload: PKPushPayload, for type: PKPushType) { + guard let eventSink = self.eventSink else { + print("[PushKitPlugin]: reportIncomingPushWith: eventSink is not initialized") + return + } + eventSink(["event": "didReceiveIncomingPushWithPayload", + "payload": payload.dictionaryPayload] as [String : Any]) + } + + public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { + if let type = arguments as? String { + if (type == "pushkit") { + self.eventSink = events + } + } + return nil + } + + public func onCancel(withArguments arguments: Any?) -> FlutterError? { + if let type = arguments as? String { + if (type == "pushkit") { + self.eventSink = nil + } + } + return nil + } + + private func convertTokenToString(data: Data) -> String { + return data.map { String(format: "%02.2hhx", $0) }.joined() + } +} diff --git a/audio_call/ios/Runner/Assets.xcassets/Contents.json b/audio_call/ios/Runner/Assets.xcassets/Contents.json deleted file mode 100644 index da4a164..0000000 --- a/audio_call/ios/Runner/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/audio_call/ios/Runner/GoogleService-Info.plist b/audio_call/ios/Runner/GoogleService-Info.plist deleted file mode 100644 index a35d940..0000000 --- a/audio_call/ios/Runner/GoogleService-Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CLIENT_ID - qwerqwer.apps.googleusercontent.com - REVERSED_CLIENT_ID - com.googleusercontent.apps.qwerqwer - API_KEY - qwer1111 - GCM_SENDER_ID - 1111 - PLIST_VERSION - 1 - BUNDLE_ID - com.voximplant.flutter.audioCall - PROJECT_ID - qwer - STORAGE_BUCKET - qwer.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - 1:1111:ios:2222 - - diff --git a/audio_call/ios/Runner/Info.plist b/audio_call/ios/Runner/Info.plist index e7506db..32b9d0d 100644 --- a/audio_call/ios/Runner/Info.plist +++ b/audio_call/ios/Runner/Info.plist @@ -2,8 +2,12 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Audio Call CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -11,7 +15,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - Audio call + $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString @@ -22,9 +26,13 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + NSMicrophoneUsageDescription + To make audio calls + UIApplicationSupportsIndirectInputEvents + UIBackgroundModes - audio + processing remote-notification voip @@ -47,65 +55,5 @@ UIViewControllerBasedStatusBarAppearance - CADisableMinimumFrameDurationOnPhone - - - - NSLocationWhenInUseUsageDescription - Need location when in use - NSLocationAlwaysAndWhenInUseUsageDescription - Always and when in use! - NSLocationUsageDescription - Older devices need location. - NSLocationAlwaysUsageDescription - Can I have location always? - - - NSAppleMusicUsageDescription - Music! - kTCCServiceMediaLibrary - media - - - NSCalendarsUsageDescription - Calendars - - - NSCameraUsageDescription - camera - - - NSContactsUsageDescription - contacts - - - NSMicrophoneUsageDescription - Access to the microphone for calls - - - NSSpeechRecognitionUsageDescription - speech - - - NSMotionUsageDescription - motion - - - NSPhotoLibraryUsageDescription - photos - - - NSRemindersUsageDescription - reminders - - - NSBluetoothAlwaysUsageDescription - bluetooth - NSBluetoothPeripheralUsageDescription - bluetooth - - - NSUserTrackingUsageDescription - appTrackingTransparency diff --git a/audio_call/lib/main.dart b/audio_call/lib/main.dart index 3ffc155..0a607e3 100644 --- a/audio_call/lib/main.dart +++ b/audio_call/lib/main.dart @@ -21,7 +21,7 @@ import 'package:flutter_voximplant/flutter_voximplant.dart'; class SimpleBlocDelegate extends BlocObserver { @override - void onEvent(Bloc bloc, Object event) { + void onEvent(Bloc bloc, Object? event) { super.onEvent(bloc, event); log(event); } @@ -33,13 +33,13 @@ class SimpleBlocDelegate extends BlocObserver { } @override - void onError(Cubit cubit, Object error, StackTrace stackTrace) { - super.onError(cubit, error, stackTrace); + void onError(BlocBase bloc, Object error, StackTrace stackTrace) { + super.onError(bloc, error, stackTrace); log(error); } } -VIClientConfig get defaultConfig => VIClientConfig(); +VIClientConfig get defaultConfig => VIClientConfig(bundleId: 'com.voximplant.flutter.audioCall'); void main() { WidgetsFlutterBinding.ensureInitialized(); @@ -55,11 +55,11 @@ void main() { NotificationHelper(); } - runApp(App()); + runApp(const App()); } class App extends StatelessWidget { - App({Key key}) : super(key: key); + const App({super.key}); @override Widget build(BuildContext context) { @@ -87,13 +87,16 @@ class App extends StatelessWidget { ), ); } else if (routeSettings.name == AppRoutes.activeCall) { - ActiveCallPageArguments arguments = routeSettings.arguments; + final routingArguments = routeSettings.arguments as ActiveCallPageArguments; + ActiveCallPageArguments arguments = routingArguments; return PageRouteBuilder( - pageBuilder: (_, a1, a2) => BlocProvider( - create: (_) => - ActiveCallBloc(arguments.isIncoming, arguments.endpoint), - child: ActiveCallPage(), - ), + pageBuilder: (_, a1, a2) => + BlocProvider( + create: (_) => + ActiveCallBloc( + arguments.isIncoming, arguments.endpoint), + child: ActiveCallPage(), + ), ); } else if (routeSettings.name == AppRoutes.incomingCall) { return PageRouteBuilder( diff --git a/audio_call/lib/screens/active_call/active_call_page.dart b/audio_call/lib/screens/active_call/active_call_page.dart index 78e7ff9..5e55cea 100644 --- a/audio_call/lib/screens/active_call/active_call_page.dart +++ b/audio_call/lib/screens/active_call/active_call_page.dart @@ -1,7 +1,5 @@ /// Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved. import 'package:audio_call/screens/active_call/active_call.dart'; -import 'package:audio_call/screens/active_call/bloc/active_call_event.dart'; -import 'package:audio_call/screens/active_call/bloc/active_call_state.dart'; import 'package:audio_call/screens/call_failed/call_failed.dart'; import 'package:audio_call/theme/voximplant_theme.dart'; import 'package:audio_call/utils/navigation_helper.dart'; @@ -17,29 +15,28 @@ class ActiveCallPage extends StatefulWidget { } class _ActiveCallPageState extends State { - ActiveCallBloc _bloc; - - _ActiveCallPageState(); + late ActiveCallBloc _bloc; @override void initState() { super.initState(); _bloc = BlocProvider.of(context); + context.read().add(ReadyToStartCallEvent()); } @override Widget build(BuildContext context) { - void _hangup() => _bloc.add(HangupPressedEvent()); + void hangup() => _bloc.add(HangupPressedEvent()); - void _hold(bool hold) => _bloc.add(HoldPressedEvent(hold: hold)); + void _hold(bool hold) => _bloc.add(HoldPressedEvent(hold)); - void _mute(bool mute) => _bloc.add(MutePressedEvent(mute: mute)); + void _mute(bool mute) => _bloc.add(MutePressedEvent(mute)); - void _selectAudioDevice(VIAudioDevice device) => _bloc.add( - SelectAudioDevicePressedEvent(device: device), + void selectAudioDevice(VIAudioDevice device) => _bloc.add( + SelectAudioDevicePressedEvent(device), ); - IconData _getIconForDevice(VIAudioDevice device) { + IconData getIconForDevice(VIAudioDevice device) { switch (device) { case VIAudioDevice.Bluetooth: return Icons.bluetooth_audio; @@ -52,22 +49,22 @@ class _ActiveCallPageState extends State { } } - String _getNameForDevice(VIAudioDevice device) { + String getNameForDevice(VIAudioDevice device) { List splitted = device.toString().split('.'); - if (splitted != null && splitted.length >= 2) { + if (splitted.length >= 2) { return splitted[1]; } else { return device.toString(); } } - _showAvailableAudioDevices(List devices) { + showAvailableAudioDevices(List devices) { return showDialog( context: context, barrierDismissible: true, builder: (BuildContext context) { return AlertDialog( - title: Text('Select audio device'), + title: const Text('Select audio device'), content: SingleChildScrollView( child: Container( width: 100, @@ -75,13 +72,13 @@ class _ActiveCallPageState extends State { child: ListView.builder( itemCount: devices.length, itemBuilder: (_, int index) { - return FlatButton( + return TextButton( child: Text( - _getNameForDevice(devices[index]), - style: TextStyle(fontSize: 16), + getNameForDevice(devices[index]), + style: const TextStyle(fontSize: 16), ), onPressed: () { - _selectAudioDevice(devices[index]); + selectAudioDevice(devices[index]); }, ); }, @@ -111,7 +108,7 @@ class _ActiveCallPageState extends State { builder: (context, state) { return Scaffold( appBar: AppBar( - title: Text('Call'), + title: const Text('Call'), ), backgroundColor: VoximplantColors.white, body: SafeArea( @@ -125,13 +122,13 @@ class _ActiveCallPageState extends State { children: [ Text( state.endpointName, - style: TextStyle( + style: const TextStyle( fontSize: 26, ), ), Text( state.callStatus, - style: TextStyle( + style: const TextStyle( fontSize: 18, ), ), @@ -151,7 +148,7 @@ class _ActiveCallPageState extends State { bottom: 20, ), child: Ink( - decoration: ShapeDecoration( + decoration: const ShapeDecoration( color: VoximplantColors.white, shape: CircleBorder( side: BorderSide( @@ -181,7 +178,7 @@ class _ActiveCallPageState extends State { left: 20, ), child: Ink( - decoration: ShapeDecoration( + decoration: const ShapeDecoration( color: VoximplantColors.white, shape: CircleBorder( side: BorderSide( @@ -211,7 +208,7 @@ class _ActiveCallPageState extends State { bottom: 20, ), child: Ink( - decoration: ShapeDecoration( + decoration: const ShapeDecoration( color: VoximplantColors.white, shape: CircleBorder( side: BorderSide( @@ -223,13 +220,13 @@ class _ActiveCallPageState extends State { ), child: IconButton( onPressed: () { - _showAvailableAudioDevices( + showAvailableAudioDevices( state.availableAudioDevices, ); }, iconSize: 40, icon: Icon( - _getIconForDevice(state.activeAudioDevice), + getIconForDevice(state.activeAudioDevice), color: VoximplantColors.button, ), tooltip: 'Select audio device', @@ -248,7 +245,7 @@ class _ActiveCallPageState extends State { bottom: 20, ), child: Ink( - decoration: ShapeDecoration( + decoration: const ShapeDecoration( color: VoximplantColors.white, shape: CircleBorder( side: BorderSide( @@ -259,9 +256,9 @@ class _ActiveCallPageState extends State { ), ), child: IconButton( - onPressed: _hangup, + onPressed: hangup, iconSize: 40, - icon: Icon( + icon: const Icon( Icons.call_end, color: VoximplantColors.red, ), diff --git a/audio_call/lib/screens/active_call/active_call_page_arguments.dart b/audio_call/lib/screens/active_call/active_call_page_arguments.dart index 26b139b..f0d90ee 100644 --- a/audio_call/lib/screens/active_call/active_call_page_arguments.dart +++ b/audio_call/lib/screens/active_call/active_call_page_arguments.dart @@ -7,6 +7,6 @@ class ActiveCallPageArguments { bool isIncoming; ActiveCallPageArguments( - {@required this.endpoint, - @required this.isIncoming}); + {required this.endpoint, + required this.isIncoming}); } diff --git a/audio_call/lib/screens/active_call/bloc/active_call_bloc.dart b/audio_call/lib/screens/active_call/bloc/active_call_bloc.dart index 82a446e..1ef19aa 100644 --- a/audio_call/lib/screens/active_call/bloc/active_call_bloc.dart +++ b/audio_call/lib/screens/active_call/bloc/active_call_bloc.dart @@ -11,153 +11,177 @@ import 'package:audio_call/services/call/callkit_service.dart'; import 'package:audio_call/utils/log.dart'; import 'package:bloc/bloc.dart'; import 'package:flutter_callkit_voximplant/flutter_callkit_voximplant.dart'; +import 'package:flutter_voximplant/flutter_voximplant.dart'; class ActiveCallBloc extends Bloc { final CallService _callService = CallService(); - final CallKitService _callKitService = + final CallKitService? _callKitService = Platform.isIOS ? CallKitService() : null; - StreamSubscription _callStateSubscription; - StreamSubscription _audioDeviceSubscription; + StreamSubscription? _callStateSubscription; + StreamSubscription? _audioDeviceSubscription; - ActiveCallBloc(bool isIncoming, String endpoint) - : super(ActiveCallState( + bool _isIncoming; + String _endpoint; + + ActiveCallBloc(this._isIncoming, this._endpoint) + : super(const ActiveCallState( isOnHold: false, isMuted: false, - activeAudioDevice: null, + activeAudioDevice: VIAudioDevice.Earpiece, availableAudioDevices: [], endpointName: '', callStatus: '', )) { - add(ReadyToStartCallEvent( - isIncoming: isIncoming, - endpoint: endpoint, - )); + on(_readyToStartCall); + on(_handleCallChanged); + on(_holdCall); + on(_muteAudio); + on(_hangupCall); + on(_selectAudioDevice); + on(_handleAudioDevicesChanged); } @override Future close() { - if (_callStateSubscription != null) { - _callStateSubscription.cancel(); - } - if (_audioDeviceSubscription != null) { - _audioDeviceSubscription.cancel(); - } + _callStateSubscription?.cancel(); + _audioDeviceSubscription?.cancel(); return super.close(); } - @override - Stream mapEventToState(ActiveCallEvent event) async* { - if (event is ReadyToStartCallEvent) { - _callStateSubscription = _callService.subscribeToCallEvents().listen( - (event) { - add(CallChangedEvent(event: event)); - }, - ); - _audioDeviceSubscription = - _callService.subscribeToAudioDeviceEvents().listen( - (event) { - add(AudioDevicesChanged(event: event)); - }, - ); - try { - if (event.isIncoming) { - if (Platform.isAndroid) { - if (_callService.hasActiveCall) { + Future _readyToStartCall( + ReadyToStartCallEvent event, + Emitter emit) async { + _callStateSubscription = _callService.subscribeToCallEvents().listen( + (event) { + add(CallChangedEvent(event)); + }, + ); + _audioDeviceSubscription = + _callService.subscribeToAudioDeviceEvents().listen( + (event) { + add(AudioDevicesChanged(event)); + }, + ); + try { + if (_isIncoming) { + if (Platform.isAndroid) { + if (_callService.hasActiveCall) { + await _callService.answerCall(); + } else { + _callService.onIncomingCall = (_) async { await _callService.answerCall(); - } else { - _callService.onIncomingCall = (_) async { - await _callService.answerCall(); - }; - } - } - } else /* if (direction == outgoing) */ { - if (Platform.isAndroid) { - await _callService.makeCall(callTo: event.endpoint); - } else if (Platform.isIOS) { - await _callKitService.startOutgoingCall(event.endpoint); + }; } } - yield state.copyWith( - callStatus: _makeStringFromCallState(_callService.callState), - endpointName: _callService?.endpoint?.displayName ?? - _callService?.endpoint?.userName ?? - event.endpoint ?? - '', - availableAudioDevices: _callService.availableAudioDevices, - activeAudioDevice: _callService.activeAudioDevice, - ); - } catch (e) { - add(CallChangedEvent(event: OnFailedCallEvent(reason: e.toString()))); - } - } else if (event is CallChangedEvent) { - CallEvent callEvent = event.event; - - if (callEvent is OnFailedCallEvent) { - _log('onFailed event'); - await _callKitService?.reportCallEnded( - reason: FCXCallEndedReason.failed, - ); - yield CallEndedActiveCallState( - reason: callEvent.reason, - failed: true, - endpointName: state.endpointName, - activeAudioDevice: state.activeAudioDevice, - ); - } else if (callEvent is OnDisconnectedCallEvent) { - _log('onDisconnected event'); - if (Platform.isIOS) { - await _callKitService?.reportCallEnded( - reason: FCXCallEndedReason.remoteEnded, - ); + } else /* if (direction == outgoing) */ { + if (Platform.isAndroid) { + await _callService.makeCall(callTo: _endpoint); + } else if (Platform.isIOS) { + await _callKitService?.startOutgoingCall(_endpoint); } - yield CallEndedActiveCallState( - reason: 'Disconnected', - failed: false, - endpointName: state.endpointName, - activeAudioDevice: state.activeAudioDevice, - ); - } else if (callEvent is OnHoldCallEvent) { - _log('onHold event'); - bool isOnHold = callEvent.hold; - String status = isOnHold ? 'Is on hold' : 'Connected'; - yield state.copyWith(isOnHold: isOnHold, callStatus: status); - } else if (callEvent is OnMuteCallEvent) { - _log('onMute event'); - yield state.copyWith(isMuted: callEvent.muted); - } else if (callEvent is OnConnectedCallEvent) { - _log('onConnected event'); - String name = callEvent.displayName ?? callEvent.username ?? ''; - yield state.copyWith(callStatus: 'Connected', endpointName: name); - await _callKitService?.reportConnected( - callEvent.username, - callEvent.displayName, - ); - } else if (callEvent is OnRingingCallEvent) { - _log('onRinging event'); - yield state.copyWith(callStatus: 'Ringing'); } - } else if (event is HoldPressedEvent) { + emit(state.copyWith( + callStatus: _makeStringFromCallState(_callService.callState), + endpointName: _callService.endpoint?.displayName ?? + _callService.endpoint?.userName ?? + _endpoint ?? + '', + availableAudioDevices: _callService.availableAudioDevices, + activeAudioDevice: _callService.activeAudioDevice, + )); + } catch (e) { + add(CallChangedEvent(OnFailedCallEvent(reason: e.toString()))); + } + } + + Future _handleCallChanged( + CallChangedEvent event, + Emitter emit) async { + CallEvent callEvent = event.event; + + if (callEvent is OnFailedCallEvent) { + _log('onFailed event'); + await _callKitService?.reportCallEnded( + reason: FCXCallEndedReason.failed, + ); + emit(CallEndedActiveCallState( + reason: callEvent.reason, + failed: true, + endpointName: state.endpointName, + activeAudioDevice: state.activeAudioDevice, + )); + } else if (callEvent is OnDisconnectedCallEvent) { + _log('onDisconnected event'); + await _callKitService?.reportCallEnded( + reason: FCXCallEndedReason.remoteEnded, + ); + emit(CallEndedActiveCallState( + reason: 'Disconnected', + failed: false, + endpointName: state.endpointName, + activeAudioDevice: state.activeAudioDevice, + )); + } else if (callEvent is OnHoldCallEvent) { + _log('onHold event'); + bool isOnHold = callEvent.hold; + String status = isOnHold ? 'Is on hold' : 'Connected'; + emit(state.copyWith(isOnHold: isOnHold, callStatus: status)); + } else if (callEvent is OnMuteCallEvent) { + _log('onMute event'); + emit(state.copyWith(isMuted: callEvent.muted)); + } else if (callEvent is OnConnectedCallEvent) { + _log('onConnected event'); + String name = callEvent.displayName ?? callEvent.username ?? ''; + emit(state.copyWith(callStatus: 'Connected', endpointName: name)); + await _callKitService?.reportConnected( + callEvent.username, + callEvent.displayName, + ); + } else if (callEvent is OnRingingCallEvent) { + _log('onRinging event'); + emit(state.copyWith(callStatus: 'Ringing')); + } + } + + Future _holdCall( + HoldPressedEvent event, + Emitter emit) async { Platform.isIOS - ? await _callKitService.holdCall(event.hold) - : await _callService.holdCall(hold: event.hold); - } else if (event is MutePressedEvent) { + ? await _callKitService?.holdCall(event.hold) + : await _callService.holdCall(hold: event.hold); + } + + Future _muteAudio( + MutePressedEvent event, + Emitter emit) async { Platform.isIOS - ? await _callKitService.muteCall(event.mute) - : await _callService.muteCall(mute: event.mute); - } else if (event is HangupPressedEvent) { + ? await _callKitService?.muteCall(event.mute) + : await _callService.muteCall(mute: event.mute); + } + + Future _hangupCall( + HangupPressedEvent event, + Emitter emit) async { Platform.isIOS - ? await _callKitService.endCall() - : await _callService.hangup(); - } else if (event is SelectAudioDevicePressedEvent) { - await _callService.selectAudioDevice(device: event.device); - } else if (event is AudioDevicesChanged) { - AudioDeviceEvent audioEvent = event.event; - if (audioEvent is OnActiveAudioDeviceChanged) { - yield state.copyWith(activeAudioDevice: audioEvent.device); - } else if (audioEvent is OnAvailableAudioDevicesListChanged) { - yield state.copyWith(availableAudioDevices: audioEvent.devices); - } + ? await _callKitService?.endCall() + : await _callService.hangup(); + } + + Future _selectAudioDevice( + SelectAudioDevicePressedEvent event, + Emitter emit) async { + await _callService.selectAudioDevice(device: event.device); + } + + Future _handleAudioDevicesChanged( + AudioDevicesChanged event, + Emitter emit) async { + AudioDeviceEvent audioEvent = event.event; + if (audioEvent is OnActiveAudioDeviceChanged) { + emit(state.copyWith(activeAudioDevice: audioEvent.device)); + } else if (audioEvent is OnAvailableAudioDevicesListChanged) { + emit(state.copyWith(availableAudioDevices: audioEvent.devices)); } } diff --git a/audio_call/lib/screens/active_call/bloc/active_call_event.dart b/audio_call/lib/screens/active_call/bloc/active_call_event.dart index cff074e..38e76a9 100644 --- a/audio_call/lib/screens/active_call/bloc/active_call_event.dart +++ b/audio_call/lib/screens/active_call/bloc/active_call_event.dart @@ -8,53 +8,36 @@ import 'package:flutter_voximplant/flutter_voximplant.dart'; abstract class ActiveCallEvent { } class ReadyToStartCallEvent implements ActiveCallEvent { - final bool isIncoming; - final String endpoint; - - ReadyToStartCallEvent({ - @required this.isIncoming, - @required this.endpoint, - }); + ReadyToStartCallEvent(); } class CallChangedEvent implements ActiveCallEvent { final CallEvent event; - - CallChangedEvent({ - @required this.event, - }); + CallChangedEvent(this.event); } class AudioDevicesChanged implements ActiveCallEvent { final AudioDeviceEvent event; - AudioDevicesChanged({ - @required this.event, - }); + AudioDevicesChanged(this.event); } class HoldPressedEvent implements ActiveCallEvent { final bool hold; - HoldPressedEvent({ - @required this.hold, - }); + HoldPressedEvent(this.hold); } class SelectAudioDevicePressedEvent implements ActiveCallEvent { final VIAudioDevice device; - SelectAudioDevicePressedEvent({ - @required this.device, - }); + SelectAudioDevicePressedEvent(this.device); } class MutePressedEvent implements ActiveCallEvent { final bool mute; - MutePressedEvent({ - @required this.mute, - }); + MutePressedEvent(this.mute); } class HangupPressedEvent implements ActiveCallEvent { } diff --git a/audio_call/lib/screens/active_call/bloc/active_call_state.dart b/audio_call/lib/screens/active_call/bloc/active_call_state.dart index e133eba..9a0dcbf 100644 --- a/audio_call/lib/screens/active_call/bloc/active_call_state.dart +++ b/audio_call/lib/screens/active_call/bloc/active_call_state.dart @@ -12,22 +12,22 @@ class ActiveCallState implements Equatable { final bool isOnHold; final bool isMuted; - ActiveCallState({ - @required this.endpointName, - @required this.callStatus, - @required this.activeAudioDevice, - @required this.availableAudioDevices, - @required this.isOnHold, - @required this.isMuted, + const ActiveCallState({ + required this.endpointName, + required this.callStatus, + required this.activeAudioDevice, + required this.availableAudioDevices, + required this.isOnHold, + required this.isMuted, }); ActiveCallState copyWith({ - String endpointName, - String callStatus, - VIAudioDevice activeAudioDevice, - List availableAudioDevices, - bool isOnHold, - bool isMuted, + String? endpointName, + String? callStatus, + VIAudioDevice? activeAudioDevice, + List? availableAudioDevices, + bool? isOnHold, + bool? isMuted, }) => ActiveCallState( endpointName: endpointName ?? this.endpointName, @@ -59,10 +59,10 @@ class CallEndedActiveCallState extends ActiveCallState { final String reason; CallEndedActiveCallState({ - @required this.reason, - @required this.failed, - @required endpointName, - @required activeAudioDevice, + required this.reason, + required this.failed, + required endpointName, + required activeAudioDevice, }) : super( endpointName: endpointName, callStatus: failed ? 'Failed' : 'Disconnected', diff --git a/audio_call/lib/screens/call_failed/call_failed_page.dart b/audio_call/lib/screens/call_failed/call_failed_page.dart index 9ee049c..8ecd5e0 100644 --- a/audio_call/lib/screens/call_failed/call_failed_page.dart +++ b/audio_call/lib/screens/call_failed/call_failed_page.dart @@ -25,27 +25,24 @@ class CallFailedPage extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - Container( - child: Column( - children: [ - Widgets.textWithPadding( - text: 'Call failed', - textColor: VoximplantColors.white, - fontSize: 40, - ), - Widgets.textWithPadding( - text: _endpoint, - textColor: VoximplantColors.white, - fontSize: 30, - verticalPadding: 30, - ), - Widgets.textWithPadding( - text: _failureReason, - textColor: VoximplantColors.white, - fontSize: 25, - ), - ], - ), + Column( + children: [ + Widgets.textWithPadding( + text: 'Call failed', + textColor: VoximplantColors.white, + fontSize: 40, + ), + Widgets.textWithPadding( + text: _endpoint, + textColor: VoximplantColors.white, + verticalPadding: 30, + ), + Widgets.textWithPadding( + text: _failureReason, + textColor: VoximplantColors.white, + fontSize: 25, + ), + ], ), Widgets.iconButton( icon: Icons.close, diff --git a/audio_call/lib/screens/call_failed/call_failed_page_arguments.dart b/audio_call/lib/screens/call_failed/call_failed_page_arguments.dart index bb4f7c7..402f9d0 100644 --- a/audio_call/lib/screens/call_failed/call_failed_page_arguments.dart +++ b/audio_call/lib/screens/call_failed/call_failed_page_arguments.dart @@ -7,7 +7,7 @@ class CallFailedPageArguments { final String endpoint; CallFailedPageArguments({ - @required this.failureReason, - @required this.endpoint, + required this.failureReason, + required this.endpoint, }); } diff --git a/audio_call/lib/screens/incoming_call/bloc/incoming_call_bloc.dart b/audio_call/lib/screens/incoming_call/bloc/incoming_call_bloc.dart index 15dce6b..178c249 100644 --- a/audio_call/lib/screens/incoming_call/bloc/incoming_call_bloc.dart +++ b/audio_call/lib/screens/incoming_call/bloc/incoming_call_bloc.dart @@ -12,43 +12,47 @@ import 'incoming_call_state.dart'; class IncomingCallBloc extends Bloc { final CallService _callService = CallService(); - StreamSubscription _callStateSubscription; + StreamSubscription? _callStateSubscription; IncomingCallBloc() : super(IncomingCallState.callIncoming) { _callStateSubscription = _callService.subscribeToCallEvents().listen(onCallEvent); + on(_checkPermissions); + on(_declineCall); + on(_callDisconnected); } @override - Stream mapEventToState(IncomingCallEvent event) async* { - switch (event) { - case IncomingCallEvent.checkPermissions: - bool granted = await checkPermissions(); - if (granted) { - yield IncomingCallState.permissionsGranted; - } - break; - case IncomingCallEvent.declineCall: - await _callService.decline(); - yield IncomingCallState.callCancelled; - break; - case IncomingCallEvent.callDisconnected: - yield IncomingCallState.callCancelled; - break; - } + Future close() { + _callStateSubscription?.cancel(); + return super.close(); } - @override - Future close() { - if (_callStateSubscription != null) { - _callStateSubscription.cancel(); + Future _checkPermissions( + CheckPermissions event, + Emitter emit) async { + bool granted = await checkPermissions(); + if (granted) { + emit(IncomingCallState.permissionsGranted); } - return super.close(); + } + + Future _declineCall( + DeclineCall event, + Emitter emit) async { + await _callService.decline(); + emit(IncomingCallState.callCancelled); + } + + Future _callDisconnected( + CallDisconnected event, + Emitter emit) async { + emit(IncomingCallState.callCancelled); } void onCallEvent(CallEvent event) { if (event is OnDisconnectedCallEvent || event is OnFailedCallEvent) { - add(IncomingCallEvent.callDisconnected); + add(CallDisconnected()); } } } diff --git a/audio_call/lib/screens/incoming_call/bloc/incoming_call_event.dart b/audio_call/lib/screens/incoming_call/bloc/incoming_call_event.dart index 36f1755..74e38e4 100644 --- a/audio_call/lib/screens/incoming_call/bloc/incoming_call_event.dart +++ b/audio_call/lib/screens/incoming_call/bloc/incoming_call_event.dart @@ -1,3 +1,16 @@ /// Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved. -enum IncomingCallEvent { checkPermissions, declineCall, callDisconnected } +abstract class IncomingCallEvent {} + +class CheckPermissions implements IncomingCallEvent { + CheckPermissions(); +} + +class DeclineCall implements IncomingCallEvent { + DeclineCall(); +} + +class CallDisconnected implements IncomingCallEvent { + CallDisconnected(); +} + diff --git a/audio_call/lib/screens/incoming_call/incoming_call_page.dart b/audio_call/lib/screens/incoming_call/incoming_call_page.dart index d81effc..221a299 100644 --- a/audio_call/lib/screens/incoming_call/incoming_call_page.dart +++ b/audio_call/lib/screens/incoming_call/incoming_call_page.dart @@ -16,7 +16,7 @@ class IncomingCallPage extends StatefulWidget { final String _endpoint; - IncomingCallPage({@required IncomingCallPageArguments arguments}) + IncomingCallPage({required IncomingCallPageArguments arguments}) : _endpoint = arguments.endpoint; @override @@ -26,7 +26,7 @@ class IncomingCallPage extends StatefulWidget { class _IncomingCallPageState extends State { final String _endpoint; - IncomingCallBloc _bloc; + late IncomingCallBloc _bloc; @override void initState() { @@ -37,9 +37,9 @@ class _IncomingCallPageState extends State { @override Widget build(BuildContext context) { - void _answerCall() => _bloc.add(IncomingCallEvent.checkPermissions); + void answerCall() => _bloc.add(CheckPermissions()); - void _declineCall() => _bloc.add(IncomingCallEvent.declineCall); + void declineCall() => _bloc.add(DeclineCall()); return BlocListener( listener: (context, state) { @@ -66,7 +66,6 @@ class _IncomingCallPageState extends State { Widgets.textWithPadding( text: 'Incoming call from', textColor: VoximplantColors.white, - fontSize: 30, verticalPadding: 20, ), Widgets.textWithPadding( @@ -75,7 +74,7 @@ class _IncomingCallPageState extends State { fontSize: 25, ), Padding( - padding: EdgeInsets.only(top: 80), + padding: const EdgeInsets.only(top: 80), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ @@ -83,13 +82,13 @@ class _IncomingCallPageState extends State { icon: Icons.call, color: VoximplantColors.button, tooltip: 'Answer', - onPressed: _answerCall, + onPressed: answerCall, ), Widgets.iconButton( icon: Icons.call_end, color: VoximplantColors.red, tooltip: 'Decline', - onPressed: _declineCall, + onPressed: declineCall, ) ], ), diff --git a/audio_call/lib/screens/incoming_call/incoming_call_page_arguments.dart b/audio_call/lib/screens/incoming_call/incoming_call_page_arguments.dart index 3c3e488..db4a4b8 100644 --- a/audio_call/lib/screens/incoming_call/incoming_call_page_arguments.dart +++ b/audio_call/lib/screens/incoming_call/incoming_call_page_arguments.dart @@ -2,5 +2,5 @@ class IncomingCallPageArguments { String endpoint; - IncomingCallPageArguments({this.endpoint}); + IncomingCallPageArguments({required this.endpoint}); } diff --git a/audio_call/lib/screens/login/bloc/login_bloc.dart b/audio_call/lib/screens/login/bloc/login_bloc.dart index d0e710f..f58a9f5 100644 --- a/audio_call/lib/screens/login/bloc/login_bloc.dart +++ b/audio_call/lib/screens/login/bloc/login_bloc.dart @@ -11,41 +11,47 @@ class LoginBloc extends Bloc { final AuthService _authService = AuthService(); LoginBloc() : super(LoginInitial()) { - add(LoadLastUser()); + on(_loadLastUser); + on(_loginWithPassword); } - @override - Stream mapEventToState(LoginEvent event) async* { - if (event is LoadLastUser) { - if (Platform.isAndroid && - await NotificationHelper().didNotificationLaunchApp()) { - print('Launched from notification, skipping autologin'); - return; - } - String lastUser = await _authService.getUsername(); - yield LoginLastUserLoaded(lastUser: lastUser); - bool canUseAccessToken = await _authService.canUseAccessToken(); - if (canUseAccessToken) { - yield LoginInProgress(); - try { - await _authService.loginWithAccessToken(); - yield LoginSuccess(); - } on VIException catch (e) { - yield LoginFailure(errorCode: e.code, errorDescription: e.message); - } - } + Future _loadLastUser( + LoadLastUser event, + Emitter emit) async { + // TODO(yulia) + // if (Platform.isAndroid && + // await NotificationHelper().didNotificationLaunchApp()) { + // print('Launched from notification, skipping autologin'); + // return; + // } + final lastUser = await _authService.getUsername(); + if (lastUser != null) { + emit(LoginLastUserLoaded(lastUser: lastUser)); } - if (event is LoginWithPassword) { - yield LoginInProgress(); + bool canUseAccessToken = await _authService.canUseAccessToken(); + if (canUseAccessToken) { + emit(LoginInProgress()); try { - await _authService.loginWithPassword( - event.username + '.voximplant.com', - event.password, - ); - yield LoginSuccess(); + await _authService.loginWithAccessToken(); + emit(LoginSuccess()); } on VIException catch (e) { - yield LoginFailure(errorCode: e.code, errorDescription: e.message); + emit(LoginFailure(errorCode: e.code, errorDescription: e.message ?? 'Unknown error')); } } } + + Future _loginWithPassword( + LoginWithPassword event, + Emitter emit) async { + emit(LoginInProgress()); + try { + await _authService.loginWithPassword( + '${event.username}.voximplant.com', + event.password, + ); + emit(LoginSuccess()); + } on VIException catch (e) { + emit(LoginFailure(errorCode: e.code, errorDescription: e.message ?? 'Unknown error')); + } + } } diff --git a/audio_call/lib/screens/login/bloc/login_event.dart b/audio_call/lib/screens/login/bloc/login_event.dart index c90441f..1b646ba 100644 --- a/audio_call/lib/screens/login/bloc/login_event.dart +++ b/audio_call/lib/screens/login/bloc/login_event.dart @@ -15,7 +15,7 @@ class LoginWithPassword extends LoginEvent { final String username; final String password; - LoginWithPassword({@required this.username, @required this.password}); + const LoginWithPassword({required this.username, required this.password}); @override List get props => [username, password]; diff --git a/audio_call/lib/screens/login/bloc/login_state.dart b/audio_call/lib/screens/login/bloc/login_state.dart index a7ccdfa..a113ac5 100644 --- a/audio_call/lib/screens/login/bloc/login_state.dart +++ b/audio_call/lib/screens/login/bloc/login_state.dart @@ -16,7 +16,7 @@ class LoginInitial extends LoginState {} class LoginLastUserLoaded extends LoginState { final String lastUser; - const LoginLastUserLoaded({@required this.lastUser}); + const LoginLastUserLoaded({required this.lastUser}); @override List get props => [lastUser]; @@ -31,10 +31,10 @@ class LoginFailure extends LoginState { final String errorDescription; const LoginFailure({ - @required this.errorCode, - @required this.errorDescription, + required this.errorCode, + required this.errorDescription, }); @override List get props => [errorCode, errorDescription]; -} \ No newline at end of file +} diff --git a/audio_call/lib/screens/login/login_page.dart b/audio_call/lib/screens/login/login_page.dart index b07ef7c..fd4db6c 100644 --- a/audio_call/lib/screens/login/login_page.dart +++ b/audio_call/lib/screens/login/login_page.dart @@ -18,7 +18,7 @@ class _LoginPageState extends State { final _passwordController = TextEditingController(); final _formKey = GlobalKey(); - LoginBloc _bloc; + late LoginBloc _bloc; bool _isUsernameValid = true; bool _isPasswordValid = true; @@ -27,6 +27,7 @@ class _LoginPageState extends State { void initState() { super.initState(); _bloc = BlocProvider.of(context); + context.read().add(LoadLastUser()); } @override @@ -38,29 +39,29 @@ class _LoginPageState extends State { @override Widget build(BuildContext context) { - void _login() => _bloc.add( + void login() => _bloc.add( LoginWithPassword( username: _usernameController.text, password: _passwordController.text, ), ); - void _handleLoginFailed(String errorCode, String errorDescription) { + void handleLoginFailed(String errorCode, String errorDescription) { if (errorCode == 'ERROR_INVALID_USERNAME') { setState(() => _isUsernameValid = false); } else if (errorCode == 'ERROR_INVALID_PASSWORD') { setState(() => _isPasswordValid = false); } else { - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('$errorDescription'), + content: Text(errorDescription), backgroundColor: Colors.red, ), ); } } - Widget _loginForm() { + Widget loginForm() { return Center( child: Form( key: _formKey, @@ -71,7 +72,6 @@ class _LoginPageState extends State { children: [ Widgets.textWithPadding( text: 'Audio call', - fontSize: 30, textColor: VoximplantColors.white, verticalPadding: 30, ), @@ -94,7 +94,7 @@ class _LoginPageState extends State { ), Widgets.maxWidthRaisedButton( text: 'Log in', - onPressed: _login, + onPressed: login, ), ], ), @@ -103,8 +103,8 @@ class _LoginPageState extends State { ); } - Widget _loginInProgress() { - return Center( + Widget loginInProgress() { + return const Center( child: CircularProgressIndicator(), ); } @@ -115,7 +115,7 @@ class _LoginPageState extends State { _usernameController.text = state.lastUser; } if (state is LoginFailure) { - _handleLoginFailed(state.errorCode, state.errorDescription); + handleLoginFailed(state.errorCode, state.errorDescription); } if (state is LoginSuccess) { setState(() { @@ -126,8 +126,8 @@ class _LoginPageState extends State { } if (state is! LoginInProgress && state is! LoginSuccess) { Future.delayed( - Duration(milliseconds: 100), - () => _formKey?.currentState?.validate(), + const Duration(milliseconds: 100), + () => _formKey.currentState?.validate(), ); } }, @@ -137,8 +137,8 @@ class _LoginPageState extends State { backgroundColor: VoximplantColors.primary, body: SafeArea( child: (state is LoginInProgress || state is LoginSuccess) - ? _loginInProgress() - : _loginForm(), + ? loginInProgress() + : loginForm(), ), ); }, diff --git a/audio_call/lib/screens/main/bloc/main_bloc.dart b/audio_call/lib/screens/main/bloc/main_bloc.dart index 0575298..33dc62f 100644 --- a/audio_call/lib/screens/main/bloc/main_bloc.dart +++ b/audio_call/lib/screens/main/bloc/main_bloc.dart @@ -15,56 +15,73 @@ import 'package:flutter_voximplant/flutter_voximplant.dart'; class MainBloc extends Bloc { final AuthService _authService = AuthService(); final CallService _callService = CallService(); - final CallKitService _callKitService = + final CallKitService? _callKitService = Platform.isIOS ? CallKitService() : null; - StreamSubscription _callStateSubscription; + late StreamSubscription _callStateSubscription; - MainBloc() : super(MainInitial(myDisplayName: AuthService().displayName)) { + MainBloc() : super(MainInitial(myDisplayName: AuthService().displayName ?? 'Unknown user')) { _authService.onDisconnected = () => add(ConnectionClosed()); _callStateSubscription = _callService.subscribeToCallEvents().listen(onCallEvent); + on(_checkPermissions); + on(_logout); + on(_connectionClosed); + on(_reconnect); + on(_receivedIncomingCall); } @override Future close() { - if (_callStateSubscription != null) { - _callStateSubscription.cancel(); - } + _callStateSubscription.cancel(); return super.close(); } - @override - Stream mapEventToState(MainEvent event) async* { - if (event is CheckPermissionsForCall) { - yield await checkPermissions() - ? PermissionCheckSuccess(myDisplayName: _authService.displayName) - : PermissionCheckFail(myDisplayName: _authService.displayName); - } - if (event is LogOut) { - await _authService.logout(); - yield LoggedOut(networkIssues: false); - } - if (event is ReceivedIncomingCall) { - yield IncomingCall( - caller: event.displayName, - myDisplayName: _authService.displayName, - ); + Future _checkPermissions( + CheckPermissionsForCall event, + Emitter emit) async { + bool permissionsGranted = await checkPermissions(); + if (permissionsGranted) { + emit(PermissionCheckSuccess(myDisplayName: _authService.displayName ?? 'Unknown user')); + } else { + emit(PermissionCheckFail(myDisplayName: _authService.displayName ?? 'Unknown user')); } - if (event is ConnectionClosed) { - yield LoggedOut(networkIssues: true); - } - if (event is Reconnect) { - try { - String displayName = await _authService.loginWithAccessToken(); - if (displayName == null) { - return; - } - yield ReconnectSuccess(myDisplayName: displayName); - } on VIException { - _authService.onDisconnected = null; - yield ReconnectFailed(); + } + + Future _logout( + LogOut event, + Emitter emit) async { + await _authService.logout(); + emit(const LoggedOut(networkIssues: false)); + } + + Future _connectionClosed( + ConnectionClosed event, + Emitter emit) async { + emit(const LoggedOut(networkIssues: true)); + } + + void _receivedIncomingCall( + ReceivedIncomingCall event, + Emitter emit) { + emit(IncomingCall( + caller: event.displayName, + myDisplayName: _authService.displayName ?? 'Unknown user', + )); + } + + Future _reconnect( + Reconnect event, + Emitter emit) async { + try { + final displayName = await _authService.loginWithAccessToken(); + if (displayName == null) { + return; } + emit(ReconnectSuccess(myDisplayName: displayName)); + } on VIException { + _authService.onDisconnected = null; + emit(const ReconnectFailed()); } } @@ -75,15 +92,18 @@ class MainBloc extends Bloc { ); } else if (event is OnIncomingCallEvent) { if (Platform.isIOS) { - await _callKitService.createIncomingCall( - _callService.callKitUUID, - event.username, - event.displayName, - ); + final callKitUUID = _callService.callKitUUID; + if (callKitUUID != null) { + await _callKitService?.createIncomingCall( + callKitUUID, + event.username, + event.displayName, + ); + } } else if (Platform.isAndroid) { add( ReceivedIncomingCall( - displayName: event.displayName ?? event.username, + displayName: event.displayName, ), ); } diff --git a/audio_call/lib/screens/main/bloc/main_event.dart b/audio_call/lib/screens/main/bloc/main_event.dart index 0393743..3c2d1a6 100644 --- a/audio_call/lib/screens/main/bloc/main_event.dart +++ b/audio_call/lib/screens/main/bloc/main_event.dart @@ -17,7 +17,7 @@ class LogOut extends MainEvent {} class ReceivedIncomingCall extends MainEvent { final String displayName; - ReceivedIncomingCall({@required this.displayName}); + ReceivedIncomingCall({required this.displayName}); @override List get props => [displayName]; @@ -25,4 +25,4 @@ class ReceivedIncomingCall extends MainEvent { class ConnectionClosed extends MainEvent {} -class Reconnect extends MainEvent {} \ No newline at end of file +class Reconnect extends MainEvent {} diff --git a/audio_call/lib/screens/main/bloc/main_state.dart b/audio_call/lib/screens/main/bloc/main_state.dart index 33fb013..f212e16 100644 --- a/audio_call/lib/screens/main/bloc/main_state.dart +++ b/audio_call/lib/screens/main/bloc/main_state.dart @@ -15,36 +15,36 @@ abstract class MainState extends Equatable { } class MainInitial extends MainState { - const MainInitial({@required String myDisplayName}) : super(myDisplayName); + const MainInitial({required String myDisplayName}) : super(myDisplayName); } class PermissionCheckFail extends MainState { - const PermissionCheckFail({@required String myDisplayName}) + const PermissionCheckFail({required String myDisplayName}) : super(myDisplayName); } class PermissionCheckSuccess extends MainState { - const PermissionCheckSuccess({@required String myDisplayName}) + const PermissionCheckSuccess({required String myDisplayName}) : super(myDisplayName); } class LoggedOut extends MainState { final bool networkIssues; - const LoggedOut({@required this.networkIssues}) : super(null); + const LoggedOut({required this.networkIssues}) : super(""); } class IncomingCall extends MainState { final String caller; - const IncomingCall({@required this.caller, @required String myDisplayName}) + const IncomingCall({required this.caller, required String myDisplayName}) : super(myDisplayName); } class ReconnectSuccess extends MainState { - const ReconnectSuccess({@required String myDisplayName}) + const ReconnectSuccess({required String myDisplayName}) : super(myDisplayName); } class ReconnectFailed extends MainState { - const ReconnectFailed() : super(null); + const ReconnectFailed() : super(""); } diff --git a/audio_call/lib/screens/main/main_page.dart b/audio_call/lib/screens/main/main_page.dart index 404a82f..bc5532a 100644 --- a/audio_call/lib/screens/main/main_page.dart +++ b/audio_call/lib/screens/main/main_page.dart @@ -20,7 +20,7 @@ class MainPage extends StatefulWidget { } class _MainPageState extends State with WidgetsBindingObserver { - MainBloc _bloc; + late MainBloc _bloc; final _callToController = TextEditingController(); @@ -58,26 +58,26 @@ class _MainPageState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { - void _makeCall() { + void makeCall() { if (_callToController.text == '') { return; } _bloc.add(CheckPermissionsForCall()); } - void _logout() => _bloc.add(LogOut()); + void logout() => _bloc.add(LogOut()); - void _showPermissionCheckError() { + void showPermissionCheckError() { showDialog( context: context, builder: (context) { return AlertDialog( - title: Text('Permissions missing'), + title: const Text('Permissions missing'), content: - Text('Please give "record audio" permissions to make calls'), + const Text('Please give "record audio" permissions to make calls'), actions: [ - FlatButton( - child: Text('Close'), + TextButton( + child: const Text('Close'), onPressed: () { Navigator.of(context).pop(); }, @@ -109,7 +109,7 @@ class _MainPageState extends State with WidgetsBindingObserver { ); } if (state is PermissionCheckFail) { - _showPermissionCheckError(); + showPermissionCheckError(); } if (state is IncomingCall) { Navigator.of(context).pushReplacementNamed( @@ -125,11 +125,11 @@ class _MainPageState extends State with WidgetsBindingObserver { builder: (context, state) { return Scaffold( appBar: AppBar( - title: Text('Voximplant'), + title: const Text('Voximplant'), actions: [ IconButton( - icon: Icon(Icons.exit_to_app), - onPressed: _logout, + icon: const Icon(Icons.exit_to_app), + onPressed: logout, ) ], ), @@ -148,7 +148,7 @@ class _MainPageState extends State with WidgetsBindingObserver { labelText: 'user or number'), Widgets.maxWidthRaisedButton( text: 'Call', - onPressed: _makeCall, + onPressed: makeCall, ), ], ), @@ -157,10 +157,9 @@ class _MainPageState extends State with WidgetsBindingObserver { Align( alignment: Alignment.bottomCenter, child: Padding( - padding: EdgeInsets.only(bottom: 20), + padding: const EdgeInsets.only(bottom: 20), child: Text( - state.myDisplayName != null && - state.myDisplayName.isNotEmpty + state.myDisplayName.isNotEmpty ? 'Logged in as ${state.myDisplayName}' : '', ), diff --git a/audio_call/lib/services/auth_service.dart b/audio_call/lib/services/auth_service.dart index f9b2858..f41dcd4 100644 --- a/audio_call/lib/services/auth_service.dart +++ b/audio_call/lib/services/auth_service.dart @@ -4,59 +4,69 @@ import 'package:audio_call/utils/log.dart'; import 'package:flutter_voximplant/flutter_voximplant.dart'; import 'package:shared_preferences/shared_preferences.dart'; -typedef void Disconnected(); +typedef Disconnected = void Function(); class AuthService { VIClient _client; - String _displayName; + String? _displayName; - String get displayName => _displayName; - Disconnected onDisconnected; + String? get displayName => _displayName; + Disconnected? onDisconnected; - String _voipToken; + String? _voipToken; set voipToken(token) { if (token == null || token == '') { _log('voip token cleared'); - _client.unregisterFromPushNotifications(_voipToken); + final voipToken = _voipToken; + if (voipToken != null) { + _client.unregisterFromPushNotifications(voipToken); + } } _voipToken = token; } VIClientState clientState = VIClientState.Disconnected; - factory AuthService() => _cache ?? AuthService._(); - static AuthService _cache; + factory AuthService() { + return _instance; + } + static final AuthService _instance = AuthService._(); AuthService._() : _client = Voximplant().getClient(defaultConfig) { _log('initialize'); _client.clientStateStream.listen((state) { clientState = state; _log('client state is changed to: $state'); if (state == VIClientState.Disconnected && onDisconnected != null) { - onDisconnected(); + onDisconnected?.call(); } }); - _cache = this; } Future loginWithPassword(String username, String password) async { _log('loginWithPassword'); VIClientState clientState = await _client.getClientState(); if (clientState == VIClientState.LoggedIn) { - return _displayName; + final displayName = _displayName; + if (displayName != null) { + return displayName; + } } if (clientState == VIClientState.Disconnected) { await _client.connect(); } VIAuthResult authResult = await _client.login(username, password); if (_voipToken != null) { - await _client.registerForPushNotifications(_voipToken); + final voipToken = _voipToken; + if (voipToken != null) { + await _client.registerForPushNotifications(voipToken); + } } await _saveAuthDetails(username, authResult.loginTokens); _displayName = authResult.displayName; - return _displayName; + return _displayName ?? "Unknown user"; } - Future loginWithAccessToken([String username]) async { + Future loginWithAccessToken() async { VIClientState clientState = await _client.getClientState(); if (clientState == VIClientState.LoggedIn) { return _displayName; @@ -68,27 +78,32 @@ class AuthService { } _log('loginWithAccessToken'); SharedPreferences prefs = await SharedPreferences.getInstance(); - VILoginTokens loginTokens = _getAuthDetails(prefs); - String user = username ?? prefs.getString('username'); - - VIAuthResult authResult = - await _client.loginWithAccessToken(user, loginTokens.accessToken); - if (_voipToken != null) { - await _client.registerForPushNotifications(_voipToken); + final loginTokens = _getAuthDetails(prefs); + String? user = prefs.getString('username'); + if (user != null && loginTokens != null) { + VIAuthResult authResult = await _client.loginWithAccessToken(user, loginTokens.accessToken); + await _saveAuthDetails(user, authResult.loginTokens); + _displayName = authResult.displayName; + } + final voipToken = _voipToken; + if (voipToken != null) { + await _client.registerForPushNotifications(voipToken); } - await _saveAuthDetails(user, authResult.loginTokens); - _displayName = authResult.displayName; return _displayName; } Future logout() async { _log('logout'); await _client.disconnect(); - VILoginTokens loginTokens = VILoginTokens(); - _saveAuthDetails(null, loginTokens); + final SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.remove('username'); + prefs.remove('accessToken'); + prefs.remove('refreshToken'); + prefs.remove('accessExpire'); + prefs.remove('refreshExpire'); } - Future getUsername() async { + Future getUsername() async { SharedPreferences prefs = await SharedPreferences.getInstance(); return prefs.getString('username')?.replaceAll('.voximplant.com', ''); } @@ -99,7 +114,10 @@ class AuthService { } Future _saveAuthDetails( - String username, VILoginTokens loginTokens) async { + String username, VILoginTokens? loginTokens) async { + if (loginTokens == null) { + return; + } final SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setString('username', username); prefs.setString('accessToken', loginTokens.accessToken); @@ -108,14 +126,21 @@ class AuthService { prefs.setInt('refreshExpire', loginTokens.refreshExpire); } - VILoginTokens _getAuthDetails(SharedPreferences prefs) { - VILoginTokens loginTokens = VILoginTokens(); - loginTokens.accessToken = prefs.getString('accessToken'); - loginTokens.accessExpire = prefs.getInt('accessExpire'); - loginTokens.refreshExpire = prefs.getInt('refreshExpire'); - loginTokens.refreshToken = prefs.getString('refreshToken'); - - return loginTokens; + VILoginTokens? _getAuthDetails(SharedPreferences prefs) { + final accessToken = prefs.getString('accessToken'); + final refreshToken = prefs.getString('refreshToken'); + final refreshExpire = prefs.getInt('refreshExpire'); + final accessExpire = prefs.getInt('accessExpire'); + if (accessToken != null && refreshToken != null && refreshExpire != null && accessExpire != null) { + VILoginTokens loginTokens = VILoginTokens( + accessToken: accessToken, + refreshToken: refreshToken, + accessExpire: accessExpire, + refreshExpire: refreshExpire + ); + return loginTokens; + } + return null; } Future pushNotificationReceived(Map payload) async { diff --git a/audio_call/lib/services/call/audio_device_event.dart b/audio_call/lib/services/call/audio_device_event.dart index 38283f3..66cd4d4 100644 --- a/audio_call/lib/services/call/audio_device_event.dart +++ b/audio_call/lib/services/call/audio_device_event.dart @@ -9,7 +9,7 @@ class OnActiveAudioDeviceChanged implements AudioDeviceEvent { final VIAudioDevice device; OnActiveAudioDeviceChanged({ - @required this.device, + required this.device, }); } @@ -17,6 +17,6 @@ class OnAvailableAudioDevicesListChanged implements AudioDeviceEvent { final List devices; OnAvailableAudioDevicesListChanged({ - @required this.devices, + required this.devices, }); } diff --git a/audio_call/lib/services/call/call_event.dart b/audio_call/lib/services/call/call_event.dart index 3caf78b..fb37b79 100644 --- a/audio_call/lib/services/call/call_event.dart +++ b/audio_call/lib/services/call/call_event.dart @@ -9,8 +9,8 @@ class OnIncomingCallEvent implements CallEvent { final String displayName; OnIncomingCallEvent({ - @required this.username, - @required this.displayName, + required this.username, + required this.displayName, }); } @@ -21,8 +21,8 @@ class OnConnectedCallEvent implements CallEvent { final String displayName; OnConnectedCallEvent({ - @required this.username, - @required this.displayName, + required this.username, + required this.displayName, }); } @@ -30,7 +30,7 @@ class OnDisconnectedCallEvent implements CallEvent { final bool answeredElsewhere; OnDisconnectedCallEvent({ - @required this.answeredElsewhere, + required this.answeredElsewhere, }); } @@ -38,7 +38,7 @@ class OnFailedCallEvent implements CallEvent { final String reason; OnFailedCallEvent({ - @required this.reason, + required this.reason, }); } @@ -46,7 +46,7 @@ class OnHoldCallEvent implements CallEvent { final bool hold; OnHoldCallEvent({ - @required this.hold, + required this.hold, }); } @@ -54,6 +54,6 @@ class OnMuteCallEvent implements CallEvent { final bool muted; OnMuteCallEvent({ - @required this.muted, + required this.muted, }); -} \ No newline at end of file +} diff --git a/audio_call/lib/services/call/call_service.dart b/audio_call/lib/services/call/call_service.dart index d980257..0474897 100644 --- a/audio_call/lib/services/call/call_service.dart +++ b/audio_call/lib/services/call/call_service.dart @@ -10,36 +10,36 @@ import 'package:audio_call/utils/notification_helper.dart'; import 'package:flutter_voximplant/flutter_voximplant.dart'; import 'package:meta/meta.dart'; -enum CallState { connecting, ringing, connected, ended } +enum CallState { none, connecting, ringing, connected, ended } class CallService { final VIClient _client; final VIAudioDeviceManager _audioDeviceManager; - VICall _activeCall; + VICall? _activeCall; bool get hasActiveCall => _activeCall != null; bool get hasNoActiveCalls => _activeCall == null; - String get callKitUUID => _activeCall?.callKitUUID; - set callKitUUID(String uuid) => _activeCall?.callKitUUID = uuid; + String? get callKitUUID => _activeCall?.callKitUUID; + set callKitUUID(String? uuid) => _activeCall?.callKitUUID = uuid; - Function onIncomingCall; + Function? onIncomingCall; - StreamController _callStreamController; - StreamController _audioDeviceStreamController; + StreamController _callStreamController = StreamController.broadcast(); + StreamController _audioDeviceStreamController = StreamController.broadcast(); CallState get callState => _callState; - CallState _callState; + CallState _callState = CallState.none; - VIEndpoint get endpoint => _endpoint; - VIEndpoint _endpoint; + VIEndpoint? get endpoint => _endpoint; + VIEndpoint? _endpoint; - VIAudioDevice get activeAudioDevice => __activeAudioDevice; - VIAudioDevice __activeAudioDevice; + VIAudioDevice? get activeAudioDevice => __activeAudioDevice; + VIAudioDevice? __activeAudioDevice; set _activeAudioDevice(VIAudioDevice device) { _log('onAudioDeviceChanged'); __activeAudioDevice = device; - _audioDeviceStreamController?.add( + _audioDeviceStreamController.add( OnActiveAudioDeviceChanged( device: device, ), @@ -47,11 +47,11 @@ class CallService { } get availableAudioDevices => __availableAudioDevices; - List __availableAudioDevices; + late List __availableAudioDevices; set _availableAudioDevices(List devices) { _log('onAudioDeviceListChanged'); __availableAudioDevices = devices; - _audioDeviceStreamController?.add( + _audioDeviceStreamController.add( OnAvailableAudioDevicesListChanged( devices: devices, ), @@ -63,9 +63,10 @@ class CallService { final VIAudioFile _progressTone; - factory CallService() => _cache ?? CallService._(); - static CallService _cache; - + factory CallService() { + return _instance; + } + static final CallService _instance = CallService._(); CallService._() : _client = Voximplant().getClient(defaultConfig), _audioDeviceManager = Voximplant().audioDeviceManager, @@ -79,7 +80,6 @@ class CallService { _client.onPushDidExpire = _pushDidExpire; _configureAudioDevices(); _configureAudioFiles(); - _cache = this; } void _configureAudioFiles() async { @@ -109,18 +109,18 @@ class CallService { } Stream subscribeToCallEvents() { - _callStreamController?.close(); + _callStreamController.close(); _callStreamController = StreamController.broadcast(); - return _callStreamController?.stream; + return _callStreamController.stream; } Stream subscribeToAudioDeviceEvents() { - _audioDeviceStreamController?.close(); + _audioDeviceStreamController.close(); _audioDeviceStreamController = StreamController.broadcast(); - return _audioDeviceStreamController?.stream; + return _audioDeviceStreamController.stream; } - Future makeCall({@required String callTo}) async { + Future makeCall({required String callTo}) async { if (hasActiveCall) { throw ('There is already an active call'); } @@ -137,50 +137,50 @@ class CallService { } VICallSettings callSettings = VICallSettings(); callSettings.videoFlags = _defaultFlags; - await _activeCall.answer(settings: callSettings); + await _activeCall?.answer(settings: callSettings); } Future hangup() async { if (hasNoActiveCalls) { throw 'Tried to hangup having no active call'; } - await _activeCall.hangup(); + await _activeCall?.hangup(); } Future decline() async { if (hasNoActiveCalls) { throw 'Tried to decline having no active call'; } - await _activeCall.decline(); + await _activeCall?.decline(); } - Future muteCall({@required bool mute}) async { + Future muteCall({required bool mute}) async { if (hasNoActiveCalls) { throw 'Tried to mute having no active call'; } - await _activeCall.sendAudio(!mute); - _callStreamController?.add(OnMuteCallEvent(muted: mute)); + await _activeCall?.sendAudio(!mute); + _callStreamController.add(OnMuteCallEvent(muted: mute)); } - Future holdCall({@required bool hold}) async { + Future holdCall({required bool hold}) async { if (hasNoActiveCalls) { throw 'Tried to hold having no active call'; } - await _activeCall.hold(hold); - _callStreamController?.add(OnHoldCallEvent(hold: hold)); + await _activeCall?.hold(hold); + _callStreamController.add(OnHoldCallEvent(hold: hold)); } - Future selectAudioDevice({@required VIAudioDevice device}) async => + Future selectAudioDevice({required VIAudioDevice device}) async => await _audioDeviceManager.selectAudioDevice(device); Future _onIncomingCall( VIClient client, VICall call, bool video, - Map headers, + Map? headers, ) async { _log('_onIncomingCall'); - if (hasActiveCall && _activeCall.callId != call.callId) { + if (hasActiveCall && _activeCall?.callId != call.callId) { await call.reject(); return; } @@ -188,14 +188,14 @@ class CallService { _callState = CallState.connecting; _endpoint = call.endpoints.first; _listenToActiveCallEvents(); - _callStreamController?.add( + _callStreamController.add( OnIncomingCallEvent( - username: call?.endpoints?.first?.userName, - displayName: call?.endpoints?.first?.displayName, + username: call.endpoints.first.userName ?? 'Unknown user', + displayName: call.endpoints.first.displayName ?? 'Unknown user', ), ); if (onIncomingCall != null) { - onIncomingCall(); + onIncomingCall?.call(); onIncomingCall = null; } } @@ -209,22 +209,22 @@ class CallService { } void _pushDidExpire(VIClient client, String callKitUUID) { - _callStreamController?.add( + _callStreamController.add( OnDisconnectedCallEvent(answeredElsewhere: false), ); } void _onCallDisconnected( VICall call, - Map headers, + Map? headers, bool answeredElsewhere, ) async { _log('onCallDisconnected'); - if (call.callId == _activeCall.callId) { + if (call.callId == _activeCall?.callId) { _activeCall = null; _callState = CallState.ended; _endpoint = null; - _callStreamController?.add( + _callStreamController.add( OnDisconnectedCallEvent(answeredElsewhere: answeredElsewhere), ); if (Platform.isAndroid) { @@ -242,14 +242,14 @@ class CallService { VICall call, int code, String description, - Map headers, + Map? headers, ) async { _log('onCallFailed($code, $description)'); if (call.callId == _activeCall?.callId) { _activeCall = null; _callState = CallState.ended; _endpoint = null; - _callStreamController?.add(OnFailedCallEvent(reason: description)); + _callStreamController.add(OnFailedCallEvent(reason: description)); try { await _progressTone.stop(); } catch (e) { @@ -258,26 +258,26 @@ class CallService { } } - void _onCallConnected(VICall call, Map headers) async { + void _onCallConnected(VICall call, Map? headers) async { _log('_onCallConnected'); if (call.callId == _activeCall?.callId) { _activeCall = call; _callState = CallState.connected; _endpoint = call.endpoints.first; - _callStreamController?.add( + _callStreamController.add( OnConnectedCallEvent( - username: call.endpoints?.first?.userName, - displayName: call.endpoints?.first?.displayName, + username: call.endpoints.first.userName ?? 'Unknown user', + displayName: call.endpoints.first.displayName ?? 'Unknown user', ), ); } } - void _onCallRinging(VICall call, Map headers) async { + void _onCallRinging(VICall call, Map? headers) async { _log('_onCallRinging'); - if (call.callId == _activeCall.callId) { + if (call.callId == _activeCall?.callId) { _callState = CallState.ringing; - _callStreamController?.add(OnRingingCallEvent()); + _callStreamController.add(OnRingingCallEvent()); try { await _progressTone.play(true); } catch (e) { diff --git a/audio_call/lib/services/call/callkit_service.dart b/audio_call/lib/services/call/callkit_service.dart index 8a9b206..f8863b5 100644 --- a/audio_call/lib/services/call/callkit_service.dart +++ b/audio_call/lib/services/call/callkit_service.dart @@ -10,27 +10,27 @@ import 'package:flutter_voximplant/flutter_voximplant.dart'; import 'package:uuid/uuid.dart'; // to generate uuids for callKit -var _uuid = Uuid(); +var _uuid = const Uuid(); // to remember a call even when only uuid available class CallWrapper { final String uuid; - FCXCall call; + FCXCall? call; CallWrapper(this.uuid); - CallWrapper.withCall(this.call) : this.uuid = call.uuid; + CallWrapper.withCall(FCXCall this.call) : uuid = call.uuid; } class CallKitService { - final AuthService _authService; - final CallService _callService; + final AuthService _authService = AuthService(); + final CallService _callService = CallService(); - final FCXPlugin _plugin; - final FCXProvider _provider; - final FCXCallController _callController; + final FCXPlugin _plugin = FCXPlugin(); + final FCXProvider _provider = FCXProvider(); + final FCXCallController _callController = FCXCallController(); - CallWrapper _activeCall; + CallWrapper? _activeCall; bool get _hasActiveCall => _activeCall != null; @@ -39,24 +39,21 @@ class CallKitService { bool _callStarting = false; // To handle late push, which call already been ended - List _endedCalls = []; + final List _endedCalls = []; bool _alreadyEnded(String uuid) => _endedCalls.contains(uuid); - factory CallKitService() => _cache ?? CallKitService._(); - static CallKitService _cache; + factory CallKitService() { + return _instance; + } + + static final CallKitService _instance = CallKitService._(); - CallKitService._() - : this._authService = AuthService(), - this._callService = CallService(), - this._plugin = FCXPlugin(), - this._provider = FCXProvider(), - this._callController = FCXCallController() { + CallKitService._() { _configure(); - _cache = this; _callService.subscribeToCallEvents().listen((event) { if (event is OnDisconnectedCallEvent && _hasActiveCall) { - _reportEnded(_activeCall.uuid, FCXCallEndedReason.declinedElsewhere); + _reportEnded(_activeCall?.uuid, FCXCallEndedReason.declinedElsewhere); } }); } @@ -68,7 +65,7 @@ class CallKitService { } try { if (_hasActiveCall) { - if (_activeCall.uuid != uuid) { + if (_activeCall?.uuid != uuid) { await _reportEnded(uuid, FCXCallEndedReason.failed); } return; @@ -85,7 +82,7 @@ class CallKitService { _provider.performStartCallAction = (startCallAction) async { if (_hasActiveCall) { - if (_activeCall.uuid != startCallAction.callUuid) { + if (_activeCall?.uuid != startCallAction.callUuid) { startCallAction.fail(); return; } @@ -124,8 +121,11 @@ class CallKitService { await _commitTransactions(); } catch (e) { _log('There was an error $e, failing'); - await _provider.reportCallEnded( - _callService.callKitUUID, null, FCXCallEndedReason.failed); + final callKitUUID = _callService.callKitUUID; + if (callKitUUID != null) { + await _provider.reportCallEnded( + callKitUUID, null, FCXCallEndedReason.failed); + } } } loginAndCommitTransactions(); @@ -141,7 +141,7 @@ class CallKitService { return; } - Future _end() async { + Future end() async { try { (_activeCall?.call?.outgoing ?? false) || (_activeCall?.call?.hasConnected ?? false) @@ -158,10 +158,10 @@ class CallKitService { // if already received call via Voximplant if (_callService.hasActiveCall) { - await _end(); + await end(); // else should wait till Voximplant send onIncomingCall } else { - _callService.onIncomingCall = () async => await _end(); + _callService.onIncomingCall = () async => await end(); } }; @@ -172,9 +172,9 @@ class CallKitService { return; } - NavigationHelper.pushToActiveCall(isIncoming: true, callTo: null); + NavigationHelper.pushToActiveCall(isIncoming: true, callTo: ""); - Future _answer() async { + Future answer() async { try { await Voximplant().audioDeviceManager.callKitConfigureAudioSession(); await _callService.answerCall(); @@ -187,10 +187,10 @@ class CallKitService { // if already received call via Voximplant if (_callService.hasActiveCall) { - await _answer(); + await answer(); // else should wait till Voximplant send onIncomingCall } else { - _callService.onIncomingCall = () async => await _answer(); + _callService.onIncomingCall = () async => await answer(); } }; @@ -244,8 +244,8 @@ class CallKitService { return; } - if (call.uuid == _activeCall.uuid) { - _activeCall.call = call; + if (call.uuid == _activeCall?.uuid) { + _activeCall?.call = call; } else { await _reportEnded(call.uuid, FCXCallEndedReason.failed); } @@ -271,25 +271,46 @@ class CallKitService { List actions = await transaction.getActions(); for (final action in actions) { if (action is FCXStartCallAction) { - _provider.performStartCallAction(action); + final perform = _provider.performStartCallAction; + if (perform != null) { + perform(action); + } } if (action is FCXAnswerCallAction) { - _provider.performAnswerCallAction(action); + final perform = _provider.performAnswerCallAction; + if (perform != null) { + perform(action); + } } if (action is FCXEndCallAction) { - _provider.performEndCallAction(action); + final perform = _provider.performEndCallAction; + if (perform != null) { + perform(action); + } } if (action is FCXSetHeldCallAction) { - _provider.performSetHeldCallAction(action); + final perform = _provider.performSetHeldCallAction; + if (perform != null) { + perform(action); + } } if (action is FCXSetMutedCallAction) { - _provider.performSetMutedCallAction(action); + final perform = _provider.performSetMutedCallAction; + if (perform != null) { + perform(action); + } } if (action is FCXSetGroupCallAction) { - _provider.performSetGroupCallAction(action); + final perform = _provider.performSetGroupCallAction; + if (perform != null) { + perform(action); + } } if (action is FCXPlayDTMFCallAction) { - _provider.performPlayDTMFCallAction(action); + final perform = _provider.performPlayDTMFCallAction; + if (perform != null) { + perform(action); + } } } } @@ -301,7 +322,7 @@ class CallKitService { String displayName, ) async { if (_hasActiveCall) { - if (_activeCall.uuid == uuid) { + if (_activeCall?.uuid == uuid) { return; } else { throw 'There is already an active call'; @@ -336,59 +357,72 @@ class CallKitService { if (_hasNoActiveCalls) { throw 'Active call is null, reportConnected failed'; } - _activeCall.call.outgoing + final call = _activeCall?.call; + if (call == null) { + return; + } + call.outgoing ? await _reportOutgoingCallConnected() : await _reportUpdated(username, displayName); } Future _reportOutgoingCallConnected() async { - if (_activeCall.call.hasConnected) { + final call = _activeCall?.call; + if (call == null || call.hasConnected) { return; } - await _provider.reportOutgoingCallConnected(_activeCall?.uuid, null); + await _provider.reportOutgoingCallConnected(call.uuid, null); } - Future _reportUpdated(String username, String displayName) async => - await _provider.reportCallUpdated( - _activeCall?.uuid, - FCXCallUpdate( - remoteHandle: FCXHandle(FCXHandleType.Generic, username), - hasVideo: false, - localizedCallerName: displayName, - ), - ); + Future _reportUpdated(String username, String displayName) async { + final call = _activeCall?.call; + if (call == null) { + return; + } + await _provider.reportCallUpdated( + call.uuid, + FCXCallUpdate( + remoteHandle: FCXHandle(FCXHandleType.Generic, username), + hasVideo: false, + localizedCallerName: displayName, + ), + ); + } Future holdCall(bool hold) async { if (_hasNoActiveCalls) { throw 'Active call is null, holdCall failed'; } - if (!_activeCall.call.hasConnected) { - _log('Cant hold due to call not being connected yet'); + final call = _activeCall?.call; + if (call == null || call.hasConnected) { return; } await _callController.requestTransactionWithAction( - FCXSetHeldCallAction(_activeCall.uuid, hold)); + FCXSetHeldCallAction(call.uuid, hold)); } Future muteCall(bool mute) async { if (_hasNoActiveCalls) { throw 'Active call is null, muteCall failed'; } - if (!_activeCall.call.hasConnected) { - _log('Cant mute due to call not being connected yet'); + final call = _activeCall?.call; + if (call == null || call.hasConnected) { return; } await _callController.requestTransactionWithAction( - FCXSetMutedCallAction(_activeCall.uuid, mute)); + FCXSetMutedCallAction(call.uuid, mute)); } Future endCall() async { if (_hasNoActiveCalls) { throw 'Active call is null, endCall failed'; } - + final call = _activeCall?.call; + if (call == null) { + return; + } await _callController - .requestTransactionWithAction(FCXEndCallAction(_activeCall.uuid)); + .requestTransactionWithAction(FCXEndCallAction(call.uuid)); } Future reportCallEnded( @@ -400,12 +434,12 @@ class CallKitService { if (!_alreadyEnded(uuid)) { _endedCalls.add(uuid); } - if (uuid == _activeCall.uuid) { + if (uuid == _activeCall?.uuid) { _activeCall = null; } } - Future _reportEnded(String uuid, FCXCallEndedReason reason) async { + Future _reportEnded(String? uuid, FCXCallEndedReason reason) async { if (uuid == null) { return; } @@ -419,11 +453,14 @@ class CallKitService { Future _failTransactions() async { List actions = []; - (await _provider.getPendingTransactions()) - .forEach((t) async => actions.addAll(await t.getActions())); + for (var transaction in (await _provider.getPendingTransactions())) { + actions.addAll(await transaction.getActions()); + } if (actions.isNotEmpty) { - actions.forEach((a) async => await a.fail()); + for (var action in actions) { + await action.fail(); + } } } diff --git a/audio_call/lib/services/push/push_service_android.dart b/audio_call/lib/services/push/push_service_android.dart index 6a70d5f..1e4550b 100644 --- a/audio_call/lib/services/push/push_service_android.dart +++ b/audio_call/lib/services/push/push_service_android.dart @@ -5,44 +5,74 @@ import 'package:audio_call/services/auth_service.dart'; import 'package:audio_call/utils/log.dart'; import 'package:audio_call/utils/notification_helper.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:firebase_core/firebase_core.dart'; + +@pragma('vm:entry-point') +Future onBackgroundMessage(RemoteMessage message) async { + print('PushServiceAndroid: _onBackgroundMessage data: ${message.data}'); + await Firebase.initializeApp(); + Map callDetails = jsonDecode(message.data['voximplant']); + final String displayName = callDetails['display_name']; + + NotificationHelper().displayNotification( + title: 'Incoming call', + description: "from $displayName", + payload: displayName, + ); +} class PushServiceAndroid { - final FirebaseMessaging _firebaseMessaging; + late FirebaseMessaging _firebaseMessaging; + static final PushServiceAndroid _instance = PushServiceAndroid._(); - factory PushServiceAndroid() => _cache ?? PushServiceAndroid._(); - static PushServiceAndroid _cache; - PushServiceAndroid._() : _firebaseMessaging = FirebaseMessaging() { + factory PushServiceAndroid() { + return _instance; + } + PushServiceAndroid._() { _configure(); - _cache = this; } Future _configure() async { _log('configure'); - _firebaseMessaging.configure(onBackgroundMessage: backgroundMessageHandler); + await Firebase.initializeApp(); + _firebaseMessaging = FirebaseMessaging.instance; + FirebaseMessaging.onMessage.listen(_onMessage); + FirebaseMessaging.onBackgroundMessage(onBackgroundMessage); _firebaseMessaging.onTokenRefresh.listen(_onToken); - String token = await _firebaseMessaging.getToken(); + String? token = await _firebaseMessaging.getToken(); _onToken(token); - return Future.value(); } - static Future backgroundMessageHandler( - Map message, - ) async { - _log('onBackgroundMessage: $message'); - if (!message.containsKey('data')) { - return Future.value(); - } + // static Future backgroundMessageHandler( + // Map message, + // ) async { + // _log('onBackgroundMessage: $message'); + // if (!message.containsKey('data')) { + // return Future.value(); + // } + // + // final Map data = + // Map.from(message['data']); + // + // if (!data.containsKey('voximplant')) { + // return Future.value(); + // } + // + // await AuthService().pushNotificationReceived(data); + // + // Map callDetails = jsonDecode(data['voximplant']); + // final String displayName = callDetails['display_name']; + // + // NotificationHelper().displayNotification( + // title: 'Incoming call', + // description: "from $displayName", + // payload: displayName, + // ); + // } - final Map data = - Map.from(message['data']); - - if (!data.containsKey('voximplant')) { - return Future.value(); - } - - await AuthService().pushNotificationReceived(data); - - Map callDetails = jsonDecode(data['voximplant']); + Future _onMessage(RemoteMessage message) async { + _log('PushServiceAndroid: onMessage data: ${message.data}'); + Map callDetails = jsonDecode(message.data['voximplant']); final String displayName = callDetails['display_name']; NotificationHelper().displayNotification( @@ -50,12 +80,10 @@ class PushServiceAndroid { description: "from $displayName", payload: displayName, ); - - return null; } - Future _onToken(String token) async { - _log("onToken: " + token); + Future _onToken(String? token) async { + _log("onToken: $token"); AuthService().voipToken = token; } diff --git a/audio_call/lib/services/push/push_service_ios.dart b/audio_call/lib/services/push/push_service_ios.dart index e4c0ee3..a37050a 100644 --- a/audio_call/lib/services/push/push_service_ios.dart +++ b/audio_call/lib/services/push/push_service_ios.dart @@ -1,45 +1,48 @@ /// Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved. -import 'package:flutter_voip_push_notification/flutter_voip_push_notification.dart'; import 'package:audio_call/services/auth_service.dart'; import 'package:audio_call/utils/log.dart'; +import 'package:flutter/services.dart'; class PushServiceIOS { - final FlutterVoipPushNotification _voipPushNotification = - FlutterVoipPushNotification(); + final MethodChannel _channel = const MethodChannel('plugins.voximplant.com/pushkit'); + final EventChannel _pushKitEventChannel = const EventChannel('plugins.voximplant.com/pushkitevents'); - factory PushServiceIOS() => _cache ?? PushServiceIOS._(); - static PushServiceIOS _cache; + factory PushServiceIOS() { + return _instance; + } + static final PushServiceIOS _instance = PushServiceIOS._(); PushServiceIOS._() { + _log('configure'); + _pushKitEventChannel.receiveBroadcastStream('pushkit').listen(_pushKitEventListener); _configure(); - _cache = this; } Future _configure() async { - _log('configure'); - await _voipPushNotification.requestNotificationPermissions(); - // listen to voip device token changes - _voipPushNotification.onTokenRefresh.listen(_onToken); - // do configure voip push - _voipPushNotification.configure(onMessage: onMessage, onResume: onResume); - } - - Future onMessage(bool isLocal, Map payload) { - // handle foreground notification - _log('onMessage: $payload'); - AuthService().pushNotificationReceived(payload); - return Future.value(); - } - - Future onResume(bool isLocal, Map payload) { - // handle background notification - _log('onResume: $payload'); - return Future.value(); + final token = await _channel.invokeMethod("voipToken"); + if (token != null) { + _log('configure: token $token}'); + AuthService().voipToken = token; + } } - Future _onToken(String token) async { - _log('onToken: $token'); - AuthService().voipToken = token; + void _pushKitEventListener(dynamic event) { + final Map map = event; + if (map['event'] == 'didUpdatePushCredentials') { + final token = map["token"]; + if (token != null) { + _log('didUpdatePushCredentials: token $token}'); + AuthService().voipToken = token; + } + } + if (map['event'] == 'didReceiveIncomingPushWithPayload') { + final payload = map['payload']; + if (payload != null) { + _log('didReceiveIncomingPushWithPayload: payload $payload}'); + Map pushPayload = Map.from(payload); + AuthService().pushNotificationReceived(pushPayload); + } + } } void _log(T message) { diff --git a/audio_call/lib/theme/voximplant_theme.dart b/audio_call/lib/theme/voximplant_theme.dart index c8179c6..c615cdb 100644 --- a/audio_call/lib/theme/voximplant_theme.dart +++ b/audio_call/lib/theme/voximplant_theme.dart @@ -3,11 +3,11 @@ import 'package:flutter/material.dart'; class VoximplantColors { - static const Color primary = const Color(0xff1c0b43); - static const Color primaryDark = const Color(0xff392b5b); - static const Color accent = const Color(0xff8b61ff); - static const Color button = const Color(0xff662eff); - static const Color white = const Color(0xffffffff); - static const Color red = const Color(0xfff54b5e); - static const Color grey = const Color(0xff212121); -} \ No newline at end of file + static const Color primary = Color(0xff1c0b43); + static const Color primaryDark = Color(0xff392b5b); + static const Color accent = Color(0xff8b61ff); + static const Color button = Color(0xff662eff); + static const Color white = Color(0xffffffff); + static const Color red = Color(0xfff54b5e); + static const Color grey = Color(0xff212121); +} diff --git a/audio_call/lib/utils/navigation_helper.dart b/audio_call/lib/utils/navigation_helper.dart index 642b424..332ec90 100644 --- a/audio_call/lib/utils/navigation_helper.dart +++ b/audio_call/lib/utils/navigation_helper.dart @@ -12,19 +12,19 @@ class NavigationHelper { static final GlobalKey navigatorKey = GlobalKey(); - static Future pushToIncomingCall({ - @required String caller, - }) => - navigatorKey.currentState.pushReplacementNamed( - AppRoutes.incomingCall, - arguments: IncomingCallPageArguments(endpoint: caller), - ); - + // static Future pushToIncomingCall({ + // String? caller, + // }) => + // navigatorKey.currentState!.pushReplacementNamed( + // AppRoutes.incomingCall, + // arguments: IncomingCallPageArguments(endpoint: caller), + // ); + // static Future pushToActiveCall({ - @required bool isIncoming, - @required String callTo, + required bool isIncoming, + required String callTo, }) => - navigatorKey.currentState.pushReplacementNamed( + navigatorKey.currentState!.pushReplacementNamed( AppRoutes.activeCall, arguments: ActiveCallPageArguments( isIncoming: isIncoming, @@ -32,13 +32,13 @@ class NavigationHelper { ), ); - static Future pop() => navigatorKey.currentState.maybePop(); + static Future pop() => navigatorKey.currentState!.maybePop(); } class AppRoutes { - static final String login = LoginPage.routeName; - static final String main = MainPage.routeName; - static final String incomingCall = IncomingCallPage.routeName; - static final String activeCall = ActiveCallPage.routeName; - static final String callFailed = CallFailedPage.routeName; -} \ No newline at end of file + static const String login = LoginPage.routeName; + static const String main = MainPage.routeName; + static const String incomingCall = IncomingCallPage.routeName; + static const String activeCall = ActiveCallPage.routeName; + static const String callFailed = CallFailedPage.routeName; +} diff --git a/audio_call/lib/utils/notification_helper.dart b/audio_call/lib/utils/notification_helper.dart index 44dfed6..a9204bd 100644 --- a/audio_call/lib/utils/notification_helper.dart +++ b/audio_call/lib/utils/notification_helper.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:audio_call/utils/log.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'navigation_helper.dart'; @@ -14,42 +13,45 @@ class NotificationHelper { int _notificationId = 100; final FlutterLocalNotificationsPlugin _plugin; - factory NotificationHelper() => _cached ?? NotificationHelper._(); - static NotificationHelper _cached; + static final NotificationHelper _instance = NotificationHelper._(); + + factory NotificationHelper() { + return _instance; + } + NotificationHelper._() : _plugin = FlutterLocalNotificationsPlugin() { _configure(); - _cached = this; } Future _configure() async { print('NotificationHelper: _configure'); await _plugin.initialize( - InitializationSettings( + const InitializationSettings( android: AndroidInitializationSettings('ic_notification'), ), - onSelectNotification: (payload) async { - print('NotificationHelper onSelect $payload'); - await NavigationHelper.pushToIncomingCall(caller: payload); + onDidReceiveNotificationResponse: (notificationResponse) async { + _log('NotificationHelper onSelect $notificationResponse'); + // await NavigationHelper.pushToIncomingCall(caller: notificationResponse.payload); return Future.value(); }, ); } Future displayNotification({ - @required String title, - @required String description, - String payload, + required String title, + required String description, + required String payload, }) async { _log('displayNotification title: $title, description: $description'); await _plugin.show( _notificationId, title, description, - NotificationDetails( + const NotificationDetails( android: AndroidNotificationDetails( 'VoximplantChannelIncomingCalls', 'CallChannel', - 'Incoming calls notifications', + channelDescription: 'Incoming calls notifications', importance: Importance.max, priority: Priority.max, ticker: 'incoming call', @@ -57,7 +59,7 @@ class NotificationHelper { ), payload: payload, ); - Timer(Duration(seconds: 15), () { + Timer(const Duration(seconds: 15), () { cancelNotification(); }); } @@ -66,10 +68,10 @@ class NotificationHelper { await _plugin.cancelAll(); } - Future didNotificationLaunchApp() async { + Future didNotificationLaunchApp() async { var details = await _plugin.getNotificationAppLaunchDetails(); - _log('didNotificationLaunchApp: ${details.didNotificationLaunchApp}'); - return details.didNotificationLaunchApp; + _log('didNotificationLaunchApp: ${details?.didNotificationLaunchApp}'); + return details?.didNotificationLaunchApp; } void _log(T message) { diff --git a/audio_call/lib/utils/permissions_helper.dart b/audio_call/lib/utils/permissions_helper.dart index 976a54a..b92eb5a 100644 --- a/audio_call/lib/utils/permissions_helper.dart +++ b/audio_call/lib/utils/permissions_helper.dart @@ -6,7 +6,7 @@ import 'package:permission_handler/permission_handler.dart'; Future checkPermissions() async { if (Platform.isAndroid) { PermissionStatus recordAudio = await Permission.microphone.status; - List requestPermissions = List(); + List requestPermissions = []; if (recordAudio != PermissionStatus.granted) { requestPermissions.add(Permission.microphone); } @@ -27,4 +27,4 @@ Future checkPermissions() async { //not supported platforms return false; } -} \ No newline at end of file +} diff --git a/audio_call/lib/widgets/widgets.dart b/audio_call/lib/widgets/widgets.dart index f2b16f8..f8475eb 100644 --- a/audio_call/lib/widgets/widgets.dart +++ b/audio_call/lib/widgets/widgets.dart @@ -5,13 +5,13 @@ import 'package:flutter/material.dart'; class Widgets { static Widget textFormField({ - @required TextEditingController controller, - @required bool darkBackground, - String labelText, - String suffixText, + required TextEditingController controller, + required bool darkBackground, + required String labelText, + String? suffixText, bool obscureText = false, TextInputType inputType = TextInputType.text, - FormFieldValidator validator, + FormFieldValidator? validator, }) { return Padding( padding: const EdgeInsets.symmetric( @@ -22,8 +22,8 @@ class Widgets { data: ThemeData( primaryColor: darkBackground ? VoximplantColors.white : VoximplantColors.button, - cursorColor: - darkBackground ? VoximplantColors.white : VoximplantColors.button, + // cursorColor: + // darkBackground ? VoximplantColors.white : VoximplantColors.button, hintColor: darkBackground ? VoximplantColors.white : VoximplantColors.button, ), @@ -60,8 +60,8 @@ class Widgets { } static Widget maxWidthRaisedButton({ - @required String text, - @required VoidCallback onPressed, + required String text, + required VoidCallback onPressed, }) { return Padding( padding: const EdgeInsets.symmetric( @@ -71,9 +71,8 @@ class Widgets { child: SizedBox( width: double.infinity, height: 50, - child: RaisedButton( - textColor: VoximplantColors.white, - color: VoximplantColors.button, + child: ElevatedButton( + style: ElevatedButton.styleFrom(textStyle: const TextStyle(color: VoximplantColors.white)), onPressed: onPressed, child: Text(text), ), @@ -82,9 +81,9 @@ class Widgets { } static Widget textWithPadding({ - @required String text, - Color textColor, - double fontSize, + required String text, + required Color textColor, + double fontSize = 30.0, double verticalPadding = 0.0, double horizontalPadding = 0.0, }) { @@ -104,13 +103,13 @@ class Widgets { } static Widget iconButton({ - @required IconData icon, - @required Color color, - @required String tooltip, - @required VoidCallback onPressed, + required IconData icon, + required Color color, + required String tooltip, + required VoidCallback onPressed, }) { return Ink( - decoration: ShapeDecoration( + decoration: const ShapeDecoration( color: VoximplantColors.white, shape: CircleBorder(), ), @@ -125,4 +124,4 @@ class Widgets { ), ); } -} \ No newline at end of file +} diff --git a/audio_call/pubspec.lock b/audio_call/pubspec.lock index 7e39cf5..93563b3 100644 --- a/audio_call/pubspec.lock +++ b/audio_call/pubspec.lock @@ -1,177 +1,240 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: "330d7fcbb72624f5b6d374af8b059b0ef4ba96ba5b8987f874964a1287eb617d" + url: "https://pub.dev" + source: hosted + version: "1.0.18" + args: + dependency: transitive + description: + name: args + sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + url: "https://pub.dev" + source: hosted + version: "2.4.0" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" source: hosted - version: "2.8.2" + version: "2.10.0" bloc: dependency: transitive description: name: bloc - url: "https://pub.dartlang.org" + sha256: "658a5ae59edcf1e58aac98b000a71c762ad8f46f1394c34a52050cafb3e11a80" + url: "https://pub.dev" source: hosted - version: "6.1.3" + version: "8.1.1" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.2.1" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" source: hosted - version: "1.16.0" - convert: + version: "1.17.0" + crypto: dependency: transitive description: - name: convert - url: "https://pub.dartlang.org" + name: crypto + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" source: hosted - version: "2.1.1" - crypto: + version: "3.0.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + url: "https://pub.dev" + source: hosted + version: "1.0.5" + dbus: dependency: transitive description: - name: crypto - url: "https://pub.dartlang.org" + name: dbus + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "0.7.8" equatable: - dependency: "direct main" + dependency: "direct dev" description: name: equatable - url: "https://pub.dartlang.org" + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" source: hosted - version: "1.2.5" + version: "2.0.5" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - url: "https://pub.dartlang.org" + sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "2.0.1" file: dependency: transitive description: name: file - url: "https://pub.dartlang.org" + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" source: hosted version: "6.1.4" firebase_core: - dependency: transitive + dependency: "direct dev" description: name: firebase_core - url: "https://pub.dartlang.org" + sha256: "75f747cafd7cbd6c00b908e3a7aa59fc31593d46ba8165d9ee8a79e69464a394" + url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "2.8.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - url: "https://pub.dartlang.org" + sha256: "0df0a064ab0cad7f8836291ca6f3272edd7b83ad5b3540478ee46a0849d8022b" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "4.6.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - url: "https://pub.dartlang.org" + sha256: "347351a8f0518f3343d79a9a0690fa67ad232fc32e2ea270677791949eac792b" + url: "https://pub.dev" source: hosted - version: "0.2.1+1" + version: "2.3.0" firebase_messaging: - dependency: "direct main" + dependency: "direct dev" description: name: firebase_messaging - url: "https://pub.dartlang.org" + sha256: e82c99bcccf707db4d82ac3fb07be629a392c4cc2a6756aaf1a74c009114846f + url: "https://pub.dev" source: hosted - version: "7.0.3" + version: "14.3.0" + firebase_messaging_platform_interface: + dependency: transitive + description: + name: firebase_messaging_platform_interface + sha256: "710bb139ff6e27fe47a0684ba88df4b7517a44d814dc6b7f54a2537568a82fcf" + url: "https://pub.dev" + source: hosted + version: "4.2.16" + firebase_messaging_web: + dependency: transitive + description: + name: firebase_messaging_web + sha256: "66ece031cb17fb771cda4b3328858395fa48aa4c3b3e6a5d1bcaff928086cacd" + url: "https://pub.dev" + source: hosted + version: "3.2.17" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" flutter_bloc: - dependency: "direct main" + dependency: "direct dev" description: name: flutter_bloc - url: "https://pub.dartlang.org" + sha256: "434951eea948dbe87f737b674281465f610b8259c16c097b8163ce138749a775" + url: "https://pub.dev" source: hosted - version: "6.0.6" + version: "8.1.2" flutter_callkit_voximplant: - dependency: "direct main" + dependency: "direct dev" description: name: flutter_callkit_voximplant - url: "https://pub.dartlang.org" + sha256: fe2c46e1ff9d12c7b5a1f7ec4c50ac20d42f5abbcb7ac988dc3e18ae09524a03 + url: "https://pub.dev" + source: hosted + version: "2.1.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" source: hosted - version: "1.2.0+3" + version: "2.0.1" flutter_local_notifications: - dependency: "direct main" + dependency: "direct dev" description: name: flutter_local_notifications - url: "https://pub.dartlang.org" + sha256: "293995f94e120c8afce768981bd1fa9c5d6de67c547568e3b42ae2defdcbb4a0" + url: "https://pub.dev" + source: hosted + version: "13.0.0" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: ccb08b93703aeedb58856e5637450bf3ffec899adb66dc325630b68994734b89 + url: "https://pub.dev" source: hosted - version: "2.0.1+1" + version: "3.0.0+1" flutter_local_notifications_platform_interface: dependency: transitive description: name: flutter_local_notifications_platform_interface - url: "https://pub.dartlang.org" + sha256: "5ec1feac5f7f7d9266759488bc5f76416152baba9aa1b26fe572246caa00d1ab" + url: "https://pub.dev" source: hosted - version: "2.0.0+1" + version: "6.0.0" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" - flutter_voip_push_notification: - dependency: "direct main" - description: - name: flutter_voip_push_notification - url: "https://pub.dartlang.org" - source: hosted - version: "0.0.3" flutter_voximplant: - dependency: "direct main" + dependency: "direct dev" description: name: flutter_voximplant - url: "https://pub.dartlang.org" + sha256: "712fb316c1d6553326d29c43d17454cf69adc930527308af9136aa6c72921d4e" + url: "https://pub.dev" source: hosted - version: "3.5.1" + version: "3.8.0" flutter_web_plugins: dependency: transitive description: flutter @@ -181,156 +244,218 @@ packages: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + lints: + dependency: transitive + description: + name: lints + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" source: hosted - version: "0.6.4" + version: "2.0.1" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" source: hosted - version: "0.12.11" + version: "0.12.13" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" source: hosted - version: "0.1.4" + version: "0.2.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" source: hosted - version: "1.7.0" + version: "1.8.0" nested: dependency: transitive description: name: nested - url: "https://pub.dartlang.org" + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" source: hosted version: "1.0.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.8.2" path_provider_linux: dependency: transitive description: name: path_provider_linux - url: "https://pub.dartlang.org" + sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" + url: "https://pub.dev" source: hosted - version: "0.0.1+2" + version: "2.1.10" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - url: "https://pub.dartlang.org" + sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "2.0.6" path_provider_windows: dependency: transitive description: name: path_provider_windows - url: "https://pub.dartlang.org" + sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130 + url: "https://pub.dev" source: hosted - version: "0.0.5" + version: "2.1.5" permission_handler: - dependency: "direct main" + dependency: "direct dev" description: name: permission_handler - url: "https://pub.dartlang.org" + sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8" + url: "https://pub.dev" + source: hosted + version: "10.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2" + url: "https://pub.dev" + source: hosted + version: "10.2.0" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: ee96ac32f5a8e6f80756e25b25b9f8e535816c8e6665a96b6d70681f8c4f7e85 + url: "https://pub.dev" source: hosted - version: "5.0.1+1" + version: "9.0.8" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - url: "https://pub.dartlang.org" + sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" + url: "https://pub.dev" + source: hosted + version: "3.9.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b + url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "0.1.2" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + url: "https://pub.dev" + source: hosted + version: "5.1.0" platform: dependency: transitive description: name: platform - url: "https://pub.dartlang.org" + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" source: hosted version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "2.1.4" process: dependency: transitive description: name: process - url: "https://pub.dartlang.org" + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" source: hosted version: "4.2.4" provider: dependency: transitive description: name: provider - url: "https://pub.dartlang.org" - source: hosted - version: "4.3.3" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "6.0.5" shared_preferences: - dependency: "direct main" + dependency: "direct dev" description: name: shared_preferences - url: "https://pub.dartlang.org" + sha256: ee6257848f822b8481691f20c3e6d2bfee2e9eccb2a3d249907fcfb198c55b41 + url: "https://pub.dev" source: hosted - version: "0.5.12+2" - shared_preferences_linux: + version: "2.0.18" + shared_preferences_android: dependency: transitive description: - name: shared_preferences_linux - url: "https://pub.dartlang.org" + name: shared_preferences_android + sha256: "7fa90471a6875d26ad78c7e4a675874b2043874586891128dc5899662c97db46" + url: "https://pub.dev" source: hosted - version: "0.0.2+4" - shared_preferences_macos: + version: "2.1.2" + shared_preferences_foundation: dependency: transitive description: - name: shared_preferences_macos - url: "https://pub.dartlang.org" + name: shared_preferences_foundation + sha256: "0c1c16c56c9708aa9c361541a6f0e5cc6fc12a3232d866a687a7b7db30032b07" + url: "https://pub.dev" source: hosted - version: "0.0.1+11" + version: "2.2.1" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa" + url: "https://pub.dev" + source: hosted + version: "2.2.0" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - url: "https://pub.dartlang.org" + sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d + url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "2.2.0" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - url: "https://pub.dartlang.org" + sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5" + url: "https://pub.dev" source: hosted - version: "0.1.2+7" + version: "2.1.0" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - url: "https://pub.dartlang.org" + sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173" + url: "https://pub.dev" source: hosted - version: "0.0.2+3" + version: "2.2.0" sky_engine: dependency: transitive description: flutter @@ -340,86 +465,106 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" source: hosted - version: "0.4.9" + version: "0.4.16" timezone: dependency: transitive description: name: timezone - url: "https://pub.dartlang.org" + sha256: "24c8fcdd49a805d95777a39064862133ff816ebfffe0ceff110fb5960e557964" + url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.9.1" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" source: hosted version: "1.3.1" uuid: - dependency: "direct main" + dependency: "direct dev" description: name: uuid - url: "https://pub.dartlang.org" + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "3.0.7" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" win32: dependency: transitive description: name: win32 - url: "https://pub.dartlang.org" + sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4 + url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "3.1.4" xdg_directories: dependency: transitive description: name: xdg_directories - url: "https://pub.dartlang.org" + sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + url: "https://pub.dev" source: hosted - version: "0.1.2" + version: "0.2.0+3" + xml: + dependency: transitive + description: + name: xml + sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + url: "https://pub.dev" + source: hosted + version: "6.2.2" sdks: - dart: ">=2.17.0 <3.0.0" - flutter: ">=1.20.0" + dart: ">=2.19.4 <3.0.0" + flutter: ">=3.0.0" diff --git a/audio_call/pubspec.yaml b/audio_call/pubspec.yaml index 57e5306..aee6ff0 100644 --- a/audio_call/pubspec.yaml +++ b/audio_call/pubspec.yaml @@ -1,78 +1,33 @@ name: audio_call description: A new Flutter project. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.2.3 +version: 1.0.0+1 environment: - sdk: ">=2.2.2 <3.0.0" + sdk: '>=2.19.4 <3.0.0' dependencies: flutter: sdk: flutter - flutter_bloc: 6.0.6 - equatable: 1.2.5 - firebase_messaging: 7.0.3 - flutter_voximplant: 3.5.1 - flutter_callkit_voximplant: 1.2.0+3 - flutter_voip_push_notification: 0.0.3 - flutter_local_notifications: 2.0.1+1 - shared_preferences: 0.5.12+2 - permission_handler: 5.0.1+1 - uuid: ^2.2.2 + cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: sdk: flutter - - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. + flutter_lints: ^2.0.0 + flutter_bloc: 8.1.2 + flutter_voximplant: 3.8.0 + flutter_callkit_voximplant: 2.1.0 + uuid: 3.0.7 + flutter_local_notifications: 13.0.0 + permission_handler: 10.2.0 + shared_preferences: 2.0.18 + equatable: 2.0.5 + firebase_core: 2.8.0 + firebase_messaging: 14.3.0 + +# The following section is specific to Flutter packages. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/audio_call/test/widget_test.dart b/audio_call/test/widget_test.dart index 486c7e8..d1fd7c4 100644 --- a/audio_call/test/widget_test.dart +++ b/audio_call/test/widget_test.dart @@ -1,7 +1,7 @@ // This is a basic Flutter widget test. // // To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll +// utility in the flutter_test package. For example, you can send tap and scroll // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. @@ -13,18 +13,18 @@ import 'package:audio_call/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(App()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); + // await tester.pumpWidget(const MyApp()); + // + // // Verify that our counter starts at 0. + // expect(find.text('0'), findsOneWidget); + // expect(find.text('1'), findsNothing); + // + // // Tap the '+' icon and trigger a frame. + // await tester.tap(find.byIcon(Icons.add)); + // await tester.pump(); + // + // // Verify that our counter has incremented. + // expect(find.text('0'), findsNothing); + // expect(find.text('1'), findsOneWidget); }); }