diff --git a/packages/hms_room_kit/CHANGELOG.md b/packages/hms_room_kit/CHANGELOG.md
index 65fe7a865..e66819d5f 100644
--- a/packages/hms_room_kit/CHANGELOG.md
+++ b/packages/hms_room_kit/CHANGELOG.md
@@ -5,6 +5,21 @@
| hms_room_kit | [![Pub Version](https://img.shields.io/pub/v/hms_room_kit)](https://pub.dev/packages/hms_room_kit) |
| hmssdk_flutter | [![Pub Version](https://img.shields.io/pub/v/hmssdk_flutter)](https://pub.dev/packages/hmssdk_flutter) |
+## 1.0.12 - 2024-02-12
+
+| Package | Version |
+| -------------- | ------------------------------------------------------------------------------------------------------ |
+| hms_room_kit | 1.0.12 |
+| hmssdk_flutter | 1.9.9 |
+
+### 🚀 Added
+
+- Introducing polls on prebuilt
+
+ Users can now create, manage, and stop polls directly from the prebuilt interface.
+
+Updated `hmssdk_flutter` package version to 1.9.9
+
## 1.0.11 - 2024-02-01
| Package | Version |
diff --git a/packages/hms_room_kit/example/ios/Flutter/AppFrameworkInfo.plist b/packages/hms_room_kit/example/ios/Flutter/AppFrameworkInfo.plist
index 9625e105d..7c5696400 100644
--- a/packages/hms_room_kit/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/packages/hms_room_kit/example/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 11.0
+ 12.0
diff --git a/packages/hms_room_kit/example/ios/Podfile.lock b/packages/hms_room_kit/example/ios/Podfile.lock
index 78aee02dc..50232f590 100644
--- a/packages/hms_room_kit/example/ios/Podfile.lock
+++ b/packages/hms_room_kit/example/ios/Podfile.lock
@@ -6,14 +6,14 @@ PODS:
- HMSBroadcastExtensionSDK (0.0.9)
- HMSHLSPlayerSDK (0.0.2):
- HMSAnalyticsSDK (= 0.0.2)
- - HMSSDK (1.4.1):
+ - HMSSDK (1.5.0):
- HMSAnalyticsSDK (= 0.0.2)
- HMSWebRTC (= 1.0.5116)
- hmssdk_flutter (1.9.6):
- Flutter
- HMSBroadcastExtensionSDK (= 0.0.9)
- HMSHLSPlayerSDK (= 0.0.2)
- - HMSSDK (= 1.4.1)
+ - HMSSDK (= 1.5.0)
- HMSWebRTC (1.0.5116)
- path_provider_foundation (0.0.1):
- Flutter
@@ -65,20 +65,20 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS:
- Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
+ Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_foreground_task: 21ef182ab0a29a3005cc72cd70e5f45cb7f7f817
HMSAnalyticsSDK: 4d2a88a729b1eb42f3d25f217c28937ec318a5b7
HMSBroadcastExtensionSDK: d80fe325f6c928bd8e5176290b5a4b7ae15d6fbb
HMSHLSPlayerSDK: 6a54ad4d12f3dc2270d1ecd24019d71282a4f6a3
- HMSSDK: 6a579cb806d4760cda149002150ff0beab03749b
- hmssdk_flutter: 0b12e79b2f184fa3308fb65dade6e3b022d805c9
+ HMSSDK: 0d1901d64faf2661d1183c1ba2881e2531a5eeba
+ hmssdk_flutter: 9f3b16d9bfc1e9a2ccd63f5d9b6a6d51669ed5ac
HMSWebRTC: ae54e9dd91b869051b283b43b14f57d43b7bf8e1
- path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
+ path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
- shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
- url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
+ shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
+ url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
-COCOAPODS: 1.14.3
+COCOAPODS: 1.15.0
diff --git a/packages/hms_room_kit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/hms_room_kit/example/ios/Runner.xcodeproj/project.pbxproj
index 16761b9f3..71331a3a9 100644
--- a/packages/hms_room_kit/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/hms_room_kit/example/ios/Runner.xcodeproj/project.pbxproj
@@ -342,7 +342,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -422,7 +422,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -471,7 +471,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
diff --git a/packages/hms_room_kit/example/pubspec.lock b/packages/hms_room_kit/example/pubspec.lock
index c47272d6d..7f0a5092c 100644
--- a/packages/hms_room_kit/example/pubspec.lock
+++ b/packages/hms_room_kit/example/pubspec.lock
@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: archive
- sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
+ sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
url: "https://pub.dev"
source: hosted
- version: "3.4.9"
+ version: "3.4.10"
args:
dependency: transitive
description:
@@ -214,15 +214,15 @@ packages:
path: ".."
relative: true
source: path
- version: "1.0.11"
+ version: "1.0.12"
hmssdk_flutter:
dependency: transitive
description:
name: hmssdk_flutter
- sha256: ff1697824b42d31cb093fd4319c8fa3ba6872b7877707630062ed3cd9cf40813
+ sha256: bad4ff87c677970f0c9acfcd4e84b60089461e1c1eef23e3a3ae6ab191484b95
url: "https://pub.dev"
source: hosted
- version: "1.9.8"
+ version: "1.9.9"
http:
dependency: transitive
description:
@@ -339,10 +339,10 @@ packages:
dependency: transitive
description:
name: path_provider
- sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
+ sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
url: "https://pub.dev"
source: hosted
- version: "2.1.1"
+ version: "2.1.2"
path_provider_android:
dependency: transitive
description:
@@ -355,10 +355,10 @@ packages:
dependency: transitive
description:
name: path_provider_foundation
- sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
+ sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
url: "https://pub.dev"
source: hosted
- version: "2.3.1"
+ version: "2.3.2"
path_provider_linux:
dependency: transitive
description:
@@ -371,10 +371,10 @@ packages:
dependency: transitive
description:
name: path_provider_platform_interface
- sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
+ sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
- version: "2.1.1"
+ version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
@@ -435,26 +435,26 @@ packages:
dependency: transitive
description:
name: platform
- sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
+ sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
source: hosted
- version: "3.1.3"
+ version: "3.1.4"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
- sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
- version: "2.1.7"
+ version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
- sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
+ sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
url: "https://pub.dev"
source: hosted
- version: "3.7.3"
+ version: "3.7.4"
provider:
dependency: transitive
description:
@@ -499,10 +499,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_foundation
- sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
+ sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
url: "https://pub.dev"
source: hosted
- version: "2.3.4"
+ version: "2.3.5"
shared_preferences_linux:
dependency: transitive
description:
@@ -515,10 +515,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_platform_interface
- sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
+ sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
url: "https://pub.dev"
source: hosted
- version: "2.3.1"
+ version: "2.3.2"
shared_preferences_web:
dependency: transitive
description:
@@ -616,26 +616,26 @@ packages:
dependency: transitive
description:
name: url_launcher
- sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86
+ sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c
url: "https://pub.dev"
source: hosted
- version: "6.2.2"
+ version: "6.2.4"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
- sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def"
+ sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f"
url: "https://pub.dev"
source: hosted
- version: "6.2.0"
+ version: "6.2.2"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
- sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3
+ sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03"
url: "https://pub.dev"
source: hosted
- version: "6.2.1"
+ version: "6.2.4"
url_launcher_linux:
dependency: transitive
description:
@@ -656,10 +656,10 @@ packages:
dependency: transitive
description:
name: url_launcher_platform_interface
- sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50"
+ sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f
url: "https://pub.dev"
source: hosted
- version: "2.2.0"
+ version: "2.3.1"
url_launcher_web:
dependency: transitive
description:
@@ -688,26 +688,26 @@ packages:
dependency: transitive
description:
name: vector_graphics
- sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43"
+ sha256: "18f6690295af52d081f6808f2f7c69f0eed6d7e23a71539d75f4aeb8f0062172"
url: "https://pub.dev"
source: hosted
- version: "1.1.9+1"
+ version: "1.1.9+2"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
- sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7"
+ sha256: "531d20465c10dfac7f5cd90b60bbe4dd9921f1ec4ca54c83ebb176dbacb7bb2d"
url: "https://pub.dev"
source: hosted
- version: "1.1.9+1"
+ version: "1.1.9+2"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
- sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26
+ sha256: "03012b0a33775c5530576b70240308080e1d5050f0faf000118c20e6463bc0ad"
url: "https://pub.dev"
source: hosted
- version: "1.1.9+1"
+ version: "1.1.9+2"
vector_math:
dependency: transitive
description:
@@ -744,10 +744,10 @@ packages:
dependency: transitive
description:
name: xdg_directories
- sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
+ sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev"
source: hosted
- version: "1.0.3"
+ version: "1.0.4"
xml:
dependency: transitive
description:
diff --git a/packages/hms_room_kit/example/pubspec.yaml b/packages/hms_room_kit/example/pubspec.yaml
index f915efd52..34b4c4d6c 100644
--- a/packages/hms_room_kit/example/pubspec.yaml
+++ b/packages/hms_room_kit/example/pubspec.yaml
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
-version: 1.0.11
+version: 1.0.12
environment:
sdk: ">=2.19.6 <3.0.0"
diff --git a/packages/hms_room_kit/lib/src/assets/icons/add_option.svg b/packages/hms_room_kit/lib/src/assets/icons/add_option.svg
new file mode 100644
index 000000000..b6926ba15
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/assets/icons/add_option.svg
@@ -0,0 +1,8 @@
+
diff --git a/packages/hms_room_kit/lib/src/assets/icons/delete_poll.svg b/packages/hms_room_kit/lib/src/assets/icons/delete_poll.svg
new file mode 100644
index 000000000..94993e03d
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/assets/icons/delete_poll.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/hms_room_kit/lib/src/assets/icons/poll.svg b/packages/hms_room_kit/lib/src/assets/icons/poll.svg
new file mode 100644
index 000000000..15c251898
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/assets/icons/poll.svg
@@ -0,0 +1,7 @@
+
diff --git a/packages/hms_room_kit/lib/src/assets/icons/polls.svg b/packages/hms_room_kit/lib/src/assets/icons/polls.svg
new file mode 100644
index 000000000..51b0dc22c
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/assets/icons/polls.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/hms_room_kit/lib/src/assets/icons/quiz.svg b/packages/hms_room_kit/lib/src/assets/icons/quiz.svg
new file mode 100644
index 000000000..d6f23aaa0
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/assets/icons/quiz.svg
@@ -0,0 +1,9 @@
+
diff --git a/packages/hms_room_kit/lib/src/common/utility_components.dart b/packages/hms_room_kit/lib/src/common/utility_components.dart
index 0c0ac98e3..7bc5d99ad 100644
--- a/packages/hms_room_kit/lib/src/common/utility_components.dart
+++ b/packages/hms_room_kit/lib/src/common/utility_components.dart
@@ -37,7 +37,7 @@ class UtilityComponents {
),
context: context,
builder: (ctx) => ChangeNotifierProvider.value(
- value: context.read(),
+ value: meetingStore,
child: LeaveSessionBottomSheet(
meetingStore: meetingStore,
)),
diff --git a/packages/hms_room_kit/lib/src/common/utility_functions.dart b/packages/hms_room_kit/lib/src/common/utility_functions.dart
index 8d46d51d4..b7a2d8512 100644
--- a/packages/hms_room_kit/lib/src/common/utility_functions.dart
+++ b/packages/hms_room_kit/lib/src/common/utility_functions.dart
@@ -5,11 +5,13 @@ import 'dart:math' as math;
///Package imports
import 'package:flutter/material.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
-import 'package:hms_room_kit/hms_room_kit.dart';
import 'package:hmssdk_flutter/hmssdk_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
+///Project imports
+import 'package:hms_room_kit/hms_room_kit.dart';
+
///This class contains the utility functions used in the app
class Utilities {
static RegExp regexEmoji = RegExp(
@@ -119,6 +121,25 @@ class Utilities {
}
}
+ ///This method returns the scale of the toast according to the index and the total number of toasts
+ static double getToastScale(int index, int toastsCount) {
+ if (toastsCount == 1) {
+ return 1;
+ } else if (toastsCount == 2) {
+ if (index == 0) {
+ return 0.95;
+ }
+ return 1;
+ } else {
+ if (index == 0) {
+ return 0.90;
+ } else if (index == 1) {
+ return 0.95;
+ }
+ return 1;
+ }
+ }
+
///This contains the list of toasts possible colors
static final List _toastColors = [
HMSThemeColors.surfaceDim,
@@ -269,6 +290,17 @@ class Utilities {
// duration: Duration(seconds: time));
}
+ static String getQuestionType(HMSPollQuestionType questionType) {
+ switch (questionType) {
+ case HMSPollQuestionType.singleChoice:
+ return "SINGLE CHOICE";
+ case HMSPollQuestionType.multiChoice:
+ return "MULTI CHOICE";
+ default:
+ return "SINGLE CHOICE";
+ }
+ }
+
static Future getStringData({required String key}) async {
final prefs = await SharedPreferences.getInstance();
diff --git a/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_bottom_navigation_bar.dart b/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_bottom_navigation_bar.dart
index 5b9831978..364b42c10 100644
--- a/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_bottom_navigation_bar.dart
+++ b/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_bottom_navigation_bar.dart
@@ -13,7 +13,7 @@ import 'package:hms_room_kit/src/widgets/bottom_sheets/chat_only_bottom_sheet.da
import 'package:hms_room_kit/src/widgets/tab_widgets/chat_participants_tab_bar.dart';
import 'package:hms_room_kit/src/common/utility_components.dart';
import 'package:hms_room_kit/src/hls_viewer/overlay_chat_component.dart';
-import 'package:hms_room_kit/src/widgets/bottom_sheets/hls_more_options.dart';
+import 'package:hms_room_kit/src/widgets/bottom_sheets/hls_app_utilities_bottom_sheet.dart';
import 'package:hms_room_kit/src/hls_viewer/hls_player_store.dart';
import 'package:hms_room_kit/src/meeting/meeting_store.dart';
import 'package:hms_room_kit/src/widgets/common_widgets/hms_embedded_button.dart';
@@ -242,7 +242,7 @@ class HLSViewerBottomNavigationBar extends StatelessWidget {
value:
context.read(),
child:
- const HLSMoreOptionsBottomSheet()),
+ const HLSAppUtilitiesBottomSheet()),
)
},
enabledBorderColor: HMSThemeColors
diff --git a/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_header.dart b/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_header.dart
index 0dcd96025..1b2d934b8 100644
--- a/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_header.dart
+++ b/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_header.dart
@@ -15,6 +15,7 @@ import 'package:hms_room_kit/src/layout_api/hms_theme_colors.dart';
import 'package:hms_room_kit/src/meeting/meeting_store.dart';
import 'package:hms_room_kit/src/widgets/common_widgets/hms_title_text.dart';
import 'package:hms_room_kit/src/common/utility_functions.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/live_badge.dart';
///[HLSViewerHeader] is the header of the HLS Viewer screen
class HLSViewerHeader extends StatelessWidget {
@@ -74,24 +75,7 @@ class HLSViewerHeader extends StatelessWidget {
meetingStore.streamingType['rtmp'] ==
HMSStreamingState.started),
builder: (_, isHLSStarted, __) {
- return isHLSStarted
- ? Container(
- height: 24,
- width: 43,
- decoration: BoxDecoration(
- color: HMSThemeColors.alertErrorDefault,
- borderRadius: BorderRadius.circular(4)),
- child: Center(
- child: HMSTitleText(
- text: "LIVE",
- fontSize: 10,
- lineHeight: 16,
- letterSpacing: 1.5,
- textColor:
- HMSThemeColors.alertErrorBrighter),
- ),
- )
- : Container();
+ return isHLSStarted ? const LiveBadge() : Container();
}),
const SizedBox(
width: 8,
diff --git a/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_page.dart b/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_page.dart
index c0e66023a..46c8ea8b2 100644
--- a/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_page.dart
+++ b/packages/hms_room_kit/lib/src/hls_viewer/hls_viewer_page.dart
@@ -1,11 +1,17 @@
+///Dart imports
+import 'dart:math';
+
///Package imports
import 'package:flutter/material.dart';
-import 'package:hms_room_kit/src/widgets/common_widgets/hms_left_room_screen.dart';
+import 'package:hms_room_kit/src/widgets/toasts/toast_widget.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'package:hmssdk_flutter/hmssdk_flutter.dart';
///Project imports
+import 'package:hms_room_kit/src/widgets/app_dialogs/audio_device_change_dialog.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_left_room_screen.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_toast_model.dart';
import 'package:hms_room_kit/src/hls_viewer/hls_viewer_bottom_navigation_bar.dart';
import 'package:hms_room_kit/src/hls_viewer/hls_viewer_header.dart';
import 'package:hms_room_kit/src/layout_api/hms_theme_colors.dart';
@@ -84,209 +90,332 @@ class _HLSViewerPageState extends State {
scaffoldBackgroundColor:
HMSThemeColors.backgroundDim),
child: SingleChildScrollView(
- child: Stack(
- children: [
- Selector(
- selector: (_, meetingStore) =>
- meetingStore.hasHlsStarted,
- builder: (_, hasHlsStarted, __) {
- _setStreamStatus(hasHlsStarted);
- return (hasHlsStarted)
- ? SizedBox(
- width: MediaQuery.of(context)
- .size
- .width,
- height: MediaQuery.of(context)
- .size
- .height,
- child: HLSPlayer(
- key: Key(context
- .read()
- .localPeer
- ?.peerId ??
- "HLS_PLAYER"),
- ratio: Utilities
- .getHLSPlayerDefaultRatio(
- MediaQuery.of(context)
- .size),
- ),
- )
- : SizedBox(
- width: MediaQuery.of(context)
- .size
- .width,
- height: MediaQuery.of(context)
- .size
- .height,
- child: const HLSWaitingUI());
- }),
+ child: SizedBox(
+ width: MediaQuery.of(context).size.width,
+ height: MediaQuery.of(context).size.height,
+ child: Stack(
+ children: [
+ Selector(
+ selector: (_, meetingStore) =>
+ meetingStore.hasHlsStarted,
+ builder: (_, hasHlsStarted, __) {
+ _setStreamStatus(hasHlsStarted);
+ return (hasHlsStarted)
+ ? SizedBox(
+ width: MediaQuery.of(context)
+ .size
+ .width,
+ height: MediaQuery.of(context)
+ .size
+ .height,
+ child: HLSPlayer(
+ key: Key(context
+ .read<
+ MeetingStore>()
+ .localPeer
+ ?.peerId ??
+ "HLS_PLAYER"),
+ ratio: Utilities
+ .getHLSPlayerDefaultRatio(
+ MediaQuery.of(
+ context)
+ .size),
+ ),
+ )
+ : SizedBox(
+ width: MediaQuery.of(context)
+ .size
+ .width,
+ height: MediaQuery.of(context)
+ .size
+ .height,
+ child: const HLSWaitingUI());
+ }),
- ///Will only be displayed when the controls are visible
- SizedBox(
- height: MediaQuery.of(context).size.height,
- child: Column(
- mainAxisAlignment:
- MainAxisAlignment.spaceBetween,
- children: [
- Selector(
- selector: (_, hlsPlayerStore) =>
- hlsPlayerStore
- .areStreamControlsVisible,
- builder: (_,
- areStreamControlsVisible, __) {
- return AnimatedContainer(
- duration: const Duration(
- milliseconds: 200),
- height: areStreamControlsVisible
- ? 100
- : 0,
- child: areStreamControlsVisible
- ? const HLSViewerHeader()
- : Container(),
- );
- }),
- const HLSViewerBottomNavigationBar()
- ],
+ ///Will only be displayed when the controls are visible
+ SizedBox(
+ height:
+ MediaQuery.of(context).size.height,
+ child: Column(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceBetween,
+ children: [
+ Selector(
+ selector: (_, hlsPlayerStore) =>
+ hlsPlayerStore
+ .areStreamControlsVisible,
+ builder: (_,
+ areStreamControlsVisible,
+ __) {
+ return AnimatedContainer(
+ duration: const Duration(
+ milliseconds: 200),
+ height:
+ areStreamControlsVisible
+ ? 100
+ : 0,
+ child: areStreamControlsVisible
+ ? const HLSViewerHeader()
+ : Container(),
+ );
+ }),
+ const HLSViewerBottomNavigationBar()
+ ],
+ ),
),
- ),
- ///This renders the preview for role component
- Selector<
- MeetingStore,
- Tuple3<
- HMSLocalVideoTrack?,
- HMSLocalAudioTrack?,
- HMSRoleChangeRequest?>>(
- selector: (_, meetingStore) => Tuple3(
- meetingStore.previewForRoleVideoTrack,
- meetingStore.previewForRoleAudioTrack,
- meetingStore
- .currentRoleChangeRequest),
- builder: (_, previewForRoleTracks, __) {
- ///If the preview for role tracks are not null
- ///we show the preview for role component
- ///else we show and empty Container
- if (previewForRoleTracks.item1 != null ||
- previewForRoleTracks.item2 !=
- null ||
- previewForRoleTracks.item3 !=
- null) {
- WidgetsBinding.instance
- .addPostFrameCallback(
- (timeStamp) {
- ///For preview for role component we use the [showGeneralDialog]
- showGeneralDialog(
- context: context,
- pageBuilder: (ctx, _, __) {
- return ListenableProvider
- .value(
- value: context
- .read(),
- child: Scaffold(
- body: SafeArea(
- child: Container(
- color: HMSThemeColors
- .backgroundDim,
- height: MediaQuery.of(
- context)
- .size
- .height,
- width: MediaQuery.of(
- context)
- .size
- .width,
+ ///This renders the preview for role component
+ Selector<
+ MeetingStore,
+ Tuple3<
+ HMSLocalVideoTrack?,
+ HMSLocalAudioTrack?,
+ HMSRoleChangeRequest?>>(
+ selector: (_, meetingStore) => Tuple3(
+ meetingStore
+ .previewForRoleVideoTrack,
+ meetingStore
+ .previewForRoleAudioTrack,
+ meetingStore
+ .currentRoleChangeRequest),
+ builder: (_, previewForRoleTracks, __) {
+ ///If the preview for role tracks are not null
+ ///we show the preview for role component
+ ///else we show and empty Container
+ if (previewForRoleTracks.item1 != null ||
+ previewForRoleTracks.item2 !=
+ null ||
+ previewForRoleTracks.item3 !=
+ null) {
+ WidgetsBinding.instance
+ .addPostFrameCallback(
+ (timeStamp) {
+ ///For preview for role component we use the [showGeneralDialog]
+ showGeneralDialog(
+ context: context,
+ pageBuilder: (ctx, _, __) {
+ return ListenableProvider
+ .value(
+ value: context
+ .read(),
+ child: Scaffold(
+ body: SafeArea(
+ child: Container(
+ color: HMSThemeColors
+ .backgroundDim,
+ height:
+ MediaQuery.of(
+ context)
+ .size
+ .height,
+ width:
+ MediaQuery.of(
+ context)
+ .size
+ .width,
- ///We render the preview for role component
- child: Stack(
- children: [
- ///This renders the video component
- ///[HMSVideoView] is only rendered if video is ON
- ///
- ///else we render the [HMSCircularAvatar]
- Selector<
- MeetingStore,
- bool>(
- selector: (_,
- meetingStore) =>
- meetingStore
- .isVideoOn,
- builder: (_,
- isVideoOn,
- __) {
- return Container(
- height: MediaQuery.of(
- context)
- .size
- .height,
- width: MediaQuery.of(
- context)
- .size
- .width,
- color: HMSThemeColors
- .backgroundDim,
- child: (isVideoOn &&
- previewForRoleTracks.item1 != null)
- ? Center(
- child: HMSVideoView(
- scaleType: ScaleType.SCALE_ASPECT_FILL,
- track: previewForRoleTracks.item1!,
- setMirror: true,
+ ///We render the preview for role component
+ child: Stack(
+ children: [
+ ///This renders the video component
+ ///[HMSVideoView] is only rendered if video is ON
+ ///
+ ///else we render the [HMSCircularAvatar]
+ Selector<
+ MeetingStore,
+ bool>(
+ selector: (_,
+ meetingStore) =>
+ meetingStore
+ .isVideoOn,
+ builder: (_,
+ isVideoOn,
+ __) {
+ return Container(
+ height: MediaQuery.of(context)
+ .size
+ .height,
+ width: MediaQuery.of(context)
+ .size
+ .width,
+ color: HMSThemeColors
+ .backgroundDim,
+ child: (isVideoOn &&
+ previewForRoleTracks.item1 != null)
+ ? Center(
+ child: HMSVideoView(
+ scaleType: ScaleType.SCALE_ASPECT_FILL,
+ track: previewForRoleTracks.item1!,
+ setMirror: true,
+ ),
+ )
+ : Center(
+ child: HMSCircularAvatar(name: context.read().localPeer?.name ?? ""),
),
- )
- : Center(
- child: HMSCircularAvatar(name: context.read().localPeer?.name ?? ""),
- ),
- );
- }),
+ );
+ }),
- ///This renders the preview for role header
- const PreviewForRoleHeader(),
+ ///This renders the preview for role header
+ const PreviewForRoleHeader(),
- ///This renders the preview for role bottom sheet
- PreviewForRoleBottomSheet(
- meetingStore:
- context.read<
- MeetingStore>(),
- roleChangeRequest: context
- .read<
- MeetingStore>()
- .currentRoleChangeRequest,
- )
- ],
+ ///This renders the preview for role bottom sheet
+ PreviewForRoleBottomSheet(
+ meetingStore:
+ context.read<
+ MeetingStore>(),
+ roleChangeRequest: context
+ .read<
+ MeetingStore>()
+ .currentRoleChangeRequest,
+ )
+ ],
+ ),
),
),
),
- ),
+ );
+ });
+ });
+ }
+ return Container();
+ }),
+
+ Selector(
+ selector: (_, meetingStore) =>
+ meetingStore.hmsTrackChangeRequest,
+ builder:
+ (_, hmsTrackChangeRequest, __) {
+ if (hmsTrackChangeRequest != null) {
+ HMSTrackChangeRequest
+ currentRequest =
+ hmsTrackChangeRequest;
+ context
+ .read()
+ .hmsTrackChangeRequest = null;
+ WidgetsBinding.instance
+ .addPostFrameCallback((_) {
+ UtilityComponents
+ .showTrackChangeDialog(
+ context, currentRequest);
+ });
+ }
+ return const SizedBox();
+ }),
+ Selector(
+ selector: (_, meetingStore) =>
+ meetingStore
+ .showAudioDeviceChangePopup,
+ builder: (_, showAudioDeviceChangePopup,
+ __) {
+ if (showAudioDeviceChangePopup) {
+ context
+ .read()
+ .showAudioDeviceChangePopup =
+ false;
+ WidgetsBinding.instance
+ .addPostFrameCallback((_) {
+ showDialog(
+ context: context,
+ builder: (_) =>
+ AudioDeviceChangeDialog(
+ currentAudioDevice: context
+ .read<
+ MeetingStore>()
+ .currentAudioOutputDevice!,
+ audioDevicesList: context
+ .read<
+ MeetingStore>()
+ .availableAudioOutputDevices,
+ changeAudioDevice:
+ (audioDevice) {
+ context
+ .read<
+ MeetingStore>()
+ .switchAudioOutput(
+ audioDevice:
+ audioDevice);
+ },
+ ));
+ });
+ }
+ return const SizedBox();
+ }),
+ Selector, int>>(
+ selector: (_, meetingStore) => Tuple2(
+ meetingStore.toasts,
+ meetingStore.toasts.length),
+ builder: (_, toastsItem, __) {
+ if (toastsItem.item1.isEmpty) {
+ return Container();
+ }
+ return Stack(
+ children: toastsItem.item1
+ .sublist(0,
+ min(3, toastsItem.item2))
+ .asMap()
+ .entries
+ .map((toasts) {
+ var meetingStore =
+ context.read();
+ return Selector(
+ selector: (_, hlsPlayerStore) =>
+ hlsPlayerStore
+ .areStreamControlsVisible,
+ builder: (_,
+ areStreamControlsVisible,
+ __) {
+ return AnimatedPositioned(
+ duration: const Duration(
+ milliseconds: 200),
+ bottom:
+ (areStreamControlsVisible
+ ? 28.0
+ : 10.0) +
+ 8 * toasts.key,
+ left: 5,
+ child: ToastWidget(
+ toast: toasts.value,
+ index: toasts.key,
+ toastsCount:
+ toastsItem.item2,
+ meetingStore:
+ meetingStore),
);
});
- });
- }
- return Container();
- }),
- Selector(
- selector: (_, meetingStore) =>
- meetingStore.reconnecting,
- builder: (_, reconnecting, __) {
- if (reconnecting) {
- return UtilityComponents
- .showReconnectingDialog(context);
- }
- return const SizedBox();
- }),
- if (failureData.item2 != null &&
- (failureData.item2?.code?.errorCode ==
- 1003 ||
- failureData.item2?.code?.errorCode ==
- 2000 ||
- failureData.item2?.code?.errorCode ==
- 4005))
- UtilityComponents.showFailureError(
- failureData.item2!,
- context,
- () => context
- .read()
- .leave())
- ],
+ }).toList());
+ }),
+
+ Selector(
+ selector: (_, meetingStore) =>
+ meetingStore.reconnecting,
+ builder: (_, reconnecting, __) {
+ if (reconnecting) {
+ return UtilityComponents
+ .showReconnectingDialog(
+ context);
+ }
+ return const SizedBox();
+ }),
+ if (failureData.item2 != null &&
+ (failureData.item2?.code?.errorCode ==
+ 1003 ||
+ failureData
+ .item2?.code?.errorCode ==
+ 2000 ||
+ failureData
+ .item2?.code?.errorCode ==
+ 4005))
+ UtilityComponents.showFailureError(
+ failureData.item2!,
+ context,
+ () => context
+ .read()
+ .leave())
+ ],
+ ),
),
),
),
diff --git a/packages/hms_room_kit/lib/src/hls_viewer/overlay_chat_component.dart b/packages/hms_room_kit/lib/src/hls_viewer/overlay_chat_component.dart
index b051cb8c9..9eca6a19c 100644
--- a/packages/hms_room_kit/lib/src/hls_viewer/overlay_chat_component.dart
+++ b/packages/hms_room_kit/lib/src/hls_viewer/overlay_chat_component.dart
@@ -223,6 +223,8 @@ class _OverlayChatComponentState extends State {
),
GestureDetector(
onTap: () {
+ var meetingStore =
+ context.read();
showModalBottomSheet(
isScrollControlled: true,
backgroundColor:
@@ -235,8 +237,7 @@ class _OverlayChatComponentState extends State {
context: context,
builder: (ctx) =>
ChangeNotifierProvider.value(
- value: context
- .read(),
+ value: meetingStore,
child:
ChatUtilitiesBottomSheet(
message: data.item1[index],
diff --git a/packages/hms_room_kit/lib/src/hmssdk_interactor.dart b/packages/hms_room_kit/lib/src/hmssdk_interactor.dart
index 558007313..8e22d78d4 100644
--- a/packages/hms_room_kit/lib/src/hmssdk_interactor.dart
+++ b/packages/hms_room_kit/lib/src/hmssdk_interactor.dart
@@ -413,4 +413,40 @@ class HMSSDKInteractor {
hmsSDK.lowerRemotePeerHand(
forPeer: forPeer, hmsActionResultListener: hmsActionResultListener);
}
+
+ void quickStartPoll(
+ {required HMSPollBuilder pollBuilder,
+ HMSActionResultListener? hmsActionResultListener}) {
+ HMSPollInteractivityCenter.quickStartPoll(
+ pollBuilder: pollBuilder,
+ hmsActionResultListener: hmsActionResultListener);
+ }
+
+ Future addSingleChoicePollResponse(
+ {required HMSPoll poll,
+ required HMSPollQuestion question,
+ required HMSPollQuestionOption pollQuestionOption,
+ HMSPeer? peer}) {
+ return HMSPollInteractivityCenter.addSingleChoicePollResponse(
+ hmsPoll: poll,
+ pollQuestion: question,
+ optionSelected: pollQuestionOption,
+ peer: peer);
+ }
+
+ Future addMultiChoicePollResponse(
+ {required HMSPoll poll,
+ required HMSPollQuestion question,
+ required List pollQuestionOption,
+ HMSPeer? peer}) {
+ return HMSPollInteractivityCenter.addMultiChoicePollResponse(
+ hmsPoll: poll,
+ pollQuestion: question,
+ optionsSelected: pollQuestionOption,
+ peer: peer);
+ }
+
+ Future stopPoll({required HMSPoll poll}) {
+ return HMSPollInteractivityCenter.stopPoll(poll: poll);
+ }
}
diff --git a/packages/hms_room_kit/lib/src/meeting/meeting_header.dart b/packages/hms_room_kit/lib/src/meeting/meeting_header.dart
index 49b4fc786..66bc25957 100644
--- a/packages/hms_room_kit/lib/src/meeting/meeting_header.dart
+++ b/packages/hms_room_kit/lib/src/meeting/meeting_header.dart
@@ -4,12 +4,13 @@ import 'dart:developer';
///Package imports
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
-import 'package:hms_room_kit/src/meeting/meeting_navigation_visibility_controller.dart';
import 'package:hmssdk_flutter/hmssdk_flutter.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
///Project imports
+import 'package:hms_room_kit/src/meeting/meeting_navigation_visibility_controller.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/live_badge.dart';
import 'package:hms_room_kit/hms_room_kit.dart';
import 'package:hms_room_kit/src/layout_api/hms_room_layout.dart';
import 'package:hms_room_kit/src/meeting/meeting_store.dart';
@@ -89,24 +90,7 @@ class _MeetingHeaderState extends State {
HMSStreamingState.started),
builder: (_, isHLSStarted, __) {
return isHLSStarted
- ? Container(
- height: 24,
- width: 43,
- decoration: BoxDecoration(
- color: HMSThemeColors
- .alertErrorDefault,
- borderRadius:
- BorderRadius.circular(4)),
- child: Center(
- child: HMSTitleText(
- text: "LIVE",
- fontSize: 10,
- lineHeight: 16,
- letterSpacing: 1.5,
- textColor: HMSThemeColors
- .alertErrorBrighter),
- ),
- )
+ ? const LiveBadge()
: Container();
}),
const SizedBox(
diff --git a/packages/hms_room_kit/lib/src/meeting/meeting_page.dart b/packages/hms_room_kit/lib/src/meeting/meeting_page.dart
index 2410aa88e..fd27b8d46 100644
--- a/packages/hms_room_kit/lib/src/meeting/meeting_page.dart
+++ b/packages/hms_room_kit/lib/src/meeting/meeting_page.dart
@@ -5,6 +5,7 @@ import 'dart:math';
///Package imports
import 'package:flutter/material.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
+import 'package:hms_room_kit/src/widgets/toasts/toast_widget.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'package:hmssdk_flutter/hmssdk_flutter.dart';
@@ -15,11 +16,7 @@ import 'package:hms_room_kit/src/meeting/meeting_grid_component.dart';
import 'package:hms_room_kit/src/meeting/meeting_navigation_visibility_controller.dart';
import 'package:hms_room_kit/src/meeting/meeting_bottom_navigation_bar.dart';
import 'package:hms_room_kit/src/meeting/meeting_header.dart';
-import 'package:hms_room_kit/src/widgets/toasts/hms_bring_on_stage_toast.dart';
-import 'package:hms_room_kit/src/widgets/toasts/hms_local_screen_share_toast.dart';
-import 'package:hms_room_kit/src/widgets/toasts/hms_role_change_decline_toast.dart';
import 'package:hms_room_kit/src/widgets/toasts/hms_toast_model.dart';
-import 'package:hms_room_kit/src/widgets/toasts/hms_toasts_type.dart';
import 'package:hms_room_kit/src/common/utility_components.dart';
import 'package:hms_room_kit/src/widgets/app_dialogs/audio_device_change_dialog.dart';
import 'package:hms_room_kit/src/meeting/meeting_store.dart';
@@ -28,8 +25,6 @@ import 'package:hms_room_kit/src/preview_for_role/preview_for_role_bottom_sheet.
import 'package:hms_room_kit/src/preview_for_role/preview_for_role_header.dart';
import 'package:hms_room_kit/src/widgets/common_widgets/hms_circular_avatar.dart';
import 'package:hms_room_kit/src/widgets/common_widgets/hms_left_room_screen.dart';
-import 'package:hms_room_kit/src/widgets/toasts/hms_recording_error_toast.dart';
-import 'package:hms_room_kit/src/widgets/toasts/hms_chat_pause_resume_toast.dart';
///[MeetingPage] is the main page of the meeting
///It takes the following parameters:
@@ -77,60 +72,6 @@ class _MeetingPageState extends State {
context.read().initForegroundTask();
}
- ///This method returns the toast according to the type of toast
- Widget getToast(HMSToastModel toast, int index, int toastsCount) {
- switch (toast.hmsToastType) {
- case HMSToastsType.roleChangeToast:
- return HMSBringOnStageToast(
- toastColor: Utilities.getToastColor(index, toastsCount),
- peer: toast.toastData,
- meetingStore: context.read(),
- );
- case HMSToastsType.recordingErrorToast:
- return HMSRecordingErrorToast(
- recordingError: toast.toastData,
- meetingStore: context.read());
- case HMSToastsType.localScreenshareToast:
- return HMSLocalScreenShareToast(
- toastColor: Utilities.getToastColor(index, toastsCount),
- meetingStore: context.read(),
- );
-
- case HMSToastsType.roleChangeDeclineToast:
- return HMSRoleChangeDeclineToast(
- peer: toast.toastData,
- toastColor: Utilities.getToastColor(index, toastsCount),
- meetingStore: context.read(),
- );
- case HMSToastsType.chatPauseResumeToast:
- return HMSChatPauseResumeToast(
- isChatEnabled: toast.toastData["enabled"],
- userName: toast.toastData["updatedBy"],
- meetingStore: context.read());
- default:
- return const SizedBox();
- }
- }
-
- ///This method returns the scale of the toast according to the index and the total number of toasts
- double _getToastScale(int index, int toastsCount) {
- if (toastsCount == 1) {
- return 1;
- } else if (toastsCount == 2) {
- if (index == 0) {
- return 0.95;
- }
- return 1;
- } else {
- if (index == 0) {
- return 0.90;
- } else if (index == 1) {
- return 0.95;
- }
- return 1;
- }
- }
-
@override
Widget build(BuildContext context) {
return WillPopScope(
@@ -419,19 +360,20 @@ class _MeetingPageState extends State {
.asMap()
.entries
.map((toasts) {
- return Positioned(
- bottom:
- 48.0 + 8 * toasts.key,
- left: 5,
- child: Transform.scale(
- scale: _getToastScale(
- toasts.key,
- toastsItem.item2),
- child: getToast(
- toasts.value,
- toasts.key,
- toastsItem.item2),
- ));
+ var meetingStore = context
+ .read();
+ return ChangeNotifierProvider
+ .value(
+ value:
+ _visibilityController,
+ child: ToastWidget(
+ toast: toasts.value,
+ index: toasts.key,
+ toastsCount:
+ toastsItem.item2,
+ meetingStore:
+ meetingStore),
+ );
}).toList());
}),
Selector(
diff --git a/packages/hms_room_kit/lib/src/meeting/meeting_store.dart b/packages/hms_room_kit/lib/src/meeting/meeting_store.dart
index 2852ca961..ccd9c7ddc 100644
--- a/packages/hms_room_kit/lib/src/meeting/meeting_store.dart
+++ b/packages/hms_room_kit/lib/src/meeting/meeting_store.dart
@@ -2,7 +2,6 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
-import 'dart:math' as math;
//Package imports
import 'package:hmssdk_flutter/hmssdk_flutter.dart';
@@ -12,6 +11,7 @@ import 'package:flutter_foreground_task/flutter_foreground_task.dart';
import 'package:intl/intl.dart';
//Project imports
+import 'package:hms_room_kit/src/model/poll_store.dart';
import 'package:hms_room_kit/src/model/participant_store.dart';
import 'package:hms_room_kit/hms_room_kit.dart';
import 'package:hms_room_kit/src/enums/meeting_mode.dart';
@@ -34,7 +34,8 @@ class MeetingStore extends ChangeNotifier
HMSStatsListener,
HMSLogListener,
HMSKeyChangeListener,
- HMSHLSPlaybackEventsListener {
+ HMSHLSPlaybackEventsListener,
+ HMSPollListener {
late HMSSDKInteractor _hmsSDKInteractor;
MeetingStore({required HMSSDKInteractor hmsSDKInteractor}) {
@@ -250,6 +251,15 @@ class MeetingStore extends ChangeNotifier
///Stores whether Chat State
Map chatControls = {"enabled": true, "updatedBy": null};
+ ///Polls
+ ///Stores the poll questions
+ List pollQuestions = [];
+
+ List hlsViewerPolls = [];
+
+ ///List of bottom sheets currently open
+ List bottomSheets = [];
+
Future join(String userName, String roomCode,
{HMSConfig? roomConfig}) async {
//If roomConfig is null then only we call the methods to get the authToken
@@ -281,6 +291,7 @@ class MeetingStore extends ChangeNotifier
_hmsSDKInteractor.addUpdateListener(this);
_hmsSDKInteractor.addLogsListener(this);
+ HMSPollInteractivityCenter.addPollUpdateListener(listener: this);
HMSHLSPlayerController.addHMSHLSPlaybackEventsListener(this);
WidgetsBinding.instance.addObserver(this);
setMeetingModeUsingLayoutApi();
@@ -454,6 +465,10 @@ class MeetingStore extends ChangeNotifier
case HMSToastsType.errorToast:
toasts.removeWhere(
(toast) => toast.hmsToastType == HMSToastsType.errorToast);
+ case HMSToastsType.pollStartedToast:
+ toasts.removeWhere((toast) =>
+ (toast.hmsToastType == HMSToastsType.pollStartedToast) &&
+ (toast.toastData.poll.pollId == data));
}
notifyListeners();
}
@@ -1365,6 +1380,26 @@ class MeetingStore extends ChangeNotifier
}
}
+ ///Function to add bottomsheet context in the bottomsheets list
+ void addBottomSheet(BuildContext context) {
+ bottomSheets.add(context);
+ }
+
+ ///Function to remove bottomsheet context from bottomsheets list
+ void removeBottomSheet(BuildContext context) {
+ bottomSheets.remove(context);
+ }
+
+ ///Function to remove all bottomsheets from list
+ void removeAllBottomSheets() {
+ for (var bottomSheetContext in bottomSheets) {
+ if (bottomSheetContext.mounted) {
+ Navigator.pop(bottomSheetContext);
+ }
+ }
+ bottomSheets.clear();
+ }
+
void removePeer(HMSPeer peer) {
peers.remove(peer);
participantsInMeetingMap[peer.role.name]
@@ -1506,6 +1541,7 @@ class MeetingStore extends ChangeNotifier
case HMSPeerUpdate.roleUpdated:
if (peer.isLocal) {
+ removeAllBottomSheets();
getSpotlightPeer();
setPreviousRole(localPeer?.role.name ?? "");
resetLayout(peer.role.name);
@@ -2198,6 +2234,37 @@ class MeetingStore extends ChangeNotifier
notifyListeners();
}
+ ///Polls and Quiz
+
+ ///Method to start poll
+ void quickStartPoll(HMSPollBuilder pollBuilder) {
+ _hmsSDKInteractor.quickStartPoll(
+ pollBuilder: pollBuilder, hmsActionResultListener: this);
+ }
+
+ ///Method to add Poll Response
+ void addSingleChoicePollResponse(HMSPoll poll, HMSPollQuestion question,
+ HMSPollQuestionOption pollQuestionOption) {
+ _hmsSDKInteractor.addSingleChoicePollResponse(
+ poll: poll,
+ question: question,
+ pollQuestionOption: pollQuestionOption,
+ peer: localPeer);
+ }
+
+ void addMultiChoicePollResponse(HMSPoll poll, HMSPollQuestion question,
+ List pollQuestionOption) {
+ _hmsSDKInteractor.addMultiChoicePollResponse(
+ poll: poll,
+ question: question,
+ pollQuestionOption: pollQuestionOption,
+ peer: localPeer);
+ }
+
+ void stopPoll(HMSPoll poll) {
+ _hmsSDKInteractor.stopPoll(poll: poll);
+ }
+
//Get onSuccess or onException callbacks for HMSActionResultListenerMethod
@override
void onSuccess(
@@ -2330,6 +2397,10 @@ class MeetingStore extends ChangeNotifier
break;
case HMSActionResultListenerMethod.lowerRemotePeerHand:
break;
+ case HMSActionResultListenerMethod.addSingleChoicePollResponse:
+ break;
+ case HMSActionResultListenerMethod.addMultiChoicePollResponse:
+ break;
default:
log("ActionResultListener onException-> method: ${methodType.toString()}Could not find a valid case while switching");
break;
@@ -2415,6 +2486,10 @@ class MeetingStore extends ChangeNotifier
break;
case HMSActionResultListenerMethod.lowerRemotePeerHand:
break;
+ case HMSActionResultListenerMethod.addSingleChoicePollResponse:
+ break;
+ case HMSActionResultListenerMethod.addMultiChoicePollResponse:
+ break;
default:
log("ActionResultListener onException-> method: ${methodType.toString()} Could not find a valid case while switching");
break;
@@ -2499,12 +2574,32 @@ class MeetingStore extends ChangeNotifier
@override
void onCue({required HMSHLSCue hlsCue}) {
+ log("onCue -> payload:${hlsCue.startDate}");
/**
* Here we use a list of alignments and select an alignment at random and use it
* to position the toast for timed metadata
*/
-
if (hlsCue.payload != null) {
+ /*
+ * Below code shows the poll for hls-viewer who are viewing stream at a delay.
+ * Here we get pollId from the payload and we find the poll object
+ * from `onPollUpdate` we use this object to show the toast for the poll.
+ * Mock payload for poll looks like "poll:{poll_id}"
+ */
+ if (hlsCue.payload!.startsWith("poll:")) {
+ var pollId = hlsCue.payload?.replaceFirst(RegExp(r'^poll:'), "");
+ int? index = hlsViewerPolls
+ .indexWhere((element) => element.poll.pollId == pollId);
+ if (index != -1) {
+ toasts.add(HMSToastModel(hlsViewerPolls[index],
+ hmsToastType: HMSToastsType.pollStartedToast));
+ pollQuestions.add(hlsViewerPolls[index]);
+ hlsViewerPolls.removeAt(index);
+ notifyListeners();
+ }
+ }
+
+ /*******************************This is the implementation for showing emoji's in HLS *******************/
/**
* Generally we are assuming that the timed metadata payload will be a JSON String
* but if it's a normal string then this throws the format exception
@@ -2512,23 +2607,24 @@ class MeetingStore extends ChangeNotifier
* The toast is displayed for the time duration hlsCue.endDate - hlsCue.startDate
* If endDate is null then toast is displayed for 2 seconds by default
*/
- try {
- final Map data = jsonDecode(hlsCue.payload!);
- Utilities.showTimedMetadata(
- Utilities.getTimedMetadataEmojiFromId(data["emojiId"]),
- time: hlsCue.endDate == null
- ? 2
- : (hlsCue.endDate!.difference(hlsCue.startDate)).inSeconds,
- align: Utilities.timedMetadataAlignment[math.Random()
- .nextInt(Utilities.timedMetadataAlignment.length)]);
- } catch (e) {
- Utilities.showTimedMetadata(hlsCue.payload!,
- time: hlsCue.endDate == null
- ? 2
- : (hlsCue.endDate!.difference(hlsCue.startDate)).inSeconds,
- align: Utilities.timedMetadataAlignment[math.Random()
- .nextInt(Utilities.timedMetadataAlignment.length)]);
- }
+ // try {
+ // final Map data = jsonDecode(hlsCue.payload!);
+ // Utilities.showTimedMetadata(
+ // Utilities.getTimedMetadataEmojiFromId(data["emojiId"]),
+ // time: hlsCue.endDate == null
+ // ? 2
+ // : (hlsCue.endDate!.difference(hlsCue.startDate)).inSeconds,
+ // align: Utilities.timedMetadataAlignment[math.Random()
+ // .nextInt(Utilities.timedMetadataAlignment.length)]);
+ // } catch (e) {
+ // Utilities.showTimedMetadata(hlsCue.payload!,
+ // time: hlsCue.endDate == null
+ // ? 2
+ // : (hlsCue.endDate!.difference(hlsCue.startDate)).inSeconds,
+ // align: Utilities.timedMetadataAlignment[math.Random()
+ // .nextInt(Utilities.timedMetadataAlignment.length)]);
+ // }
+ /************************************************************************************************************/
}
}
@@ -2573,4 +2669,74 @@ class MeetingStore extends ChangeNotifier
addPeer(peer);
}
}
+
+ @override
+ void onPollUpdate(
+ {required HMSPoll poll, required HMSPollUpdateType pollUpdateType}) {
+ log("onPollUpdate -> poll $poll updateType: $pollUpdateType startedAt: ${poll.startedAt}");
+ switch (pollUpdateType) {
+ ///If the poll is started we add the poll in questions list
+ case HMSPollUpdateType.started:
+
+ /*
+ * Here we check whether the peer has permission to view polls
+ * Then if the user is a realtime user we show the poll immediately
+ * while for hls viewer we show the poll based on the `onCue` event i.e.
+ * timed metadata event for poll
+ */
+ if ((localPeer?.role.permissions.pollRead ?? false) ||
+ (localPeer?.role.permissions.pollWrite ?? false)) {
+ int index = pollQuestions
+ .indexWhere((element) => element.poll.pollId == poll.pollId);
+ if (index == -1) {
+ HMSPollStore store = HMSPollStore(poll: poll);
+ if (HMSRoomLayout.peerType == PeerRoleType.conferencing) {
+ pollQuestions.add(store);
+ toasts.add(HMSToastModel(store,
+ hmsToastType: HMSToastsType.pollStartedToast));
+ notifyListeners();
+ } else {
+ /*
+ * Here we check whether the poll start time is
+ * more than 20 secs older from now or not. We have kept 20 secs
+ * since the stream playback rolling window time is
+ * set to 20 by default i.e. if the time difference is less than 20 secs
+ * we will get the [onCue] callback again. If its greater than 20
+ * we show the toast immediately for the user to vote.
+ */
+ if (poll.startedAt != null &&
+ (DateTime.now().difference(poll.startedAt!) >
+ const Duration(seconds: 20))) {
+ pollQuestions.add(store);
+ toasts.add(HMSToastModel(store,
+ hmsToastType: HMSToastsType.pollStartedToast));
+ } else {
+ hlsViewerPolls.add(store);
+ }
+ }
+ }
+ }
+ break;
+
+ ///In other cases we just update the state of the poll
+ case HMSPollUpdateType.resultsupdated:
+ int index = pollQuestions
+ .indexWhere((element) => element.poll.pollId == poll.pollId);
+ if (index != -1) {
+ pollQuestions[index].updateState(poll);
+ }
+ notifyListeners();
+ break;
+
+ case HMSPollUpdateType.stopped:
+ removeToast(HMSToastsType.pollStartedToast, data: poll.pollId);
+ int index = pollQuestions
+ .indexWhere((element) => element.poll.pollId == poll.pollId);
+ if (index != -1) {
+ pollQuestions[index].updateState(poll);
+ }
+ notifyListeners();
+ break;
+ }
+ }
}
diff --git a/packages/hms_room_kit/lib/src/meeting/meeting_toasts.dart b/packages/hms_room_kit/lib/src/meeting/meeting_toasts.dart
new file mode 100644
index 000000000..9873b83ba
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/meeting/meeting_toasts.dart
@@ -0,0 +1,46 @@
+///Package imports
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+///Project imports
+import 'package:hms_room_kit/src/meeting/meeting_navigation_visibility_controller.dart';
+import 'package:hms_room_kit/src/meeting/meeting_store.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_toast_model.dart';
+import 'package:hms_room_kit/src/widgets/toasts/toast_widget.dart';
+
+///[MeetingToasts] widget returns toast to be displayed on meeting UI
+///A separate widget is used to avoid rebuild of whole meeting ui when controls are hidden
+class MeetingToasts extends StatelessWidget {
+ final HMSToastModel toast;
+ final int index;
+ final int toastsCount;
+ final MeetingStore meetingStore;
+
+ const MeetingToasts({
+ super.key,
+ required this.toast,
+ required this.index,
+ required this.toastsCount,
+ required this.meetingStore,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Selector(
+ selector: (_, meetingNavigationVisibilityController) =>
+ meetingNavigationVisibilityController.showControls,
+ builder: (_, showControls, __) {
+ return AnimatedPositioned(
+ duration: const Duration(milliseconds: 200),
+ bottom: (showControls ? 28.0 : 10.0) + 8 * index,
+ left: 5,
+ child: ToastWidget(
+ toast: toast,
+ index: index,
+ toastsCount: toastsCount,
+ meetingStore: meetingStore),
+ );
+ },
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/model/poll_store.dart b/packages/hms_room_kit/lib/src/model/poll_store.dart
new file mode 100644
index 000000000..2925fff52
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/model/poll_store.dart
@@ -0,0 +1,20 @@
+///Package imports
+import 'package:flutter/material.dart';
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+
+///[HMSPollStore] is used to notify updates for the poll state
+///
+///This is useful in updating polls UI based on state in realtime
+///without rebuilding the whole UI
+class HMSPollStore extends ChangeNotifier {
+ HMSPoll poll;
+
+ HMSPollStore({required this.poll});
+
+ ///Here we update the poll object with the new object
+ ///and notify the listeners
+ void updateState(HMSPoll poll) {
+ this.poll = poll;
+ notifyListeners();
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/app_utilities_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/app_utilities_bottom_sheet.dart
index 68712e88c..3528d5be7 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/app_utilities_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/app_utilities_bottom_sheet.dart
@@ -2,6 +2,7 @@
import 'package:badges/badges.dart' as badge;
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:hms_room_kit/src/widgets/bottom_sheets/poll_and_quiz_bottom_sheet.dart';
import 'package:hmssdk_flutter/hmssdk_flutter.dart';
import 'package:provider/provider.dart';
@@ -27,6 +28,18 @@ class AppUtilitiesBottomSheet extends StatefulWidget {
}
class _AppUtilitiesBottomSheetState extends State {
+ @override
+ void initState() {
+ super.initState();
+ context.read().addBottomSheet(context);
+ }
+
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
+ }
+
@override
Widget build(BuildContext context) {
MeetingStore meetingStore = context.read();
@@ -177,10 +190,10 @@ class _AppUtilitiesBottomSheetState extends State {
),
),
optionText:
- meetingStore.isBRB ? "I'm Back" : "Be Right Back")
+ meetingStore.isBRB ? "I'm Back" : "Be Right Back"),
///This renders the raise hand option
- ,
+
MoreOptionItem(
onTap: () async {
context.read().toggleLocalPeerHandRaise();
@@ -199,6 +212,38 @@ class _AppUtilitiesBottomSheetState extends State {
? "Lower Hand"
: "Raise Hand"),
+ ///This renders the polls and quizzes option
+ if ((meetingStore.localPeer?.role.permissions.pollRead ??
+ false) ||
+ (meetingStore.localPeer?.role.permissions.pollWrite ??
+ false))
+ MoreOptionItem(
+ onTap: () {
+ Navigator.pop(context);
+ showModalBottomSheet(
+ isScrollControlled: true,
+ backgroundColor: HMSThemeColors.surfaceDim,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16)),
+ ),
+ context: context,
+ builder: (ctx) => ChangeNotifierProvider.value(
+ value: meetingStore,
+ child: const PollAndQuizBottomSheet()),
+ );
+ },
+ optionIcon: SvgPicture.asset(
+ "packages/hms_room_kit/lib/src/assets/icons/polls.svg",
+ height: 20,
+ width: 20,
+ colorFilter: ColorFilter.mode(
+ HMSThemeColors.onSurfaceHighEmphasis,
+ BlendMode.srcIn),
+ ),
+ optionText: "Polls and Quizzes"),
+
///This renders the recording option
///This option is only rendered if the local peer has the permission to
///start/stop browser recording
@@ -252,31 +297,35 @@ class _AppUtilitiesBottomSheetState extends State {
topRight: Radius.circular(16)),
),
context: context,
- builder: (ctx) => EndServiceBottomSheet(
- onButtonPressed: () =>
- meetingStore.stopRtmpAndRecording(),
- title: HMSTitleText(
- text: "Stop Recording",
- textColor: HMSThemeColors.alertErrorDefault,
- letterSpacing: 0.15,
- fontSize: 20,
- ),
- bottomSheetTitleIcon: SvgPicture.asset(
- "packages/hms_room_kit/lib/src/assets/icons/alert.svg",
- height: 20,
- width: 20,
- colorFilter: ColorFilter.mode(
- HMSThemeColors.alertErrorDefault,
- BlendMode.srcIn),
- ),
- subTitle: HMSSubheadingText(
- text:
- "Are you sure you want to stop recording? You\n can’t undo this action.",
- maxLines: 2,
- textColor:
- HMSThemeColors.onSurfaceMediumEmphasis,
+ builder: (ctx) => ChangeNotifierProvider.value(
+ value: meetingStore,
+ child: EndServiceBottomSheet(
+ onButtonPressed: () =>
+ meetingStore.stopRtmpAndRecording(),
+ title: HMSTitleText(
+ text: "Stop Recording",
+ textColor:
+ HMSThemeColors.alertErrorDefault,
+ letterSpacing: 0.15,
+ fontSize: 20,
+ ),
+ bottomSheetTitleIcon: SvgPicture.asset(
+ "packages/hms_room_kit/lib/src/assets/icons/alert.svg",
+ height: 20,
+ width: 20,
+ colorFilter: ColorFilter.mode(
+ HMSThemeColors.alertErrorDefault,
+ BlendMode.srcIn),
+ ),
+ subTitle: HMSSubheadingText(
+ text:
+ "Are you sure you want to stop recording? You\n can’t undo this action.",
+ maxLines: 2,
+ textColor: HMSThemeColors
+ .onSurfaceMediumEmphasis,
+ ),
+ buttonText: "Stop Recording",
),
- buttonText: "Stop Recording",
),
);
} else {
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/audio_settings_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/audio_settings_bottom_sheet.dart
index c4bccd18d..8a38ddb4e 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/audio_settings_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/audio_settings_bottom_sheet.dart
@@ -27,6 +27,13 @@ class _AudioSettingsBottomSheetState extends State {
@override
void initState() {
super.initState();
+ context.read().addBottomSheet(context);
+ }
+
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
}
@override
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/change_name_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/change_name_bottom_sheet.dart
index 97f02d7d6..7ba5b474e 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/change_name_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/change_name_bottom_sheet.dart
@@ -29,6 +29,7 @@ class _ChangeNameBottomSheetState extends State {
void initState() {
super.initState();
nameController.text = context.read().localPeer?.name ?? "";
+ context.read().addBottomSheet(context);
}
@override
@@ -37,6 +38,12 @@ class _ChangeNameBottomSheetState extends State {
super.dispose();
}
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
+ }
+
///This function is called when the change name button is clicked
void _changeName() {
///We only change the name
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_bottom_sheet.dart
index 5da03b874..8286a0368 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_bottom_sheet.dart
@@ -34,6 +34,12 @@ class _ChatBottomSheetState extends State {
final ScrollController _scrollController = ScrollController();
final DateFormat formatter = DateFormat('hh:mm a');
+ @override
+ void initState() {
+ super.initState();
+ setRecipientChipValue();
+ }
+
@override
void dispose() {
_scrollController.dispose();
@@ -49,12 +55,6 @@ class _ChatBottomSheetState extends State {
}
}
- @override
- void initState() {
- super.initState();
- setRecipientChipValue();
- }
-
///This function sets the value of the recipient chip
void setRecipientChipValue() {
dynamic currentValue = context.read().recipientSelectorValue;
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_only_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_only_bottom_sheet.dart
index 3406c8789..7c5ff0568 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_only_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_only_bottom_sheet.dart
@@ -14,9 +14,14 @@ import 'package:provider/provider.dart';
///[ChatOnlyBottomSheet] is a bottom sheet that is used to render the bottom sheet to show chat only when participants
///list is disabled from dashboard
-class ChatOnlyBottomSheet extends StatelessWidget {
+class ChatOnlyBottomSheet extends StatefulWidget {
const ChatOnlyBottomSheet({Key? key}) : super(key: key);
+ @override
+ State createState() => _ChatOnlyBottomSheetState();
+}
+
+class _ChatOnlyBottomSheetState extends State {
@override
Widget build(BuildContext context) {
return SafeArea(
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_utilities_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_utilities_bottom_sheet.dart
index 3cf62ec6a..4d24872fd 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_utilities_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/chat_utilities_bottom_sheet.dart
@@ -32,6 +32,7 @@ class _ChatUtilitiesBottomSheetState extends State {
@override
initState() {
super.initState();
+ context.read().addBottomSheet(context);
isPinned = context.read().pinnedMessages.indexWhere(
(element) => element["id"] == widget.message.messageId) !=
-1;
@@ -41,6 +42,12 @@ class _ChatUtilitiesBottomSheetState extends State {
-1;
}
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
+ }
+
@override
Widget build(BuildContext context) {
return FractionallySizedBox(
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/end_service_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/end_service_bottom_sheet.dart
index 7583deb95..9eec948b2 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/end_service_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/end_service_bottom_sheet.dart
@@ -3,8 +3,10 @@ import 'package:flutter/material.dart';
///Project imports
import 'package:hms_room_kit/src/layout_api/hms_theme_colors.dart';
+import 'package:hms_room_kit/src/meeting/meeting_store.dart';
import 'package:hms_room_kit/src/widgets/common_widgets/hms_cross_button.dart';
import 'package:hms_room_kit/src/widgets/common_widgets/hms_title_text.dart';
+import 'package:provider/provider.dart';
///[EndServiceBottomSheet] is a bottom sheet that is used to render the bottom sheet to stop services
///It has following parameters:
@@ -14,7 +16,7 @@ import 'package:hms_room_kit/src/widgets/common_widgets/hms_title_text.dart';
/// [buttonText] is the text of the button
/// [onButtonPressed] is the function that is called when the button is pressed
/// [buttonColor] is the color of the button
-class EndServiceBottomSheet extends StatelessWidget {
+class EndServiceBottomSheet extends StatefulWidget {
final Widget? bottomSheetTitleIcon;
final Widget? title;
final Widget? subTitle;
@@ -31,6 +33,23 @@ class EndServiceBottomSheet extends StatelessWidget {
this.onButtonPressed,
this.buttonColor});
+ @override
+ State createState() => _EndServiceBottomSheetState();
+}
+
+class _EndServiceBottomSheetState extends State {
+ @override
+ void initState() {
+ super.initState();
+ context.read().addBottomSheet(context);
+ }
+
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
+ }
+
@override
Widget build(BuildContext context) {
return FractionallySizedBox(
@@ -46,11 +65,11 @@ class EndServiceBottomSheet extends StatelessWidget {
children: [
Row(
children: [
- bottomSheetTitleIcon ?? const SizedBox(),
+ widget.bottomSheetTitleIcon ?? const SizedBox(),
const SizedBox(
width: 8,
),
- title ?? const SizedBox()
+ widget.title ?? const SizedBox()
],
),
const Row(
@@ -64,7 +83,7 @@ class EndServiceBottomSheet extends StatelessWidget {
const SizedBox(
height: 8,
),
- subTitle ?? const SizedBox(),
+ widget.subTitle ?? const SizedBox(),
const SizedBox(
height: 16,
),
@@ -73,14 +92,15 @@ class EndServiceBottomSheet extends StatelessWidget {
shadowColor:
MaterialStateProperty.all(HMSThemeColors.surfaceDim),
backgroundColor: MaterialStateProperty.all(
- buttonColor ?? HMSThemeColors.alertErrorDefault),
+ widget.buttonColor ??
+ HMSThemeColors.alertErrorDefault),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
))),
onPressed: () {
- if (onButtonPressed != null) {
- onButtonPressed!();
+ if (widget.onButtonPressed != null) {
+ widget.onButtonPressed!();
}
Navigator.pop(context);
},
@@ -88,7 +108,7 @@ class EndServiceBottomSheet extends StatelessWidget {
height: 48,
child: Center(
child: HMSTitleText(
- text: buttonText ?? "",
+ text: widget.buttonText ?? "",
textColor: HMSThemeColors.alertErrorBrighter),
),
))
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/hls_more_options.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/hls_app_utilities_bottom_sheet.dart
similarity index 72%
rename from packages/hms_room_kit/lib/src/widgets/bottom_sheets/hls_more_options.dart
rename to packages/hms_room_kit/lib/src/widgets/bottom_sheets/hls_app_utilities_bottom_sheet.dart
index b7e9e0140..3bcd3860c 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/hls_more_options.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/hls_app_utilities_bottom_sheet.dart
@@ -1,6 +1,7 @@
///Package imports
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:hms_room_kit/src/widgets/bottom_sheets/poll_and_quiz_bottom_sheet.dart';
import 'package:provider/provider.dart';
import 'package:badges/badges.dart' as badge;
@@ -14,20 +15,27 @@ import 'package:hms_room_kit/src/layout_api/hms_room_layout.dart';
import 'package:hms_room_kit/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart';
import 'package:hms_room_kit/src/widgets/tab_widgets/chat_participants_tab_bar.dart';
-///[HLSMoreOptionsBottomSheet] is a bottom sheet that is used to show more options in the meeting
-class HLSMoreOptionsBottomSheet extends StatefulWidget {
- const HLSMoreOptionsBottomSheet({super.key});
+///[HLSAppUtilitiesBottomSheet] is a bottom sheet that is used to show more options in the meeting
+class HLSAppUtilitiesBottomSheet extends StatefulWidget {
+ const HLSAppUtilitiesBottomSheet({super.key});
@override
- State createState() =>
+ State createState() =>
_HLSMoreOptionsBottomSheetBottomSheetState();
}
class _HLSMoreOptionsBottomSheetBottomSheetState
- extends State {
+ extends State {
@override
void initState() {
super.initState();
+ context.read().addBottomSheet(context);
+ }
+
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
}
@override
@@ -67,8 +75,8 @@ class _HLSMoreOptionsBottomSheetBottomSheetState
///Here we render the participants button and the change name button
Wrap(
- spacing: 12,
runSpacing: 24,
+ spacing: MediaQuery.of(context).size.width * 0.005,
children: [
if (HMSRoomLayout.isParticipantsListEnabled)
MoreOptionItem(
@@ -160,7 +168,50 @@ class _HLSMoreOptionsBottomSheetBottomSheetState
HMSThemeColors.onSurfaceHighEmphasis,
BlendMode.srcIn),
),
- optionText: "Change Name")
+ optionText: "Change Name"),
+
+ ///This renders the polls and quizzes option
+ if ((context
+ .read()
+ .localPeer
+ ?.role
+ .permissions
+ .pollRead ??
+ false) ||
+ (context
+ .read()
+ .localPeer
+ ?.role
+ .permissions
+ .pollWrite ??
+ false))
+ MoreOptionItem(
+ onTap: () {
+ var meetingStore = context.read();
+ Navigator.pop(context);
+ showModalBottomSheet(
+ isScrollControlled: true,
+ backgroundColor: HMSThemeColors.surfaceDim,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16)),
+ ),
+ context: context,
+ builder: (ctx) => ChangeNotifierProvider.value(
+ value: meetingStore,
+ child: const PollAndQuizBottomSheet()),
+ );
+ },
+ optionIcon: SvgPicture.asset(
+ "packages/hms_room_kit/lib/src/assets/icons/polls.svg",
+ height: 20,
+ width: 20,
+ colorFilter: ColorFilter.mode(
+ HMSThemeColors.onSurfaceHighEmphasis,
+ BlendMode.srcIn),
+ ),
+ optionText: "Polls and Quizzes"),
],
),
],
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/leave_session_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/leave_session_bottom_sheet.dart
index 00dbda0d9..b85347fb4 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/leave_session_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/leave_session_bottom_sheet.dart
@@ -5,16 +5,37 @@ import 'package:hms_room_kit/src/meeting/meeting_store.dart';
import 'package:hms_room_kit/src/widgets/bottom_sheets/end_service_bottom_sheet.dart';
import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart';
import 'package:hms_room_kit/src/widgets/common_widgets/leave_session_tile.dart';
+import 'package:provider/provider.dart';
-class LeaveSessionBottomSheet extends StatelessWidget {
+class LeaveSessionBottomSheet extends StatefulWidget {
final MeetingStore meetingStore;
const LeaveSessionBottomSheet({super.key, required this.meetingStore});
+ @override
+ State createState() =>
+ _LeaveSessionBottomSheetState();
+}
+
+class _LeaveSessionBottomSheetState extends State {
+ @override
+ void initState() {
+ super.initState();
+ context.read().addBottomSheet(context);
+ }
+
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
+ }
+
@override
Widget build(BuildContext context) {
- return ((meetingStore.localPeer?.role.permissions.endRoom ?? false) ||
- ((meetingStore.localPeer?.role.permissions.hlsStreaming ?? false) &&
- meetingStore.hasHlsStarted))
+ return ((widget.meetingStore.localPeer?.role.permissions.endRoom ??
+ false) ||
+ ((widget.meetingStore.localPeer?.role.permissions.hlsStreaming ??
+ false) &&
+ widget.meetingStore.hasHlsStarted))
? Padding(
padding: const EdgeInsets.only(top: 12.0),
child: Column(
@@ -46,31 +67,34 @@ class LeaveSessionBottomSheet extends StatelessWidget {
topRight: Radius.circular(16)),
),
context: context,
- builder: (ctx) => EndServiceBottomSheet(
- onButtonPressed: () => {
- meetingStore.leave(),
- },
- title: HMSTitleText(
- text: "Leave Session",
- textColor: HMSThemeColors.alertErrorDefault,
- letterSpacing: 0.15,
- fontSize: 20,
- ),
- bottomSheetTitleIcon: SvgPicture.asset(
- "packages/hms_room_kit/lib/src/assets/icons/end_warning.svg",
- height: 20,
- width: 20,
- colorFilter: ColorFilter.mode(
- HMSThemeColors.alertErrorDefault,
- BlendMode.srcIn),
- ),
- subTitle: HMSSubheadingText(
- text:
- "Others will continue after you leave. You can join\n the session again.",
- maxLines: 2,
- textColor: HMSThemeColors.onSurfaceMediumEmphasis,
+ builder: (ctx) => ChangeNotifierProvider.value(
+ value: widget.meetingStore,
+ child: EndServiceBottomSheet(
+ onButtonPressed: () => {
+ widget.meetingStore.leave(),
+ },
+ title: HMSTitleText(
+ text: "Leave Session",
+ textColor: HMSThemeColors.alertErrorDefault,
+ letterSpacing: 0.15,
+ fontSize: 20,
+ ),
+ bottomSheetTitleIcon: SvgPicture.asset(
+ "packages/hms_room_kit/lib/src/assets/icons/end_warning.svg",
+ height: 20,
+ width: 20,
+ colorFilter: ColorFilter.mode(
+ HMSThemeColors.alertErrorDefault,
+ BlendMode.srcIn),
+ ),
+ subTitle: HMSSubheadingText(
+ text:
+ "Others will continue after you leave. You can join\n the session again.",
+ maxLines: 2,
+ textColor: HMSThemeColors.onSurfaceMediumEmphasis,
+ ),
+ buttonText: "Leave Session",
),
- buttonText: "Leave Session",
),
)
},
@@ -83,17 +107,17 @@ class LeaveSessionBottomSheet extends StatelessWidget {
HMSThemeColors.alertErrorBrighter, BlendMode.srcIn),
semanticsLabel: "leave_room_button",
),
- title:
- ((meetingStore.localPeer?.role.permissions.hlsStreaming ??
- false) &&
- meetingStore.hasHlsStarted)
- ? "End Stream"
- : "End Session",
+ title: ((widget.meetingStore.localPeer?.role.permissions
+ .hlsStreaming ??
+ false) &&
+ widget.meetingStore.hasHlsStarted)
+ ? "End Stream"
+ : "End Session",
titleColor: HMSThemeColors.alertErrorBrighter,
- subTitle: ((meetingStore
- .localPeer?.role.permissions.hlsStreaming ??
+ subTitle: ((widget.meetingStore.localPeer?.role.permissions
+ .hlsStreaming ??
false) &&
- meetingStore.hasHlsStarted)
+ widget.meetingStore.hasHlsStarted)
? "The stream will end for everyone after they’ve watched it."
: "The session will end for everyone in the room immediately.",
subTitleColor: HMSThemeColors.alertErrorBright,
@@ -108,57 +132,60 @@ class LeaveSessionBottomSheet extends StatelessWidget {
topRight: Radius.circular(16)),
),
context: context,
- builder: (ctx) => EndServiceBottomSheet(
- onButtonPressed: () => {
- if ((meetingStore.localPeer?.role.permissions
- .hlsStreaming ??
- false) &&
- meetingStore.hasHlsStarted)
- {
- meetingStore.stopHLSStreaming(),
- meetingStore.leave(),
- }
- else
- {
- meetingStore.endRoom(
- false, "Room Ended From Flutter"),
- },
- },
- title: HMSTitleText(
- text: ((meetingStore.localPeer?.role.permissions
- .hlsStreaming ??
+ builder: (ctx) => ChangeNotifierProvider.value(
+ value: widget.meetingStore,
+ child: EndServiceBottomSheet(
+ onButtonPressed: () => {
+ if ((widget.meetingStore.localPeer?.role.permissions
+ .hlsStreaming ??
+ false) &&
+ widget.meetingStore.hasHlsStarted)
+ {
+ widget.meetingStore.stopHLSStreaming(),
+ widget.meetingStore.leave(),
+ }
+ else
+ {
+ widget.meetingStore
+ .endRoom(false, "Room Ended From Flutter"),
+ },
+ },
+ title: HMSTitleText(
+ text: ((widget.meetingStore.localPeer?.role
+ .permissions.hlsStreaming ??
+ false) &&
+ widget.meetingStore.hasHlsStarted)
+ ? "End Stream"
+ : "End Session",
+ textColor: HMSThemeColors.alertErrorDefault,
+ letterSpacing: 0.15,
+ fontSize: 20,
+ ),
+ bottomSheetTitleIcon: SvgPicture.asset(
+ "packages/hms_room_kit/lib/src/assets/icons/end_warning.svg",
+ height: 20,
+ width: 20,
+ colorFilter: ColorFilter.mode(
+ HMSThemeColors.alertErrorDefault,
+ BlendMode.srcIn),
+ ),
+ subTitle: HMSSubheadingText(
+ text: ((widget.meetingStore.localPeer?.role
+ .permissions.hlsStreaming ??
+ false) &&
+ widget.meetingStore.hasHlsStarted)
+ ? "The stream will end for everyone after they’ve watched it."
+ : "The session will end for everyone in the room immediately.",
+ maxLines: 3,
+ textColor: HMSThemeColors.onSurfaceMediumEmphasis,
+ ),
+ buttonText: ((widget.meetingStore.localPeer?.role
+ .permissions.hlsStreaming ??
false) &&
- meetingStore.hasHlsStarted)
+ widget.meetingStore.hasHlsStarted)
? "End Stream"
: "End Session",
- textColor: HMSThemeColors.alertErrorDefault,
- letterSpacing: 0.15,
- fontSize: 20,
- ),
- bottomSheetTitleIcon: SvgPicture.asset(
- "packages/hms_room_kit/lib/src/assets/icons/end_warning.svg",
- height: 20,
- width: 20,
- colorFilter: ColorFilter.mode(
- HMSThemeColors.alertErrorDefault,
- BlendMode.srcIn),
- ),
- subTitle: HMSSubheadingText(
- text: ((meetingStore.localPeer?.role.permissions
- .hlsStreaming ??
- false) &&
- meetingStore.hasHlsStarted)
- ? "The stream will end for everyone after they’ve watched it."
- : "The session will end for everyone in the room immediately.",
- maxLines: 3,
- textColor: HMSThemeColors.onSurfaceMediumEmphasis,
),
- buttonText: ((meetingStore.localPeer?.role.permissions
- .hlsStreaming ??
- false) &&
- meetingStore.hasHlsStarted)
- ? "End Stream"
- : "End Session",
),
)
},
@@ -166,30 +193,33 @@ class LeaveSessionBottomSheet extends StatelessWidget {
],
),
)
- : EndServiceBottomSheet(
- onButtonPressed: () => {
- meetingStore.leave(),
- },
- title: HMSTitleText(
- text: "Leave Session",
- textColor: HMSThemeColors.alertErrorDefault,
- letterSpacing: 0.15,
- fontSize: 20,
- ),
- bottomSheetTitleIcon: SvgPicture.asset(
- "packages/hms_room_kit/lib/src/assets/icons/end_warning.svg",
- height: 20,
- width: 20,
- colorFilter: ColorFilter.mode(
- HMSThemeColors.alertErrorDefault, BlendMode.srcIn),
- ),
- subTitle: HMSSubheadingText(
- text:
- "Others will continue after you leave. You can join\n the session again.",
- maxLines: 2,
- textColor: HMSThemeColors.onSurfaceMediumEmphasis,
+ : ChangeNotifierProvider.value(
+ value: widget.meetingStore,
+ child: EndServiceBottomSheet(
+ onButtonPressed: () => {
+ widget.meetingStore.leave(),
+ },
+ title: HMSTitleText(
+ text: "Leave Session",
+ textColor: HMSThemeColors.alertErrorDefault,
+ letterSpacing: 0.15,
+ fontSize: 20,
+ ),
+ bottomSheetTitleIcon: SvgPicture.asset(
+ "packages/hms_room_kit/lib/src/assets/icons/end_warning.svg",
+ height: 20,
+ width: 20,
+ colorFilter: ColorFilter.mode(
+ HMSThemeColors.alertErrorDefault, BlendMode.srcIn),
+ ),
+ subTitle: HMSSubheadingText(
+ text:
+ "Others will continue after you leave. You can join\n the session again.",
+ maxLines: 2,
+ textColor: HMSThemeColors.onSurfaceMediumEmphasis,
+ ),
+ buttonText: "Leave Session",
),
- buttonText: "Leave Session",
);
}
}
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/local_peer_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/local_peer_bottom_sheet.dart
index 991e67ac7..6659c1dc7 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/local_peer_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/local_peer_bottom_sheet.dart
@@ -34,6 +34,18 @@ class LocalPeerBottomSheet extends StatefulWidget {
}
class _LocalPeerBottomSheetState extends State {
+ @override
+ void initState() {
+ super.initState();
+ context.read().addBottomSheet(context);
+ }
+
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
+ }
+
@override
Widget build(BuildContext context) {
return FractionallySizedBox(
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart
index 8f3ca83c5..27a1574fd 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/overlay_participants_bottom_sheet.dart
@@ -6,9 +6,28 @@ import 'package:hms_room_kit/src/widgets/common_widgets/hms_cross_button.dart';
import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart';
import 'package:provider/provider.dart';
-class OverlayParticipantsBottomSheet extends StatelessWidget {
+class OverlayParticipantsBottomSheet extends StatefulWidget {
const OverlayParticipantsBottomSheet({Key? key}) : super(key: key);
+ @override
+ State createState() =>
+ _OverlayParticipantsBottomSheetState();
+}
+
+class _OverlayParticipantsBottomSheetState
+ extends State {
+ @override
+ void initState() {
+ super.initState();
+ context.read().addBottomSheet(context);
+ }
+
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
+ }
+
@override
Widget build(BuildContext context) {
return SafeArea(
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_bottom_sheet.dart
index 21a089bd4..90c7753bd 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_bottom_sheet.dart
@@ -53,6 +53,7 @@ class _ParticipantsBottomSheetState extends State {
}
void viewAll(String role) {
+ var meetingStore = context.read();
showModalBottomSheet(
isScrollControlled: true,
backgroundColor: HMSThemeColors.surfaceDim,
@@ -61,7 +62,7 @@ class _ParticipantsBottomSheetState extends State {
),
context: context,
builder: (ctx) => ChangeNotifierProvider.value(
- value: context.read(),
+ value: meetingStore,
child: ParticipantsViewAllBottomSheet(role: role)),
);
}
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_view_all_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_view_all_bottom_sheet.dart
index 6a6844f16..9f48c5d4a 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_view_all_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/participants_view_all_bottom_sheet.dart
@@ -40,6 +40,7 @@ class _ParticipantsViewAllBottomSheetState
void initState() {
super.initState();
context.read().disableRefresh();
+ context.read().addBottomSheet(context);
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
@@ -49,9 +50,9 @@ class _ParticipantsViewAllBottomSheetState
}
@override
- void dispose() {
- _scrollController.dispose();
- super.dispose();
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
}
Widget _kebabMenu(HMSPeer peer) {
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/poll_and_quiz_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/poll_and_quiz_bottom_sheet.dart
new file mode 100644
index 000000000..6d2350ce6
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/poll_and_quiz_bottom_sheet.dart
@@ -0,0 +1,187 @@
+///Package imports
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+import 'package:tuple/tuple.dart';
+
+///Project imports
+import 'package:hms_room_kit/hms_room_kit.dart';
+import 'package:hms_room_kit/src/meeting/meeting_store.dart';
+import 'package:hms_room_kit/src/model/poll_store.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_cross_button.dart';
+import 'package:hms_room_kit/src/widgets/poll_widgets/poll_creation_widgets/poll_form.dart';
+import 'package:hms_room_kit/src/widgets/poll_widgets/poll_creation_widgets/poll_question_card.dart';
+
+///[PollAndQuizBottomSheet] renders the poll and quiz creation UI
+class PollAndQuizBottomSheet extends StatefulWidget {
+ const PollAndQuizBottomSheet({Key? key}) : super(key: key);
+
+ @override
+ State createState() => _PollAndQuizBottomSheetState();
+}
+
+class _PollAndQuizBottomSheetState extends State {
+ @override
+ void initState() {
+ super.initState();
+ context.read().addBottomSheet(context);
+ }
+
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return FractionallySizedBox(
+ heightFactor: 0.87,
+ child: Padding(
+ padding: const EdgeInsets.only(top: 12.0, left: 16, right: 16),
+ child: SingleChildScrollView(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ ///Top bar
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ HMSTitleText(
+ text: "Polls and Quizzes",
+ fontSize: 20,
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ ),
+ const HMSCrossButton(),
+ ],
+ ),
+
+ Padding(
+ padding: const EdgeInsets.only(top: 10, bottom: 16),
+ child: Divider(
+ color: HMSThemeColors.borderDefault,
+ height: 5,
+ ),
+ ),
+
+ ///Poll and Quiz selection buttons
+ ///Will be added in upcoming release
+ // if (context
+ // .read()
+ // .localPeer
+ // ?.role
+ // .permissions
+ // .pollWrite ??
+ // false)
+ // const PollQuizSelectionWidget(),
+
+ if (context
+ .read()
+ .localPeer
+ ?.role
+ .permissions
+ .pollWrite ??
+ false)
+ const SizedBox(
+ height: 24,
+ ),
+
+ ///Poll or Quiz Section
+ if (context
+ .read()
+ .localPeer
+ ?.role
+ .permissions
+ .pollWrite ??
+ false)
+ const PollForm(),
+
+ ///This section shows all the previous polls
+ ///which are either started or stopped
+ if ((context
+ .read()
+ .localPeer
+ ?.role
+ .permissions
+ .pollRead ??
+ false) ||
+ (context
+ .read()
+ .localPeer
+ ?.role
+ .permissions
+ .pollWrite ??
+ false))
+ Selector(
+ selector: (_, meetingStore) =>
+ meetingStore.pollQuestions.length,
+ builder: (_, data, __) {
+ return data > 0
+ ? Padding(
+ padding:
+ const EdgeInsets.symmetric(vertical: 24.0),
+ child: HMSTitleText(
+ text: "Previous Polls and Quizzes",
+ textColor:
+ HMSThemeColors.onSurfaceHighEmphasis,
+ fontSize: 20,
+ letterSpacing: 0.15,
+ ),
+ )
+ : const SizedBox();
+ }),
+
+ if ((context
+ .read()
+ .localPeer
+ ?.role
+ .permissions
+ .pollRead ??
+ false) ||
+ ((context
+ .read()
+ .localPeer
+ ?.role
+ .permissions
+ .pollWrite ??
+ false)))
+ Selector>>(
+ selector: (_, meetingStore) => Tuple2(
+ meetingStore.pollQuestions.length,
+ meetingStore.pollQuestions),
+ builder: (_, data, __) {
+ if (data.item2.isEmpty &&
+ (!(context
+ .read()
+ .localPeer
+ ?.role
+ .permissions
+ .pollWrite ??
+ true))) {
+ return Center(
+ child: HMSTitleText(
+ text: "No polls started in this session",
+ textColor: HMSThemeColors.onPrimaryHighEmphasis),
+ );
+ } else {
+ return ListView.builder(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: data.item1,
+ itemBuilder: (BuildContext context, int index) {
+ return ChangeNotifierProvider.value(
+ value: data.item2[data.item1 - index - 1],
+ child: const PollQuestionCard());
+ },
+ );
+ }
+ },
+ ),
+ const SizedBox(
+ height: 8,
+ )
+ ],
+ ),
+ )),
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/poll_vote_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/poll_vote_bottom_sheet.dart
new file mode 100644
index 000000000..281d4432f
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/poll_vote_bottom_sheet.dart
@@ -0,0 +1,195 @@
+///Dart imports
+import 'dart:math' as math;
+
+///Package imports
+import 'package:flutter/material.dart';
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+import 'package:provider/provider.dart';
+
+///Project imports
+import 'package:hms_room_kit/src/layout_api/hms_theme_colors.dart';
+import 'package:hms_room_kit/src/meeting/meeting_store.dart';
+import 'package:hms_room_kit/src/model/poll_store.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_button.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_cross_button.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_title_text.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/live_badge.dart';
+import 'package:hms_room_kit/src/widgets/poll_widgets/voting_flow_widgets/poll_result_card.dart';
+import 'package:hms_room_kit/src/widgets/poll_widgets/voting_flow_widgets/poll_vote_card.dart';
+
+///[PollVoteBottomSheet] renders the voting bottom sheet for polls
+class PollVoteBottomSheet extends StatefulWidget {
+ const PollVoteBottomSheet({super.key});
+
+ @override
+ State createState() => _PollVoteBottomSheetState();
+}
+
+class _PollVoteBottomSheetState extends State {
+ @override
+ void initState() {
+ super.initState();
+ context.read().addBottomSheet(context);
+ }
+
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ var hmsPollStore = context.watch();
+ return FractionallySizedBox(
+ heightFactor: 0.87,
+ child: Padding(
+ padding: const EdgeInsets.only(top: 12.0, left: 16, right: 16),
+ child: SingleChildScrollView(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ IconButton(
+ icon: Icon(
+ Icons.arrow_back_ios_new,
+ size: 16,
+ color: HMSThemeColors.onSurfaceHighEmphasis,
+ ),
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ ),
+ Container(
+ constraints: BoxConstraints(
+ maxWidth: MediaQuery.of(context).size.width * 0.4),
+ child: HMSTitleText(
+ text: hmsPollStore.poll.title,
+ fontSize: 20,
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ maxLines: 2,
+ ),
+ ),
+ const SizedBox(
+ width: 12,
+ ),
+ Selector(
+ selector: (_, hmsPollStore) =>
+ hmsPollStore.poll.state,
+ builder: (_, state, __) {
+ return state == HMSPollState.started
+ ? const LiveBadge()
+ : LiveBadge(
+ badgeColor: HMSThemeColors.secondaryDefault,
+ text: "ENDED",
+ width: 50,
+ );
+ }),
+ ],
+ ),
+ const Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ HMSCrossButton(),
+ ],
+ ),
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 10, bottom: 16),
+ child: Divider(
+ color: HMSThemeColors.borderDefault,
+ height: 5,
+ ),
+ ),
+ HMSTitleText(
+ text:
+ "${hmsPollStore.poll.createdBy?.name.substring(0, math.min(15, hmsPollStore.poll.createdBy?.name.length ?? 0)) ?? ""} started a poll",
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ letterSpacing: 0.15,
+ ),
+ const SizedBox(
+ height: 8,
+ ),
+ Selector(
+ selector: (_, pollStore) => pollStore.poll,
+ builder: (_, poll, __) {
+ return ListView.builder(
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: poll.questions?.length ?? 0,
+ shrinkWrap: true,
+ itemBuilder: (BuildContext context, index) {
+ if ((poll.questions![index].myResponses.isNotEmpty) ||
+ (poll.state == HMSPollState.stopped)) {
+ var totalVotes = 0;
+ for (var element
+ in poll.questions![index].options) {
+ totalVotes += element.voteCount;
+ }
+ var isVoteCountHidden = false;
+ if (poll.rolesThatCanViewResponses.isNotEmpty &&
+ !poll.rolesThatCanViewResponses.contains(context
+ .read()
+ .localPeer
+ ?.role)) {
+ isVoteCountHidden = true;
+ }
+ return PollResultCard(
+ questionNumber: index,
+ totalQuestions: poll.questions?.length ?? 0,
+ question: poll.questions![index],
+ totalVotes: totalVotes,
+ isVoteCountHidden: isVoteCountHidden,
+ );
+ } else {
+ return PollVoteCard(
+ questionNumber: index,
+ totalQuestions: poll.questions?.length ?? 0,
+ question: poll.questions![index]);
+ }
+ });
+ }),
+ Selector(
+ selector: (_, pollStore) => pollStore.poll.state,
+ builder: (_, pollState, __) {
+ ///End Poll is only shown when user has permission to end Poll and poll is not stopped.
+ return (pollState != HMSPollState.stopped &&
+ (context
+ .read()
+ .localPeer
+ ?.role
+ .permissions
+ .pollWrite ??
+ false))
+ ? Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ HMSButton(
+ width: MediaQuery.of(context).size.width * 0.3,
+ onPressed: () {
+ context
+ .read()
+ .stopPoll(hmsPollStore.poll);
+ },
+ childWidget: HMSTitleText(
+ text: "End Poll",
+ textColor:
+ HMSThemeColors.onPrimaryHighEmphasis),
+ buttonBackgroundColor:
+ HMSThemeColors.alertErrorDefault,
+ ),
+ ],
+ )
+ : const SizedBox();
+ })
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/remote_peer_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/remote_peer_bottom_sheet.dart
index 709b7a608..be92c7d03 100644
--- a/packages/hms_room_kit/lib/src/widgets/bottom_sheets/remote_peer_bottom_sheet.dart
+++ b/packages/hms_room_kit/lib/src/widgets/bottom_sheets/remote_peer_bottom_sheet.dart
@@ -8,6 +8,7 @@ import 'package:hms_room_kit/src/meeting/meeting_store.dart';
import 'package:hms_room_kit/src/model/peer_track_node.dart';
import 'package:hms_room_kit/src/widgets/common_widgets/hms_cross_button.dart';
import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart';
+import 'package:provider/provider.dart';
///[RemotePeerBottomSheet] is a widget that is used to render the bottom sheet for remote peers
///This bottom sheet is shown when the more option button is clicked on a remote peer tile
@@ -23,6 +24,18 @@ class RemotePeerBottomSheet extends StatefulWidget {
}
class _RemotePeerBottomSheetState extends State {
+ @override
+ void initState() {
+ super.initState();
+ context.read().addBottomSheet(context);
+ }
+
+ @override
+ void deactivate() {
+ context.read().removeBottomSheet(context);
+ super.deactivate();
+ }
+
@override
Widget build(BuildContext context) {
return FractionallySizedBox(
diff --git a/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_button.dart b/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_button.dart
index c091f4fac..d60388e2b 100644
--- a/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_button.dart
+++ b/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_button.dart
@@ -1,6 +1,8 @@
+///Package imports
import 'package:flutter/material.dart';
-import 'package:hms_room_kit/src/common/app_color.dart';
+import 'package:hms_room_kit/hms_room_kit.dart';
+///[HMSButton] is a button based on HMS theme colors
class HMSButton extends StatelessWidget {
final double width;
final Color? shadowColor;
@@ -20,10 +22,8 @@ class HMSButton extends StatelessWidget {
width: width,
child: ElevatedButton(
style: ButtonStyle(
- shadowColor:
- MaterialStateProperty.all(shadowColor ?? themeSurfaceColor),
backgroundColor: MaterialStateProperty.all(
- buttonBackgroundColor ?? hmsdefaultColor),
+ buttonBackgroundColor ?? HMSThemeColors.primaryDefault),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
diff --git a/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_listenable_button.dart b/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_listenable_button.dart
index 57283b4dc..6a1686b32 100644
--- a/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_listenable_button.dart
+++ b/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_listenable_button.dart
@@ -45,11 +45,12 @@ class HMSListenableButton extends StatelessWidget {
style: ButtonStyle(
shadowColor: MaterialStateProperty.all(
shadowColor ?? HMSThemeColors.surfaceDim),
- backgroundColor: (textController.text.isEmpty || isDisabled)
- ? MaterialStateProperty.all(
- HMSThemeColors.primaryDisabled)
- : MaterialStateProperty.all(
- HMSThemeColors.primaryDefault),
+ backgroundColor:
+ (textController.text.trim().isEmpty || isDisabled)
+ ? MaterialStateProperty.all(
+ HMSThemeColors.primaryDisabled)
+ : MaterialStateProperty.all(
+ HMSThemeColors.primaryDefault),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
diff --git a/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_title_text.dart b/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_title_text.dart
index 050c68b66..bba895b99 100644
--- a/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_title_text.dart
+++ b/packages/hms_room_kit/lib/src/widgets/common_widgets/hms_title_text.dart
@@ -13,6 +13,7 @@ import 'package:hms_room_kit/src/widgets/common_widgets/hms_text_style.dart';
///[fontSize] is the size of the font, default value is 16
///[fontWeight] is the weight of the font, default value is FontWeight.w600
///[textOverflow] is the overflow of the text, default value is TextOverflow.ellipsis
+///[maxLines] are the max number of lines that HMSTitleText will render after that it will overflow
class HMSTitleText extends StatelessWidget {
final String text;
final Color textColor;
@@ -21,6 +22,7 @@ class HMSTitleText extends StatelessWidget {
final double? fontSize;
final FontWeight? fontWeight;
final TextOverflow? textOverflow;
+ final int maxLines;
const HMSTitleText(
{Key? key,
@@ -30,13 +32,15 @@ class HMSTitleText extends StatelessWidget {
this.lineHeight = 24,
this.fontSize = 16,
this.fontWeight = FontWeight.w600,
- this.textOverflow = TextOverflow.ellipsis})
+ this.textOverflow = TextOverflow.ellipsis,
+ this.maxLines = 1})
: super(key: key);
@override
Widget build(BuildContext context) {
return Text(text,
overflow: textOverflow,
+ maxLines: maxLines,
style: HMSTextStyle.setTextStyle(
color: textColor,
height: lineHeight! / fontSize!,
diff --git a/packages/hms_room_kit/lib/src/widgets/common_widgets/live_badge.dart b/packages/hms_room_kit/lib/src/widgets/common_widgets/live_badge.dart
new file mode 100644
index 000000000..478f42118
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/common_widgets/live_badge.dart
@@ -0,0 +1,36 @@
+///Package imports
+import 'package:flutter/cupertino.dart';
+
+///Project imports
+import 'package:hms_room_kit/src/layout_api/hms_theme_colors.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_title_text.dart';
+
+///[LiveBadge] renders the badge based on the text and colors provided.
+///By default it renders a live badge as the name suggests.
+///It also provides parameters to configure the text, color and size of the badge
+class LiveBadge extends StatelessWidget {
+ final double? height;
+ final double? width;
+ final Color? badgeColor;
+ final String? text;
+ const LiveBadge(
+ {super.key, this.height, this.width, this.badgeColor, this.text});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: height ?? 24,
+ width: width ?? 43,
+ decoration: BoxDecoration(
+ color: badgeColor ?? HMSThemeColors.alertErrorDefault,
+ borderRadius: BorderRadius.circular(4)),
+ child: Center(
+ child: HMSTitleText(
+ text: text ?? "LIVE",
+ fontSize: 10,
+ lineHeight: 16,
+ letterSpacing: 1.5,
+ textColor: HMSThemeColors.alertErrorBrighter),
+ ));
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/common_widgets/message_container.dart b/packages/hms_room_kit/lib/src/widgets/common_widgets/message_container.dart
index 77e310f81..bdd456ea9 100644
--- a/packages/hms_room_kit/lib/src/widgets/common_widgets/message_container.dart
+++ b/packages/hms_room_kit/lib/src/widgets/common_widgets/message_container.dart
@@ -100,6 +100,7 @@ class MessageContainer extends StatelessWidget {
),
GestureDetector(
onTap: () {
+ var meetingStore = context.read();
showModalBottomSheet(
isScrollControlled: true,
backgroundColor: HMSThemeColors.surfaceDim,
@@ -110,7 +111,7 @@ class MessageContainer extends StatelessWidget {
),
context: context,
builder: (ctx) => ChangeNotifierProvider.value(
- value: context.read(),
+ value: meetingStore,
child: ChatUtilitiesBottomSheet(
message: message,
)),
diff --git a/packages/hms_room_kit/lib/src/widgets/peer_widgets/local_peer_more_option.dart b/packages/hms_room_kit/lib/src/widgets/peer_widgets/local_peer_more_option.dart
index 8e64da5a8..3b1c5bf22 100644
--- a/packages/hms_room_kit/lib/src/widgets/peer_widgets/local_peer_more_option.dart
+++ b/packages/hms_room_kit/lib/src/widgets/peer_widgets/local_peer_more_option.dart
@@ -30,6 +30,7 @@ class LocalPeerMoreOption extends StatelessWidget {
///[peerTrackNode] is the peerTrackNode of the peer whose more option is clicked
///We only show the modal bottom sheet if the peer is the local peer
var peerTrackNode = context.read();
+ var meetingStore = context.read();
showModalBottomSheet(
isScrollControlled: true,
backgroundColor: HMSThemeColors.surfaceDim,
@@ -40,10 +41,10 @@ class LocalPeerMoreOption extends StatelessWidget {
),
context: context,
builder: (ctx) => ChangeNotifierProvider.value(
- value: context.read(),
+ value: meetingStore,
child: LocalPeerBottomSheet(
isInsetTile: isInsetTile,
- meetingStore: context.read(),
+ meetingStore: meetingStore,
peerTrackNode: peerTrackNode,
callbackFunction: callbackFunction,
)),
diff --git a/packages/hms_room_kit/lib/src/widgets/peer_widgets/more_option.dart b/packages/hms_room_kit/lib/src/widgets/peer_widgets/more_option.dart
index 41b70925c..6fe4fa69e 100644
--- a/packages/hms_room_kit/lib/src/widgets/peer_widgets/more_option.dart
+++ b/packages/hms_room_kit/lib/src/widgets/peer_widgets/more_option.dart
@@ -36,6 +36,7 @@ class _MoreOptionState extends State {
///[peerTrackNode] is the peerTrackNode of the peer whose more option is clicked
///We only show the modal bottom sheet if the peer is not the local peer
var peerTrackNode = context.read();
+ var meetingStore = context.read();
if (peerTrackNode.peer.peerId !=
meetingStore.localPeer!.peerId) {
showModalBottomSheet(
@@ -48,7 +49,7 @@ class _MoreOptionState extends State {
),
context: context,
builder: (ctx) => ChangeNotifierProvider.value(
- value: context.read(),
+ value: meetingStore,
child: RemotePeerBottomSheet(
meetingStore: meetingStore,
peerTrackNode: peerTrackNode,
diff --git a/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/create_poll_form.dart b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/create_poll_form.dart
new file mode 100644
index 000000000..daa7efc2f
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/create_poll_form.dart
@@ -0,0 +1,476 @@
+///Package imports
+import 'package:dropdown_button2/dropdown_button2.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+import 'package:tuple/tuple.dart';
+
+///Project imports
+import 'package:hms_room_kit/hms_room_kit.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_dropdown.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart';
+
+///[CreatePollForm] widget renders the poll creation form
+class CreatePollForm extends StatefulWidget {
+ final int questionNumber;
+ final int totalQuestions;
+ final HMSPollQuestionType questionType;
+ final TextEditingController questionController;
+ final List optionsTextController;
+ final HMSPollQuestionBuilder questionBuilder;
+ final Function deleteQuestionCallback;
+ final Function savePollCallback;
+ const CreatePollForm(
+ {super.key,
+ required this.questionNumber,
+ required this.totalQuestions,
+ required this.questionType,
+ required this.optionsTextController,
+ required this.questionController,
+ required this.questionBuilder,
+ required this.deleteQuestionCallback,
+ required this.savePollCallback});
+
+ @override
+ State createState() => _CreatePollFormState();
+}
+
+class _CreatePollFormState extends State {
+ late TextEditingController _questionController;
+ late List _optionsTextController;
+ // bool _isSkippable = false;
+ // bool _canChangeResponse = false;
+
+ List> getPollQuestionType() {
+ return const [
+ Tuple2("Single Choice", HMSPollQuestionType.singleChoice),
+ Tuple2("Multiple Choice", HMSPollQuestionType.multiChoice)
+ ];
+ }
+
+ @override
+ void initState() {
+ ///Here we initialise the quesetion controller with
+ ///the value passed from widget. It's empty when a fresh poll is created
+ ///while it has `text` value when it's being edited
+ _questionController = widget.questionController;
+
+ ///Here options text controller gets initialised.
+ ///If controllers are passed on to the widget then we just
+ ///assign those controllers to _optionsTextController
+ if (widget.optionsTextController.isEmpty) {
+ _optionsTextController = [
+ TextEditingController(),
+ TextEditingController()
+ ];
+ } else {
+ _optionsTextController = widget.optionsTextController;
+ }
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ ///Here we dispose the question and options controller
+ _questionController.dispose();
+ for (var element in _optionsTextController) {
+ element.dispose();
+ }
+ super.dispose();
+ }
+
+ ///This function set's whether the question is skippable or not
+ // void setIsSkippable(value) {
+ // widget.questionBuilder.withCanSkip = value;
+ // setState(() {
+ // _isSkippable = value;
+ // });
+ // }
+
+ ///This function set's [canChangeResponse] which decides can the answer be changed
+ ///once voted
+ // void setCanChangeResponse(value) {
+ // widget.questionBuilder.withCanChangeResponse = value;
+ // setState(() {
+ // _canChangeResponse = value;
+ // });
+ // }
+
+ ///This adds a new option controller
+ void _addOption() {
+ _optionsTextController.add(TextEditingController());
+ setState(() {});
+ }
+
+ ///This function checks whether the poll is valid or not
+ ///This is checked before launching the poll
+ bool _isPollValid() {
+ bool areOptionsFilled = _optionsTextController.length >= 2;
+ for (var optionController in _optionsTextController) {
+ areOptionsFilled = areOptionsFilled && (optionController.text.isNotEmpty);
+ }
+ return (areOptionsFilled && _questionController.text.isNotEmpty);
+ }
+
+ ///This function set's the text for the question
+ void _setText(String text) {
+ widget.questionBuilder.withText = text;
+ }
+
+ ///This function save the poll option
+ void _savePollOption(String option, int index) {
+ _optionsTextController[index].text = option;
+ }
+
+ ///This function saves the option and also fires a callback
+ ///to save the question
+ void saveQuestion() {
+ widget.questionBuilder.withOption =
+ _optionsTextController.map((e) => e.text).toList();
+ if (_isPollValid()) {
+ widget.savePollCallback(widget.questionBuilder);
+ }
+ }
+
+ ///This function updates the poll type selection
+ void _updatePollType(HMSPollQuestionType questionType) {
+ widget.questionBuilder.withType = questionType;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: double.infinity,
+ decoration: BoxDecoration(
+ color: HMSThemeColors.surfaceDefault,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ HMSTitleText(
+ text:
+ "QUESTION ${widget.questionNumber + 1} OF ${widget.totalQuestions}",
+ textColor: HMSThemeColors.onSurfaceLowEmphasis,
+ fontSize: 10,
+ letterSpacing: 1.5,
+ lineHeight: 16),
+ const SizedBox(
+ height: 8,
+ ),
+ HMSSubheadingText(
+ text: "Question Type",
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ ),
+ const SizedBox(
+ height: 8,
+ ),
+
+ ///Dropdown for poll type
+ DropdownButtonHideUnderline(
+ child: HMSDropDown(
+ dropDownItems: getPollQuestionType()
+ .map((e) => DropdownMenuItem(
+ value: e.item2,
+ child: HMSTitleText(
+ text: e.item1,
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ fontWeight: FontWeight.w400,
+ ),
+ ))
+ .toList(),
+ buttonStyleData: ButtonStyleData(
+ width: double.infinity,
+ padding: const EdgeInsets.symmetric(
+ horizontal: 8,
+ ),
+ decoration: BoxDecoration(
+ color: HMSThemeColors.surfaceBright,
+ borderRadius: BorderRadius.circular(8),
+ )),
+ dropdownStyleData: DropdownStyleData(
+ padding: const EdgeInsets.symmetric(
+ vertical: 12, horizontal: 16),
+ decoration: BoxDecoration(
+ color: HMSThemeColors.surfaceBright,
+ )),
+ selectedValue: widget.questionType,
+ updateSelectedValue: (value) {
+ _updatePollType(value);
+ })),
+ const SizedBox(
+ height: 8,
+ ),
+ HMSSubheadingText(
+ text: "Question",
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ ),
+ const SizedBox(
+ height: 8,
+ ),
+
+ ///Textfield for setting the question
+ SizedBox(
+ height: 48,
+ child: TextField(
+ cursorColor: HMSThemeColors.onSurfaceHighEmphasis,
+ onTapOutside: (event) =>
+ FocusManager.instance.primaryFocus?.unfocus(),
+ textInputAction: TextInputAction.done,
+ textCapitalization: TextCapitalization.words,
+ style: HMSTextStyle.setTextStyle(
+ color: HMSThemeColors.onSurfaceHighEmphasis),
+ controller: _questionController,
+ keyboardType: TextInputType.text,
+ onChanged: (value) {
+ _setText(value.trim());
+ setState(() {});
+ },
+ decoration: InputDecoration(
+ contentPadding: const EdgeInsets.symmetric(
+ horizontal: 16, vertical: 12),
+ fillColor: HMSThemeColors.surfaceBright,
+ filled: true,
+ hintText: "e.g. Who will win the match?",
+ hintStyle: HMSTextStyle.setTextStyle(
+ color: HMSThemeColors.onSurfaceLowEmphasis,
+ height: 1.5,
+ fontSize: 16,
+ letterSpacing: 0.5,
+ fontWeight: FontWeight.w400),
+ focusedBorder: OutlineInputBorder(
+ borderRadius:
+ const BorderRadius.all(Radius.circular(8)),
+ borderSide:
+ BorderSide(color: HMSThemeColors.primaryDefault)),
+ border:
+ const OutlineInputBorder(borderSide: BorderSide.none)),
+ ),
+ ),
+ const SizedBox(
+ height: 8,
+ ),
+
+ ///Divider
+ Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 12.0,
+ ),
+ child: Divider(
+ height: 5,
+ color: HMSThemeColors.borderBright,
+ ),
+ ),
+
+ HMSSubheadingText(
+ text: "Options",
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ ),
+
+ const SizedBox(
+ height: 8,
+ ),
+
+ ///Here we set the options
+ ListView.builder(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: _optionsTextController.length,
+ itemBuilder: (context, index) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ SizedBox(
+ height: 48,
+ width: MediaQuery.of(context).size.width * 0.67,
+ child: TextField(
+ cursorColor: HMSThemeColors.onSurfaceHighEmphasis,
+ onTapOutside: (event) =>
+ FocusManager.instance.primaryFocus?.unfocus(),
+ textInputAction: TextInputAction.done,
+ textCapitalization: TextCapitalization.words,
+ style: HMSTextStyle.setTextStyle(
+ color: HMSThemeColors.onSurfaceHighEmphasis),
+ controller: _optionsTextController[index],
+ keyboardType: TextInputType.text,
+ onChanged: (value) {
+ _savePollOption(value.trim(), index);
+ },
+ decoration: InputDecoration(
+ contentPadding: const EdgeInsets.symmetric(
+ horizontal: 16, vertical: 12),
+ fillColor: HMSThemeColors.surfaceBright,
+ filled: true,
+ hintText: "Option ${index + 1}",
+ hintStyle: HMSTextStyle.setTextStyle(
+ color: HMSThemeColors.onSurfaceLowEmphasis,
+ height: 1.5,
+ fontSize: 16,
+ letterSpacing: 0.5,
+ fontWeight: FontWeight.w400),
+ focusedBorder: OutlineInputBorder(
+ borderRadius: const BorderRadius.all(
+ Radius.circular(8)),
+ borderSide: BorderSide(
+ color: HMSThemeColors.primaryDefault)),
+ border: const OutlineInputBorder(
+ borderSide: BorderSide.none)),
+ ),
+ ),
+ if (_optionsTextController.length > 2)
+ IconButton(
+ onPressed: () {
+ _optionsTextController.removeAt(index);
+ setState(() {});
+ },
+ icon: SvgPicture.asset(
+ "packages/hms_room_kit/lib/src/assets/icons/delete_poll.svg",
+ width: 24,
+ height: 24,
+ colorFilter: ColorFilter.mode(
+ HMSThemeColors.onSurfaceLowEmphasis,
+ BlendMode.srcIn)))
+ ],
+ ),
+ );
+ }),
+ if (_optionsTextController.length < 8)
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
+ child: GestureDetector(
+ onTap: () {
+ _addOption();
+ },
+ child: Row(
+ children: [
+ SvgPicture.asset(
+ "packages/hms_room_kit/lib/src/assets/icons/add_option.svg",
+ width: 24,
+ height: 24,
+ colorFilter: ColorFilter.mode(
+ HMSThemeColors.onSurfaceMediumEmphasis,
+ BlendMode.srcIn)),
+ const SizedBox(
+ width: 16,
+ ),
+ HMSSubheadingText(
+ text: "Add an option",
+ textColor: HMSThemeColors.onSurfaceMediumEmphasis,
+ ),
+ ],
+ ),
+ ),
+ ),
+
+ Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 12.0,
+ ),
+ child: Divider(
+ height: 5,
+ color: HMSThemeColors.borderBright,
+ ),
+ ),
+
+ ///Not supported now
+ ///Switch for setting skippable and change response variables.
+ // ListTile(
+ // horizontalTitleGap: 1,
+ // enabled: false,
+ // dense: true,
+ // contentPadding: EdgeInsets.zero,
+ // title: HMSSubheadingText(
+ // text: "Allow to skip",
+ // textColor: HMSThemeColors.onSurfaceMediumEmphasis),
+ // trailing: SizedBox(
+ // height: 24,
+ // width: 40,
+ // child: FittedBox(
+ // fit: BoxFit.contain,
+ // child: CupertinoSwitch(
+ // value: _isSkippable,
+ // onChanged: (value) => setIsSkippable(value),
+ // activeColor: HMSThemeColors.primaryDefault,
+ // ),
+ // ),
+ // ),
+ // ),
+
+ ///Not supported as of now
+ // ListTile(
+ // horizontalTitleGap: 1,
+ // enabled: false,
+ // dense: true,
+ // contentPadding: EdgeInsets.zero,
+ // title: HMSSubheadingText(
+ // text: "Allow to vote again",
+ // textColor: HMSThemeColors.onSurfaceMediumEmphasis),
+ // trailing: SizedBox(
+ // height: 24,
+ // width: 40,
+ // child: FittedBox(
+ // fit: BoxFit.contain,
+ // child: CupertinoSwitch(
+ // value: _canChangeResponse,
+ // onChanged: (value) => setCanChangeResponse(value),
+ // activeColor: HMSThemeColors.primaryDefault,
+ // ),
+ // ),
+ // ),
+ // ),
+
+ ///Save the question
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ // if (widget.totalQuestions > 1)
+ // HMSEmbeddedButton(
+ // onTap: () =>
+ // widget.deleteQuestionCallback(widget.questionBuilder),
+ // isActive: true,
+ // onColor: HMSThemeColors.surfaceDefault,
+ // child: SvgPicture.asset(
+ // "packages/hms_room_kit/lib/src/assets/icons/delete_poll.svg",
+ // colorFilter: ColorFilter.mode(
+ // HMSThemeColors.onSurfaceHighEmphasis,
+ // BlendMode.srcIn),
+ // fit: BoxFit.scaleDown,
+ // ),
+ // ),
+ // const SizedBox(
+ // width: 8,
+ // ),
+ ElevatedButton(
+ style: ButtonStyle(
+ elevation: MaterialStateProperty.all(0),
+ backgroundColor: (_questionController.text.isNotEmpty)
+ ? MaterialStateProperty.all(
+ HMSThemeColors.secondaryDefault)
+ : MaterialStateProperty.all(
+ HMSThemeColors.secondaryDim),
+ shape:
+ MaterialStateProperty.all(
+ RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(8.0),
+ ))),
+ onPressed: () {
+ saveQuestion();
+ },
+ child: HMSTitleText(
+ text: "Save",
+ textColor: _isPollValid()
+ ? HMSThemeColors.onSecondaryHighEmphasis
+ : HMSThemeColors.onSecondaryLowEmphasis))
+ ],
+ )
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/poll_form.dart b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/poll_form.dart
new file mode 100644
index 000000000..b36bedda5
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/poll_form.dart
@@ -0,0 +1,223 @@
+///Package imports
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:hms_room_kit/src/widgets/poll_widgets/poll_creation_widgets/poll_question_bottom_sheet.dart';
+import 'package:provider/provider.dart';
+
+///Project imports
+import 'package:hms_room_kit/hms_room_kit.dart';
+import 'package:hms_room_kit/src/meeting/meeting_store.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_listenable_button.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart';
+
+///[PollForm] widget renders the poll creation form with poll title.
+class PollForm extends StatefulWidget {
+ const PollForm({super.key});
+
+ @override
+ State createState() => _PollFormState();
+}
+
+class _PollFormState extends State {
+ late TextEditingController _pollNameController;
+ bool _hideVoteCount = false;
+ // bool _isAnonymous = false;
+
+ @override
+ void initState() {
+ _pollNameController = TextEditingController();
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ _pollNameController.dispose();
+ super.dispose();
+ }
+
+ ///This method sets the value of the hide vote count switch
+ void setHideVoteCount(bool value) {
+ setState(() {
+ _hideVoteCount = value;
+ });
+ }
+
+ ///This method sets the value of the is anonymous switch
+ // void setIsAnonymous(bool value) {
+ // setState(() {
+ // _isAnonymous = value;
+ // });
+ // }
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ HMSSubheadingText(
+ text: "Poll Name", textColor: HMSThemeColors.onSurfaceHighEmphasis),
+ const SizedBox(
+ height: 8,
+ ),
+
+ ///Text Field
+ SizedBox(
+ height: 48,
+ child: TextField(
+ cursorColor: HMSThemeColors.onSurfaceHighEmphasis,
+ onTapOutside: (event) =>
+ FocusManager.instance.primaryFocus?.unfocus(),
+ textInputAction: TextInputAction.done,
+ textCapitalization: TextCapitalization.words,
+ style: HMSTextStyle.setTextStyle(
+ color: HMSThemeColors.onSurfaceHighEmphasis),
+ controller: _pollNameController,
+ keyboardType: TextInputType.text,
+ onChanged: (value) {
+ setState(() {});
+ },
+ decoration: InputDecoration(
+ contentPadding:
+ const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ fillColor: HMSThemeColors.surfaceDefault,
+ filled: true,
+ hintText: "Name this Poll",
+ hintStyle: HMSTextStyle.setTextStyle(
+ color: HMSThemeColors.onSurfaceLowEmphasis,
+ height: 1.5,
+ fontSize: 16,
+ letterSpacing: 0.5,
+ fontWeight: FontWeight.w400),
+ focusedBorder: OutlineInputBorder(
+ borderRadius: const BorderRadius.all(Radius.circular(8)),
+ borderSide:
+ BorderSide(color: HMSThemeColors.primaryDefault)),
+ border: const OutlineInputBorder(borderSide: BorderSide.none)),
+ ),
+ ),
+
+ ///Padding
+ const SizedBox(
+ height: 24,
+ ),
+
+ ///Divider
+ Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 12.0,
+ ),
+ child: Divider(
+ height: 5,
+ color: HMSThemeColors.borderBright,
+ ),
+ ),
+
+ ///Settings section
+ Padding(
+ padding: const EdgeInsets.only(bottom: 4.0),
+ child: HMSSubheadingText(
+ text: "Settings",
+ textColor: HMSThemeColors.onSurfaceHighEmphasis),
+ ),
+
+ ///Hide vote count switch
+ ListTile(
+ horizontalTitleGap: 1,
+ enabled: false,
+ dense: true,
+ contentPadding: EdgeInsets.zero,
+ title: HMSSubheadingText(
+ text: "Hide vote count",
+ textColor: HMSThemeColors.onSurfaceMediumEmphasis),
+ trailing: SizedBox(
+ height: 24,
+ width: 40,
+ child: FittedBox(
+ fit: BoxFit.contain,
+ child: CupertinoSwitch(
+ value: _hideVoteCount,
+ onChanged: (value) => setHideVoteCount(value),
+ activeColor: HMSThemeColors.primaryDefault,
+ ),
+ ),
+ ),
+ ),
+
+ ///Is the poll anonymous switch
+ // ListTile(
+ // horizontalTitleGap: 1,
+ // enabled: false,
+ // dense: true,
+ // contentPadding: EdgeInsets.zero,
+ // title: Row(
+ // children: [
+ // HMSSubheadingText(
+ // text: "Make results anonymous",
+ // textColor: HMSThemeColors.onSurfaceMediumEmphasis),
+ // // const SizedBox(
+ // // width: 8,
+ // // ),
+ // // SvgPicture.asset(
+ // // "packages/hms_room_kit/lib/src/assets/icons/info.svg",
+ // // height: 16,
+ // // width: 16,
+ // // colorFilter: ColorFilter.mode(
+ // // HMSThemeColors.onSurfaceLowEmphasis, BlendMode.srcIn),
+ // // )
+ // ],
+ // ),
+ // trailing: SizedBox(
+ // height: 24,
+ // width: 40,
+ // child: FittedBox(
+ // fit: BoxFit.contain,
+ // child: CupertinoSwitch(
+ // value: _isAnonymous,
+ // onChanged: (value) => setIsAnonymous(value),
+ // activeColor: HMSThemeColors.primaryDefault,
+ // ),
+ // ),
+ // ),
+ // ),
+
+ ///Padding
+ const SizedBox(
+ height: 24,
+ ),
+
+ ///Button to add question
+ SizedBox(
+ height: 40,
+ child: HMSListenableButton(
+ textController: _pollNameController,
+ width: MediaQuery.of(context).size.width - 40,
+ onPressed: () {
+ if (_pollNameController.text.trim().isEmpty) return;
+ var meetingStore = context.read();
+ showModalBottomSheet(
+ isScrollControlled: true,
+ backgroundColor: HMSThemeColors.surfaceDim,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16)),
+ ),
+ context: context,
+ builder: (ctx) => ChangeNotifierProvider.value(
+ value: meetingStore,
+ child: PollQuestionBottomSheet(
+ pollName: _pollNameController.text.trim(),
+ )),
+ );
+ },
+ childWidget: HMSTitleText(
+ text: 'Create Poll',
+ textColor: _pollNameController.text.isEmpty
+ ? HMSThemeColors.onPrimaryLowEmphasis
+ : HMSThemeColors.onPrimaryHighEmphasis,
+ )),
+ )
+ ],
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/poll_question_bottom_sheet.dart b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/poll_question_bottom_sheet.dart
new file mode 100644
index 000000000..c0e1b3f0d
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/poll_question_bottom_sheet.dart
@@ -0,0 +1,262 @@
+///Package imports
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:hms_room_kit/src/widgets/poll_widgets/poll_creation_widgets/create_poll_form.dart';
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+import 'package:provider/provider.dart';
+
+///Project imports
+import 'package:hms_room_kit/src/layout_api/hms_theme_colors.dart';
+import 'package:hms_room_kit/src/meeting/meeting_store.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_cross_button.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_title_text.dart';
+import 'package:hms_room_kit/src/widgets/poll_widgets/poll_creation_widgets/saved_question_widget.dart';
+
+///[PollQuestionBottomSheet] renders the poll question form sheet
+class PollQuestionBottomSheet extends StatefulWidget {
+ final String pollName;
+
+ const PollQuestionBottomSheet({Key? key, required this.pollName})
+ : super(key: key);
+
+ @override
+ State createState() =>
+ _PollQuestionBottomSheetState();
+}
+
+class _PollQuestionBottomSheetState extends State {
+ late HMSPollBuilder pollBuilder;
+ bool _isPollValid = false;
+ Map pollQuestionBuilders = {};
+
+ @override
+ void initState() {
+ ///Here we create a new poll builder object with single question
+ pollBuilder = HMSPollBuilder();
+ pollQuestionBuilders[HMSPollQuestionBuilder()] = false;
+
+ ///Setting the title of the poll
+ pollBuilder.withTitle = widget.pollName;
+ super.initState();
+ }
+
+ ///This function adds a new question builder
+ void _addQuestion() {
+ pollQuestionBuilders[HMSPollQuestionBuilder()] = false;
+ setState(() {});
+ }
+
+ void _deleteQuestionCallback(HMSPollQuestionBuilder pollQuestionBuilder) {
+ pollQuestionBuilders.remove(pollQuestionBuilder);
+ setState(() {});
+ }
+
+ void _savePollCallback(HMSPollQuestionBuilder pollQuestionBuilder) {
+ pollQuestionBuilders[pollQuestionBuilder] = true;
+ _checkPollValidity();
+ setState(() {});
+ }
+
+ void _editPollCallback(HMSPollQuestionBuilder pollQuestionBuilder) {
+ pollQuestionBuilders[pollQuestionBuilder] = false;
+ _isPollValid = false;
+ setState(() {});
+ }
+
+ void _checkPollValidity() {
+ var isValid = true;
+ pollQuestionBuilders.forEach((key, value) {
+ isValid &= value;
+ });
+ _isPollValid = isValid;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return FractionallySizedBox(
+ heightFactor: 0.87,
+ child: Padding(
+ padding: const EdgeInsets.only(top: 12.0, left: 16, right: 16),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ///Top bar
+ Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Row(
+ children: [
+ IconButton(
+ icon: Icon(
+ Icons.arrow_back_ios_new,
+ size: 16,
+ color: HMSThemeColors.onSurfaceHighEmphasis,
+ ),
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ ),
+ Expanded(
+ child: HMSTitleText(
+ text: widget.pollName,
+ fontSize: 20,
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ maxLines: 2,
+ ),
+ ),
+ ],
+ ),
+ ),
+ const Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ HMSCrossButton(),
+ ],
+ ),
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 10, bottom: 16),
+ child: Divider(
+ color: HMSThemeColors.borderDefault,
+ height: 5,
+ ),
+ ),
+ ],
+ ),
+ Expanded(
+ child: SingleChildScrollView(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ///List to render poll form
+ ListView.builder(
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: pollQuestionBuilders.length,
+ shrinkWrap: true,
+ itemBuilder: (context, index) => Padding(
+ padding:
+ const EdgeInsets.symmetric(vertical: 8.0),
+ child: pollQuestionBuilders.values
+ .elementAt(index)
+ ? SavedQuestionWidget(
+ questionNumber: index,
+ totalQuestions:
+ pollQuestionBuilders.length,
+ pollQuestionBuilder: pollQuestionBuilders
+ .keys
+ .elementAt(index),
+ editPollCallback: _editPollCallback,
+ )
+ : CreatePollForm(
+ questionNumber: index,
+ totalQuestions:
+ pollQuestionBuilders.length,
+ questionType: pollQuestionBuilders.keys
+ .elementAt(index)
+ .type,
+ questionController: TextEditingController(
+ text: pollQuestionBuilders.keys
+ .elementAt(index)
+ .text),
+ optionsTextController:
+ pollQuestionBuilders.keys
+ .elementAt(index)
+ .pollOptions
+ .map((e) => TextEditingController(
+ text: e))
+ .toList(),
+ deleteQuestionCallback:
+ _deleteQuestionCallback,
+ questionBuilder: pollQuestionBuilders.keys
+ .elementAt(index),
+ savePollCallback: _savePollCallback,
+ ),
+ )),
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
+ child: GestureDetector(
+ onTap: () => _addQuestion(),
+ child: Row(
+ children: [
+ SvgPicture.asset(
+ "packages/hms_room_kit/lib/src/assets/icons/add_option.svg",
+ width: 24,
+ height: 24,
+ colorFilter: ColorFilter.mode(
+ HMSThemeColors.onSurfaceMediumEmphasis,
+ BlendMode.srcIn)),
+ const SizedBox(
+ width: 16,
+ ),
+ HMSSubheadingText(
+ text: "Add another question",
+ textColor: HMSThemeColors.onSurfaceMediumEmphasis,
+ ),
+ ],
+ ),
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ ElevatedButton(
+ style: ButtonStyle(
+ shadowColor: MaterialStateProperty.all(
+ HMSThemeColors.surfaceDim),
+ backgroundColor: _isPollValid
+ ? MaterialStateProperty.all(
+ HMSThemeColors.primaryDefault)
+ : MaterialStateProperty.all(
+ HMSThemeColors.primaryDisabled),
+ shape: MaterialStateProperty.all<
+ RoundedRectangleBorder>(
+ RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(8.0),
+ ))),
+ onPressed: () {
+ if (_isPollValid) {
+ pollQuestionBuilders.forEach((key, value) {
+ if (value) {
+ pollBuilder.addQuestion(key);
+ }
+ });
+ pollBuilder.withAnonymous = false;
+ pollBuilder.withCategory = HMSPollCategory.poll;
+ pollBuilder.withMode =
+ HMSPollUserTrackingMode.user_id;
+
+ context
+ .read()
+ .quickStartPoll(pollBuilder);
+ Navigator.pop(context);
+ Navigator.pop(context);
+ }
+ },
+ child: Center(
+ child: HMSTitleText(
+ text: "Launch Poll",
+ textColor: _isPollValid
+ ? HMSThemeColors.onPrimaryHighEmphasis
+ : HMSThemeColors.onPrimaryLowEmphasis,
+ )),
+ )
+ ],
+ ),
+ const SizedBox(
+ height: 64,
+ )
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/poll_question_card.dart b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/poll_question_card.dart
new file mode 100644
index 000000000..0543c44dc
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/poll_question_card.dart
@@ -0,0 +1,99 @@
+///Package imports
+import 'package:flutter/material.dart';
+import 'package:hms_room_kit/src/meeting/meeting_store.dart';
+import 'package:hms_room_kit/src/widgets/bottom_sheets/poll_vote_bottom_sheet.dart';
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+import 'package:provider/provider.dart';
+
+///Project imports
+import 'package:hms_room_kit/hms_room_kit.dart';
+import 'package:hms_room_kit/src/model/poll_store.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_button.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/live_badge.dart';
+
+///[PollQuestionCard] widget renders the cards for poll which are either started, ended, created or are in draft
+class PollQuestionCard extends StatelessWidget {
+ const PollQuestionCard({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.only(bottom: 24),
+ child: Container(
+ decoration: BoxDecoration(
+ color: HMSThemeColors.surfaceDefault,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Selector(
+ selector: (_, hmsPollStore) => hmsPollStore.poll.title,
+ builder: (_, title, __) {
+ return HMSTitleText(
+ text: title,
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ letterSpacing: 0.15,
+ maxLines: 3,
+ );
+ }),
+ ),
+ Selector(
+ selector: (_, hmsPollStore) => hmsPollStore.poll.state,
+ builder: (_, pollState, __) {
+ return pollState == HMSPollState.stopped
+ ? LiveBadge(
+ text: "ENDED",
+ badgeColor: HMSThemeColors.surfaceBrighter,
+ width: 50,
+ )
+ : const LiveBadge();
+ })
+ ],
+ ),
+ const SizedBox(
+ height: 16,
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ HMSButton(
+ width: MediaQuery.of(context).size.width * 0.23,
+ onPressed: () {
+ var meetingStore = context.read();
+ var pollStore = context.read();
+ showModalBottomSheet(
+ isScrollControlled: true,
+ backgroundColor: HMSThemeColors.surfaceDim,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16)),
+ ),
+ context: context,
+ builder: (ctx) => ChangeNotifierProvider.value(
+ value: meetingStore,
+ child: ChangeNotifierProvider.value(
+ value: pollStore,
+ child: const PollVoteBottomSheet(),
+ ),
+ ));
+ },
+ childWidget: HMSTitleText(
+ text: "View",
+ textColor: HMSThemeColors.onSurfaceHighEmphasis))
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/saved_question_widget.dart b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/saved_question_widget.dart
new file mode 100644
index 000000000..bfd793927
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_creation_widgets/saved_question_widget.dart
@@ -0,0 +1,123 @@
+///Package imports
+import 'package:flutter/material.dart';
+import 'package:hms_room_kit/hms_room_kit.dart';
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+
+///Project imports
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart';
+
+///[SavedQuestionWidget] widget renders the saved question UI
+class SavedQuestionWidget extends StatelessWidget {
+ final int questionNumber;
+ final int totalQuestions;
+ final HMSPollQuestionBuilder pollQuestionBuilder;
+ final Function editPollCallback;
+
+ const SavedQuestionWidget(
+ {super.key,
+ required this.questionNumber,
+ required this.totalQuestions,
+ required this.pollQuestionBuilder,
+ required this.editPollCallback});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ decoration: BoxDecoration(
+ color: HMSThemeColors.surfaceDefault,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ children: [
+ HMSTitleText(
+ text: "QUESTION ${questionNumber + 1} OF $totalQuestions: ",
+ textColor: HMSThemeColors.onSurfaceLowEmphasis,
+ fontSize: 10,
+ letterSpacing: 1.5,
+ lineHeight: 16),
+ HMSTitleText(
+ fontSize: 10,
+ letterSpacing: 1.5,
+ lineHeight: 16,
+ text: Utilities.getQuestionType(pollQuestionBuilder.type),
+ textColor: HMSThemeColors.onSurfaceLowEmphasis)
+ ],
+ ),
+ const SizedBox(
+ height: 16,
+ ),
+ Theme(
+ data: ThemeData().copyWith(dividerColor: Colors.transparent),
+ child: ExpansionTile(
+ expandedCrossAxisAlignment: CrossAxisAlignment.start,
+ expandedAlignment: Alignment.centerLeft,
+ initiallyExpanded: true,
+ iconColor: HMSThemeColors.onSurfaceHighEmphasis,
+ collapsedIconColor: HMSThemeColors.onSurfaceHighEmphasis,
+ tilePadding: EdgeInsets.zero,
+ childrenPadding: EdgeInsets.zero,
+ title: HMSTitleText(
+ text: pollQuestionBuilder.text ?? "",
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ fontWeight: FontWeight.w400,
+ maxLines: 3,
+ ),
+ children: pollQuestionBuilder.pollOptions
+ .map((e) => Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8.0),
+ child: HMSSubheadingText(
+ text: e,
+ textColor: HMSThemeColors.onSurfaceMediumEmphasis,
+ maxLines: 3,
+ ),
+ ))
+ .toList()),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ // HMSEmbeddedButton(
+ // onTap: () => {},
+ // // widget.deleteQuestionCallback(widget.questionBuilder),
+ // isActive: true,
+ // onColor: HMSThemeColors.surfaceDefault,
+ // child: SvgPicture.asset(
+ // "packages/hms_room_kit/lib/src/assets/icons/delete_poll.svg",
+ // colorFilter: ColorFilter.mode(
+ // HMSThemeColors.onSurfaceHighEmphasis, BlendMode.srcIn),
+ // fit: BoxFit.scaleDown,
+ // ),
+ // ),
+ const SizedBox(
+ width: 16,
+ ),
+ ElevatedButton(
+ style: ButtonStyle(
+ elevation: MaterialStateProperty.all(0),
+ backgroundColor: MaterialStateProperty.all(
+ HMSThemeColors.secondaryDefault),
+ shape:
+ MaterialStateProperty.all(
+ RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(8.0),
+ ))),
+ onPressed: () {
+ editPollCallback(pollQuestionBuilder);
+ },
+ child: HMSTitleText(
+ text: "Edit",
+ textColor: HMSThemeColors.onSecondaryHighEmphasis))
+ ],
+ )
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_quiz_selection_button.dart b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_quiz_selection_button.dart
new file mode 100644
index 000000000..8505133f1
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_quiz_selection_button.dart
@@ -0,0 +1,68 @@
+///Package imports
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:hms_room_kit/hms_room_kit.dart';
+
+///[PollQuizSelectionButton] widget renders the option with an icon and text side by side
+///Used here for poll or quiz selection button
+class PollQuizSelectionButton extends StatelessWidget {
+ final bool isSelected;
+ final String iconName;
+ final String text;
+
+ const PollQuizSelectionButton(
+ {Key? key,
+ required this.isSelected,
+ required this.iconName,
+ required this.text})
+ : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: 64,
+ width: (MediaQuery.of(context).size.width - 48) / 2,
+ decoration: BoxDecoration(
+ border: Border.all(
+ color: isSelected
+ ? HMSThemeColors.primaryDefault
+ : HMSThemeColors.borderBright,
+ ),
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Row(
+ children: [
+ Container(
+ height: 48,
+ width: 48,
+ decoration: BoxDecoration(
+ color: HMSThemeColors.borderBright,
+ border: Border.all(
+ color: isSelected
+ ? HMSThemeColors.primaryDefault
+ : HMSThemeColors.borderBright,
+ ),
+ borderRadius: BorderRadius.circular(4),
+ ),
+ child: Center(
+ child: SvgPicture.asset(
+ "packages/hms_room_kit/lib/src/assets/icons/$iconName.svg",
+ fit: BoxFit.scaleDown,
+ ),
+ )),
+ const SizedBox(
+ width: 16,
+ ),
+ HMSTitleText(
+ text: text,
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ letterSpacing: 0.15,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_quiz_selection_widget.dart b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_quiz_selection_widget.dart
new file mode 100644
index 000000000..5f86b1b4b
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/poll_widgets/poll_quiz_selection_widget.dart
@@ -0,0 +1,62 @@
+///Package imports
+import 'package:flutter/cupertino.dart';
+import 'package:hms_room_kit/hms_room_kit.dart';
+import 'package:hms_room_kit/src/widgets/poll_widgets/poll_quiz_selection_button.dart';
+
+///[PollQuizSelectionWidget] renders the widget for poll or quiz selection
+class PollQuizSelectionWidget extends StatefulWidget {
+ const PollQuizSelectionWidget({super.key});
+
+ @override
+ State createState() =>
+ _PollQuizSelectionWidgetState();
+}
+
+class _PollQuizSelectionWidgetState extends State {
+ int index = 0;
+
+ void updateSelection(newIndex) {
+ setState(() {
+ index = newIndex;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ HMSSubtitleText(
+ text: "Select the type you want to continue with",
+ textColor: HMSThemeColors.onSurfaceMediumEmphasis),
+ const SizedBox(
+ height: 8,
+ ),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ GestureDetector(
+ onTap: () => updateSelection(0),
+ child: PollQuizSelectionButton(
+ isSelected: (index == 0),
+ iconName: "poll",
+ text: "Poll",
+ ),
+ ),
+ const SizedBox(
+ width: 16,
+ ),
+ GestureDetector(
+ onTap: () => updateSelection(1),
+ child: PollQuizSelectionButton(
+ isSelected: (index == 1),
+ iconName: "quiz",
+ text: "Quiz",
+ ),
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/poll_widgets/voting_flow_widgets/poll_result_card.dart b/packages/hms_room_kit/lib/src/widgets/poll_widgets/voting_flow_widgets/poll_result_card.dart
new file mode 100644
index 000000000..60b7f46f5
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/poll_widgets/voting_flow_widgets/poll_result_card.dart
@@ -0,0 +1,177 @@
+///Package imports
+import 'package:flutter/material.dart';
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+
+///Project imports
+import 'package:hms_room_kit/hms_room_kit.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart';
+
+///[PollResultCard] renders the results for polls
+class PollResultCard extends StatelessWidget {
+ final int questionNumber;
+ final int totalQuestions;
+ final HMSPollQuestion question;
+ final int totalVotes;
+ final bool isVoteCountHidden;
+
+ const PollResultCard(
+ {super.key,
+ required this.questionNumber,
+ required this.totalQuestions,
+ required this.question,
+ required this.totalVotes,
+ required this.isVoteCountHidden});
+
+ bool isSelectedOption(int index) {
+ if (question.type == HMSPollQuestionType.singleChoice) {
+ for (var response in question.myResponses) {
+ if (index == response.selectedOption) {
+ return true;
+ }
+ }
+ } else if (question.type == HMSPollQuestionType.multiChoice) {
+ for (var response in question.myResponses) {
+ if (response.selectedOptions?.contains(index) ?? false) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.only(bottom: 24),
+ child: Container(
+ decoration: BoxDecoration(
+ color: HMSThemeColors.surfaceDefault,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ children: [
+ HMSTitleText(
+ text:
+ "QUESTION ${questionNumber + 1} OF $totalQuestions: ",
+ textColor: HMSThemeColors.onSurfaceLowEmphasis,
+ fontSize: 10,
+ letterSpacing: 1.5,
+ lineHeight: 16),
+ HMSTitleText(
+ fontSize: 10,
+ letterSpacing: 1.5,
+ lineHeight: 16,
+ text: Utilities.getQuestionType(question.type),
+ textColor: HMSThemeColors.onSurfaceLowEmphasis)
+ ],
+ ),
+ const SizedBox(
+ height: 16,
+ ),
+ HMSTitleText(
+ text: question.text,
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ maxLines: 3,
+ fontWeight: FontWeight.w400,
+ ),
+ const SizedBox(
+ height: 16,
+ ),
+ ListView.builder(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: question.options.length,
+ itemBuilder: (BuildContext context, index) {
+ return Padding(
+ padding: const EdgeInsets.only(bottom: 8.0),
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: HMSSubheadingText(
+ text: question.options[index].text ?? "",
+ maxLines: 3,
+ textColor:
+ HMSThemeColors.onSurfaceHighEmphasis),
+ ),
+ if (isVoteCountHidden &&
+ isSelectedOption(
+ question.options[index].index))
+ Checkbox(
+ activeColor:
+ HMSThemeColors.onSurfaceHighEmphasis,
+ checkColor: HMSThemeColors.surfaceDefault,
+ shape: const CircleBorder(),
+ value: true,
+ onChanged: (value) {}),
+ if (!isVoteCountHidden)
+ Padding(
+ padding: const EdgeInsets.only(left: 8.0),
+ child: HMSSubheadingText(
+ text:
+ "${question.options[index].voteCount.toString()} vote${question.options[index].voteCount > 1 ? "s" : ""}",
+ textColor: HMSThemeColors
+ .onSurfaceMediumEmphasis),
+ )
+ ],
+ ),
+ if (!isVoteCountHidden)
+ const SizedBox(
+ height: 8,
+ ),
+ if (!isVoteCountHidden)
+ Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ ),
+ height: 8,
+ child: Row(
+ children: [
+ Expanded(
+ flex: (question.options[index].voteCount),
+ child: Container(
+ decoration: BoxDecoration(
+ color: HMSThemeColors.primaryDefault,
+ borderRadius:
+ BorderRadius.circular(8),
+ ),
+ )),
+ Expanded(
+ flex: totalVotes -
+ question.options[index].voteCount,
+ child: Container(
+ decoration: BoxDecoration(
+ color: HMSThemeColors.surfaceBright,
+ borderRadius: BorderRadius.circular(8),
+ )))
+ ],
+ ),
+ )
+ ],
+ ),
+ );
+ }),
+ if (question.myResponses.isNotEmpty)
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ HMSTitleText(
+ text: "Voted",
+ textColor: HMSThemeColors.onSurfaceLowEmphasis)
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/poll_widgets/voting_flow_widgets/poll_vote_card.dart b/packages/hms_room_kit/lib/src/widgets/poll_widgets/voting_flow_widgets/poll_vote_card.dart
new file mode 100644
index 000000000..2d8e393a2
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/poll_widgets/voting_flow_widgets/poll_vote_card.dart
@@ -0,0 +1,180 @@
+///Package imports
+import 'package:flutter/material.dart';
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+import 'package:provider/provider.dart';
+
+///Project imports
+import 'package:hms_room_kit/hms_room_kit.dart';
+import 'package:hms_room_kit/src/meeting/meeting_store.dart';
+import 'package:hms_room_kit/src/model/poll_store.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_button.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart';
+
+///[PollVoteCard] renders the vote card for polls
+class PollVoteCard extends StatefulWidget {
+ final int questionNumber;
+ final int totalQuestions;
+ final HMSPollQuestion question;
+
+ const PollVoteCard(
+ {super.key,
+ required this.questionNumber,
+ required this.totalQuestions,
+ required this.question});
+
+ @override
+ State createState() => _PollVoteCardState();
+}
+
+class _PollVoteCardState extends State {
+ HMSPollQuestionOption? selectedOption;
+ List selectedOptions = [];
+ bool isPollAnswerValid = false;
+
+ void resetPollAnswerValidity() {
+ if (selectedOption != null || selectedOptions.isNotEmpty) {
+ isPollAnswerValid = true;
+ } else {
+ isPollAnswerValid = false;
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.only(bottom: 24),
+ child: Container(
+ decoration: BoxDecoration(
+ color: HMSThemeColors.surfaceDefault,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ children: [
+ HMSTitleText(
+ text:
+ "QUESTION ${widget.questionNumber + 1} OF ${widget.totalQuestions}: ",
+ textColor: HMSThemeColors.onSurfaceLowEmphasis,
+ fontSize: 10,
+ letterSpacing: 1.5,
+ lineHeight: 16),
+ HMSTitleText(
+ fontSize: 10,
+ letterSpacing: 1.5,
+ lineHeight: 16,
+ text: Utilities.getQuestionType(widget.question.type),
+ textColor: HMSThemeColors.onSurfaceLowEmphasis)
+ ],
+ ),
+ const SizedBox(
+ height: 16,
+ ),
+ HMSTitleText(
+ text: widget.question.text,
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ maxLines: 3,
+ fontWeight: FontWeight.w400,
+ ),
+ ListView.builder(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: widget.question.options.length,
+ itemBuilder: (BuildContext context, index) {
+ ///TODO: Add dynamic padding based on text length
+ return Row(
+ children: [
+ Checkbox(
+ activeColor: HMSThemeColors.onSurfaceHighEmphasis,
+ checkColor: HMSThemeColors.surfaceDefault,
+ value: ((widget.question.type ==
+ HMSPollQuestionType.singleChoice)
+ ? selectedOption ==
+ widget.question.options[index]
+ : selectedOptions
+ .contains(widget.question.options[index])),
+ shape: widget.question.type ==
+ HMSPollQuestionType.singleChoice
+ ? const CircleBorder()
+ : const RoundedRectangleBorder(
+ borderRadius:
+ BorderRadius.all(Radius.circular(4))),
+ onChanged: (value) {
+ if (value == true) {
+ if (widget.question.type ==
+ HMSPollQuestionType.singleChoice) {
+ selectedOption =
+ widget.question.options[index];
+ } else if (widget.question.type ==
+ HMSPollQuestionType.multiChoice) {
+ selectedOptions
+ .add(widget.question.options[index]);
+ }
+ } else {
+ if (widget.question.type ==
+ HMSPollQuestionType.multiChoice) {
+ selectedOptions
+ .remove(widget.question.options[index]);
+ }
+ }
+ resetPollAnswerValidity();
+ setState(() {});
+ }),
+ Expanded(
+ child: HMSSubheadingText(
+ text: widget.question.options[index].text ?? "",
+ maxLines: 3,
+ textColor: HMSThemeColors.onSurfaceHighEmphasis),
+ )
+ ],
+ );
+ }),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ HMSButton(
+ width: MediaQuery.of(context).size.width * 0.21,
+ onPressed: () {
+ if (isPollAnswerValid) {
+ if (widget.question.type ==
+ HMSPollQuestionType.singleChoice &&
+ selectedOption != null) {
+ context
+ .read()
+ .addSingleChoicePollResponse(
+ context.read().poll,
+ widget.question,
+ selectedOption!);
+ } else if (widget.question.type ==
+ HMSPollQuestionType.multiChoice &&
+ selectedOptions.isNotEmpty) {
+ context
+ .read()
+ .addMultiChoicePollResponse(
+ context.read().poll,
+ widget.question,
+ selectedOptions);
+ }
+ }
+ },
+ buttonBackgroundColor: isPollAnswerValid
+ ? HMSThemeColors.primaryDefault
+ : HMSThemeColors.primaryDisabled,
+ childWidget: HMSTitleText(
+ text: "Vote",
+ textColor: isPollAnswerValid
+ ? HMSThemeColors.onPrimaryHighEmphasis
+ : HMSThemeColors.onPrimaryLowEmphasis))
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/toasts/hms_poll_start_toast.dart b/packages/hms_room_kit/lib/src/widgets/toasts/hms_poll_start_toast.dart
new file mode 100644
index 000000000..42967add7
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/toasts/hms_poll_start_toast.dart
@@ -0,0 +1,83 @@
+///Dart imports
+import 'dart:math' as math;
+
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:hms_room_kit/src/layout_api/hms_theme_colors.dart';
+import 'package:hms_room_kit/src/meeting/meeting_store.dart';
+import 'package:hms_room_kit/src/model/poll_store.dart';
+import 'package:hms_room_kit/src/widgets/bottom_sheets/poll_vote_bottom_sheet.dart';
+import 'package:hms_room_kit/src/widgets/common_widgets/hms_subheading_text.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_toast.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_toast_button.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_toasts_type.dart';
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+import 'package:provider/provider.dart';
+
+class HMSPollStartToast extends StatelessWidget {
+ final HMSPoll poll;
+ final MeetingStore meetingStore;
+
+ const HMSPollStartToast(
+ {Key? key, required this.poll, required this.meetingStore})
+ : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return HMSToast(
+ leading: SvgPicture.asset(
+ "packages/hms_room_kit/lib/src/assets/icons/polls.svg",
+ height: 24,
+ width: 24,
+ colorFilter: ColorFilter.mode(
+ HMSThemeColors.onSurfaceHighEmphasis, BlendMode.srcIn),
+ ),
+ subtitle: SizedBox(
+ width: MediaQuery.of(context).size.width * 0.5,
+ child: HMSSubheadingText(
+ text:
+ "${poll.createdBy?.name.substring(0, math.min(8, poll.createdBy?.name.length ?? 0)) ?? ""}${(poll.createdBy?.name.length ?? 0) > 8 ? "..." : ""} started a new poll",
+ textColor: HMSThemeColors.onSurfaceHighEmphasis,
+ fontWeight: FontWeight.w600,
+ letterSpacing: 0.1,
+ maxLines: 2,
+ ),
+ ),
+ action: HMSToastButton(
+ buttonTitle: "Vote",
+ action: () {
+ var pollStore = context.read();
+ showModalBottomSheet(
+ isScrollControlled: true,
+ backgroundColor: HMSThemeColors.surfaceDim,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16)),
+ ),
+ context: context,
+ builder: (ctx) => ChangeNotifierProvider.value(
+ value: meetingStore,
+ child: ChangeNotifierProvider.value(
+ value: pollStore,
+ child: const PollVoteBottomSheet(),
+ )));
+ meetingStore.removeToast(HMSToastsType.pollStartedToast,
+ data: poll.pollId);
+ },
+ height: 36,
+ buttonColor: HMSThemeColors.secondaryDefault,
+ textColor: HMSThemeColors.onSecondaryHighEmphasis,
+ ),
+ cancelToastButton: GestureDetector(
+ onTap: () => meetingStore.removeToast(HMSToastsType.pollStartedToast,
+ data: poll.pollId),
+ child: Icon(
+ Icons.close,
+ color: HMSThemeColors.onSurfaceHighEmphasis,
+ size: 24,
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/hms_room_kit/lib/src/widgets/toasts/hms_toasts_type.dart b/packages/hms_room_kit/lib/src/widgets/toasts/hms_toasts_type.dart
index f366d16d0..491fb7430 100644
--- a/packages/hms_room_kit/lib/src/widgets/toasts/hms_toasts_type.dart
+++ b/packages/hms_room_kit/lib/src/widgets/toasts/hms_toasts_type.dart
@@ -5,5 +5,6 @@ enum HMSToastsType {
localScreenshareToast,
roleChangeDeclineToast,
chatPauseResumeToast,
- errorToast
+ errorToast,
+ pollStartedToast
}
diff --git a/packages/hms_room_kit/lib/src/widgets/toasts/toast_widget.dart b/packages/hms_room_kit/lib/src/widgets/toasts/toast_widget.dart
new file mode 100644
index 000000000..6607376d6
--- /dev/null
+++ b/packages/hms_room_kit/lib/src/widgets/toasts/toast_widget.dart
@@ -0,0 +1,79 @@
+///Package imports
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+///Project imports
+import 'package:hms_room_kit/hms_room_kit.dart';
+import 'package:hms_room_kit/src/meeting/meeting_store.dart';
+import 'package:hms_room_kit/src/model/poll_store.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_bring_on_stage_toast.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_chat_pause_resume_toast.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_local_screen_share_toast.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_poll_start_toast.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_recording_error_toast.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_role_change_decline_toast.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_toast_model.dart';
+import 'package:hms_room_kit/src/widgets/toasts/hms_toasts_type.dart';
+
+///[ToastWidget] returns toast based on the toast type
+class ToastWidget extends StatelessWidget {
+ final HMSToastModel toast;
+ final int index;
+ final int toastsCount;
+ final MeetingStore meetingStore;
+
+ const ToastWidget({
+ super.key,
+ required this.toast,
+ required this.index,
+ required this.toastsCount,
+ required this.meetingStore,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ switch (toast.hmsToastType) {
+ case HMSToastsType.roleChangeToast:
+ return HMSBringOnStageToast(
+ toastColor: Utilities.getToastColor(index, toastsCount),
+ peer: toast.toastData,
+ meetingStore: meetingStore,
+ );
+ case HMSToastsType.recordingErrorToast:
+ return HMSRecordingErrorToast(
+ recordingError: toast.toastData,
+ meetingStore: meetingStore,
+ );
+ case HMSToastsType.localScreenshareToast:
+ return HMSLocalScreenShareToast(
+ toastColor: Utilities.getToastColor(index, toastsCount),
+ meetingStore: meetingStore,
+ );
+ case HMSToastsType.roleChangeDeclineToast:
+ return HMSRoleChangeDeclineToast(
+ peer: toast.toastData,
+ toastColor: Utilities.getToastColor(index, toastsCount),
+ meetingStore: meetingStore,
+ );
+ case HMSToastsType.chatPauseResumeToast:
+ return HMSChatPauseResumeToast(
+ isChatEnabled: toast.toastData["enabled"],
+ userName: toast.toastData["updatedBy"],
+ meetingStore: meetingStore,
+ );
+ case HMSToastsType.pollStartedToast:
+ return ChangeNotifierProvider.value(
+ value: toast.toastData! as HMSPollStore,
+ child: Transform.scale(
+ scale: Utilities.getToastScale(index, toastsCount),
+ child: HMSPollStartToast(
+ poll: toast.toastData.poll,
+ meetingStore: meetingStore,
+ ),
+ ),
+ );
+ default:
+ return const SizedBox();
+ }
+ }
+}
diff --git a/packages/hms_room_kit/pubspec.lock b/packages/hms_room_kit/pubspec.lock
index 7ec7253f4..d803a2f6b 100644
--- a/packages/hms_room_kit/pubspec.lock
+++ b/packages/hms_room_kit/pubspec.lock
@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: archive
- sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
+ sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
url: "https://pub.dev"
source: hosted
- version: "3.4.9"
+ version: "3.4.10"
args:
dependency: transitive
description:
@@ -204,10 +204,10 @@ packages:
dependency: "direct main"
description:
name: hmssdk_flutter
- sha256: ff1697824b42d31cb093fd4319c8fa3ba6872b7877707630062ed3cd9cf40813
+ sha256: bad4ff87c677970f0c9acfcd4e84b60089461e1c1eef23e3a3ae6ab191484b95
url: "https://pub.dev"
source: hosted
- version: "1.9.8"
+ version: "1.9.9"
http:
dependency: transitive
description:
@@ -324,10 +324,10 @@ packages:
dependency: "direct main"
description:
name: path_provider
- sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
+ sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
url: "https://pub.dev"
source: hosted
- version: "2.1.1"
+ version: "2.1.2"
path_provider_android:
dependency: transitive
description:
@@ -340,10 +340,10 @@ packages:
dependency: transitive
description:
name: path_provider_foundation
- sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
+ sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
url: "https://pub.dev"
source: hosted
- version: "2.3.1"
+ version: "2.3.2"
path_provider_linux:
dependency: transitive
description:
@@ -356,10 +356,10 @@ packages:
dependency: transitive
description:
name: path_provider_platform_interface
- sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
+ sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
- version: "2.1.1"
+ version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
@@ -420,26 +420,26 @@ packages:
dependency: transitive
description:
name: platform
- sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
+ sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
source: hosted
- version: "3.1.3"
+ version: "3.1.4"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
- sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
- version: "2.1.7"
+ version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
- sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
+ sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
url: "https://pub.dev"
source: hosted
- version: "3.7.3"
+ version: "3.7.4"
provider:
dependency: "direct main"
description:
@@ -484,10 +484,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_foundation
- sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
+ sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
url: "https://pub.dev"
source: hosted
- version: "2.3.4"
+ version: "2.3.5"
shared_preferences_linux:
dependency: transitive
description:
@@ -500,10 +500,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_platform_interface
- sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
+ sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
url: "https://pub.dev"
source: hosted
- version: "2.3.1"
+ version: "2.3.2"
shared_preferences_web:
dependency: transitive
description:
@@ -601,26 +601,26 @@ packages:
dependency: "direct main"
description:
name: url_launcher
- sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86
+ sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c
url: "https://pub.dev"
source: hosted
- version: "6.2.2"
+ version: "6.2.4"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
- sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def"
+ sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f"
url: "https://pub.dev"
source: hosted
- version: "6.2.0"
+ version: "6.2.2"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
- sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3
+ sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03"
url: "https://pub.dev"
source: hosted
- version: "6.2.1"
+ version: "6.2.4"
url_launcher_linux:
dependency: transitive
description:
@@ -641,10 +641,10 @@ packages:
dependency: transitive
description:
name: url_launcher_platform_interface
- sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50"
+ sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f
url: "https://pub.dev"
source: hosted
- version: "2.2.0"
+ version: "2.3.1"
url_launcher_web:
dependency: transitive
description:
@@ -673,26 +673,26 @@ packages:
dependency: transitive
description:
name: vector_graphics
- sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43"
+ sha256: "18f6690295af52d081f6808f2f7c69f0eed6d7e23a71539d75f4aeb8f0062172"
url: "https://pub.dev"
source: hosted
- version: "1.1.9+1"
+ version: "1.1.9+2"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
- sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7"
+ sha256: "531d20465c10dfac7f5cd90b60bbe4dd9921f1ec4ca54c83ebb176dbacb7bb2d"
url: "https://pub.dev"
source: hosted
- version: "1.1.9+1"
+ version: "1.1.9+2"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
- sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26
+ sha256: "03012b0a33775c5530576b70240308080e1d5050f0faf000118c20e6463bc0ad"
url: "https://pub.dev"
source: hosted
- version: "1.1.9+1"
+ version: "1.1.9+2"
vector_math:
dependency: transitive
description:
@@ -729,10 +729,10 @@ packages:
dependency: transitive
description:
name: xdg_directories
- sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
+ sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev"
source: hosted
- version: "1.0.3"
+ version: "1.0.4"
xml:
dependency: transitive
description:
diff --git a/packages/hms_room_kit/pubspec.yaml b/packages/hms_room_kit/pubspec.yaml
index ae5649d88..2916cfb6c 100644
--- a/packages/hms_room_kit/pubspec.yaml
+++ b/packages/hms_room_kit/pubspec.yaml
@@ -1,6 +1,6 @@
name: hms_room_kit
description: 100ms Room Kit provides simple & easy to use UI components to build Live Streaming & Video Conferencing experiences in your apps.
-version: 1.0.11
+version: 1.0.12
homepage: https://www.100ms.live/
repository: https://github.com/100mslive/100ms-flutter
issue_tracker: https://github.com/100mslive/100ms-flutter/issues
@@ -14,7 +14,7 @@ dependencies:
flutter:
sdk: flutter
- hmssdk_flutter: 1.9.8
+ hmssdk_flutter: 1.9.9
intl: ^0.18.0
permission_handler: ^11.0.0
provider: ^6.0.5
diff --git a/packages/hmssdk_flutter/CHANGELOG.md b/packages/hmssdk_flutter/CHANGELOG.md
index 611dd8110..d47ace539 100644
--- a/packages/hmssdk_flutter/CHANGELOG.md
+++ b/packages/hmssdk_flutter/CHANGELOG.md
@@ -5,6 +5,34 @@
| hms_room_kit | [![Pub Version](https://img.shields.io/pub/v/hms_room_kit)](https://pub.dev/packages/hms_room_kit) |
| hmssdk_flutter | [![Pub Version](https://img.shields.io/pub/v/hmssdk_flutter)](https://pub.dev/packages/hmssdk_flutter) |
+# 1.9.9 - 2024-02-12
+
+| Package | Version |
+| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
+| hms_room_kit | 1.0.12 |
+| hmssdk_flutter | 1.9.9 |
+
+### ✨ Added
+
+- Introducing Polls
+
+ - APIs for Comprehensive Poll Management:
+ - `quickStartPoll` to start polls
+ - `addSingleChoicePollResponse` & `addMultiChoicePollResponse` for adding single and multi choice response
+ - `stopPoll` to stop polls
+
+ - Poll Update Listeners for Real-Time Notifications:
+ - Use `addPollUpdateListener` to start listening to poll updates
+ - Use `removePollUpdateListener` to stop listening to poll updates
+
+ - Enhanced Poll Permissions within `HMSPermissions`:
+ - `pollRead` property ensures controlled read access to poll results and details.
+ - `pollWrite` property enables secure write access, allowing for poll creation and response submission.
+
+Updated to Android SDK 2.9.0 & iOS SDK 1.5.0
+
+**Full Changelog**: [1.9.8...1.9.9](https://github.com/100mslive/100ms-flutter/compare/1.9.8...1.9.9)
+
# 1.9.8 - 2024-02-01
| Package | Version |
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HmssdkFlutterPlugin.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HmssdkFlutterPlugin.kt
index df1bb4cf6..8b76aca57 100644
--- a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HmssdkFlutterPlugin.kt
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/HmssdkFlutterPlugin.kt
@@ -3,7 +3,6 @@ package live.hms.hmssdk_flutter
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
-import android.content.Context.*
import android.content.Intent
import android.content.IntentFilter
import android.media.projection.MediaProjectionManager
@@ -29,6 +28,7 @@ import kotlinx.coroutines.launch
import live.hms.hmssdk_flutter.Constants.Companion.METHOD_CALL
import live.hms.hmssdk_flutter.hls_player.HMSHLSPlayerAction
import live.hms.hmssdk_flutter.methods.*
+import live.hms.hmssdk_flutter.poll_extension.HMSPollExtension
import live.hms.hmssdk_flutter.views.HMSHLSPlayerFactory
import live.hms.hmssdk_flutter.views.HMSTextureView
import live.hms.hmssdk_flutter.views.HMSVideoViewFactory
@@ -36,7 +36,11 @@ import live.hms.video.audio.HMSAudioManager.*
import live.hms.video.connection.stats.*
import live.hms.video.error.HMSException
import live.hms.video.events.AgentType
+import live.hms.video.interactivity.HmsPollUpdateListener
import live.hms.video.media.tracks.*
+import live.hms.video.polls.HMSPollBuilder
+import live.hms.video.polls.models.HMSPollUpdateType
+import live.hms.video.polls.models.HmsPoll
import live.hms.video.sdk.*
import live.hms.video.sdk.models.*
import live.hms.video.sdk.models.enums.*
@@ -61,12 +65,14 @@ class HmssdkFlutterPlugin :
private var rtcStatsChannel: EventChannel? = null
private var sessionStoreChannel: EventChannel? = null
var hlsPlayerChannel: EventChannel? = null
+ private var pollsEventChannel : EventChannel? = null
private var eventSink: EventChannel.EventSink? = null
private var previewSink: EventChannel.EventSink? = null
private var logsSink: EventChannel.EventSink? = null
private var rtcSink: EventChannel.EventSink? = null
private var sessionStoreSink: EventChannel.EventSink? = null
var hlsPlayerSink: EventChannel.EventSink? = null
+ private var pollsSink: EventChannel.EventSink? = null
private lateinit var activity: Activity
var hmssdk: HMSSDK? = null
private lateinit var hmsVideoFactory: HMSVideoViewFactory
@@ -106,6 +112,9 @@ class HmssdkFlutterPlugin :
this.hlsPlayerChannel =
EventChannel(flutterPluginBinding.binaryMessenger, "hls_player_channel")
+ this.pollsEventChannel =
+ EventChannel(flutterPluginBinding.binaryMessenger, "polls_event_channel")
+
this.meetingEventChannel?.setStreamHandler(this) ?: Log.e("Channel Error", "Meeting event channel not found")
this.channel?.setMethodCallHandler(this) ?: Log.e("Channel Error", "Event channel not found")
this.previewChannel?.setStreamHandler(this) ?: Log.e("Channel Error", "Preview channel not found")
@@ -113,6 +122,7 @@ class HmssdkFlutterPlugin :
this.rtcStatsChannel?.setStreamHandler(this) ?: Log.e("Channel Error", "RTC Stats channel not found")
this.sessionStoreChannel?.setStreamHandler(this) ?: Log.e("Channel Error", "Session Store channel not found")
this.hlsPlayerChannel?.setStreamHandler(this) ?: Log.e("Channel Error", "HLS Player channel not found")
+ this.pollsEventChannel?.setStreamHandler(this) ?: Log.e("Channel Error","polls events channel not found")
this.hmsVideoFactory = HMSVideoViewFactory(this)
this.hmsHLSPlayerFactory = HMSHLSPlayerFactory(this)
@@ -276,6 +286,10 @@ class HmssdkFlutterPlugin :
HMSPeerListIteratorAction.peerListIteratorAction(call, result, hmssdk!!)
}
+ "add_poll_update_listener", "remove_poll_update_listener", "quick_start_poll", "add_single_choice_poll_response", "add_multi_choice_poll_response", "stop_poll" -> {
+ pollActions(call,result)
+ }
+
else -> {
result.notImplemented()
}
@@ -453,6 +467,21 @@ class HmssdkFlutterPlugin :
}
}
+ private var currentPolls = ArrayList()
+ private fun pollActions(call: MethodCall, result: Result){
+ when(call.method){
+ "add_poll_update_listener" -> {
+ hmssdk?.getHmsInteractivityCenter()?.pollUpdateListener = hmsPollListener
+ }
+ "remove_poll_update_listener" -> {
+ hmssdk?.getHmsInteractivityCenter()?.pollUpdateListener = null
+ }
+ else -> hmssdk?.let {
+ HMSPollAction.pollActions(call,result, it,currentPolls)
+ }
+ }
+ }
+
override fun onDetachedFromEngine(
@NonNull binding: FlutterPlugin.FlutterPluginBinding,
) {
@@ -464,12 +493,14 @@ class HmssdkFlutterPlugin :
rtcStatsChannel?.setStreamHandler(null) ?: Log.e("Channel Error", "RTC Stats channel not found")
sessionStoreChannel?.setStreamHandler(null) ?: Log.e("Channel Error", "Session Store channel not found")
hlsPlayerChannel?.setStreamHandler(null) ?: Log.e("Channel Error", "HLS Player channel not found")
+ pollsEventChannel?.setStreamHandler(null)?: Log.e("Channel Error", "polls event channel not found")
eventSink = null
previewSink = null
rtcSink = null
logsSink = null
sessionStoreSink = null
hlsPlayerSink = null
+ pollsSink = null
hmssdkFlutterPlugin = null
hmsBinaryMessenger = null
hmsTextureRegistry = null
@@ -600,6 +631,8 @@ class HmssdkFlutterPlugin :
this.sessionStoreSink = events
} else if (nameOfEventSink == "hls_player") {
this.hlsPlayerSink = events
+ } else if(nameOfEventSink == "polls") {
+ this.pollsSink = events
}
}
@@ -1544,11 +1577,7 @@ class HmssdkFlutterPlugin :
private fun startScreenShare(result: Result) {
androidScreenshareResult = result
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- activity.applicationContext?.registerReceiver(activityBroadcastReceiver, IntentFilter("ACTIVITY_RECEIVER"), RECEIVER_EXPORTED)
- }else {
- activity.applicationContext?.registerReceiver(activityBroadcastReceiver, IntentFilter("ACTIVITY_RECEIVER"))
- }
+ activity.applicationContext?.registerReceiver(activityBroadcastReceiver, IntentFilter("ACTIVITY_RECEIVER"))
val mediaProjectionManager: MediaProjectionManager =
activity.getSystemService(
Context.MEDIA_PROJECTION_SERVICE,
@@ -1613,11 +1642,7 @@ class HmssdkFlutterPlugin :
) {
androidAudioShareResult = result
mode = call.argument("audio_mixing_mode")
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- activity.applicationContext?.registerReceiver(activityBroadcastReceiver, IntentFilter("ACTIVITY_RECEIVER"), RECEIVER_EXPORTED)
- }else {
- activity.applicationContext?.registerReceiver(activityBroadcastReceiver, IntentFilter("ACTIVITY_RECEIVER"),)
- }
+ activity.applicationContext?.registerReceiver(activityBroadcastReceiver, IntentFilter("ACTIVITY_RECEIVER"))
val mediaProjectionManager: MediaProjectionManager? =
activity.getSystemService(
Context.MEDIA_PROJECTION_SERVICE,
@@ -2076,4 +2101,37 @@ class HmssdkFlutterPlugin :
}
}
}
+
+ private val hmsPollListener = object : HmsPollUpdateListener{
+ override fun onPollUpdate(hmsPoll: HmsPoll, hmsPollUpdateType: HMSPollUpdateType) {
+
+ if(hmsPollUpdateType == HMSPollUpdateType.started){
+ currentPolls.add(hmsPoll)
+ } else if(hmsPollUpdateType == HMSPollUpdateType.resultsupdated){
+ val index = currentPolls.indexOfFirst { it.pollId == hmsPoll.pollId }
+ if(index != -1){
+ currentPolls[index] = hmsPoll
+ }
+ } else if(hmsPollUpdateType == HMSPollUpdateType.stopped){
+ val index = currentPolls.indexOfFirst { it.pollId == hmsPoll.pollId }
+ if(index != -1){
+ currentPolls.removeAt(index)
+ }
+ }
+
+ val args = HashMap()
+ args["event_name"] = "on_poll_update"
+
+ val pollHashMap = HashMap()
+ pollHashMap["poll"] = HMSPollExtension.toDictionary(hmsPoll)
+ pollHashMap["poll_update_type"] = HMSPollExtension.getPollUpdateType(hmsPollUpdateType)
+ args["data"] = pollHashMap
+
+ CoroutineScope(Dispatchers.Main).launch {
+ pollsSink?.success(args)
+ }
+ }
+ }
+
+
}
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/hms_role_components/PermissionParamsExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/hms_role_components/PermissionParamsExtension.kt
index a7123ba96..0ea6541f8 100644
--- a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/hms_role_components/PermissionParamsExtension.kt
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/hms_role_components/PermissionParamsExtension.kt
@@ -15,6 +15,8 @@ class PermissionParamsExtension {
args["remove_others"] = permissionsParams.removeOthers
args["rtmp_streaming"] = permissionsParams.rtmpStreaming
args["un_mute"] = permissionsParams.unmute
+ args["poll_read"] = permissionsParams.pollRead
+ args["poll_write"] = permissionsParams.pollWrite
return args
}
}
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/methods/HMSPollAction.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/methods/HMSPollAction.kt
new file mode 100644
index 000000000..1386e1c2f
--- /dev/null
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/methods/HMSPollAction.kt
@@ -0,0 +1,186 @@
+package live.hms.hmssdk_flutter.methods
+
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import live.hms.hmssdk_flutter.HMSCommonAction
+import live.hms.hmssdk_flutter.HMSErrorLogger
+import live.hms.hmssdk_flutter.HMSExceptionExtension
+import live.hms.hmssdk_flutter.HMSResultExtension
+import live.hms.hmssdk_flutter.poll_extension.HMSPollBuilderExtension
+import live.hms.hmssdk_flutter.poll_extension.HMSPollAnswerResponseExtension
+import live.hms.video.error.HMSException
+import live.hms.video.polls.HMSPollResponseBuilder
+import live.hms.video.polls.models.HmsPoll
+import live.hms.video.polls.models.HmsPollState
+import live.hms.video.polls.models.answer.PollAnswerResponse
+import live.hms.video.polls.models.question.HMSPollQuestion
+import live.hms.video.polls.models.question.HMSPollQuestionOption
+import live.hms.video.sdk.HMSSDK
+import live.hms.video.sdk.HmsTypedActionResultListener
+
+class HMSPollAction {
+
+ companion object{
+ fun pollActions(call: MethodCall, result: MethodChannel.Result, hmssdk: HMSSDK, polls: ArrayList?){
+ when(call.method){
+ "quick_start_poll" -> quickStartPoll(call,result,hmssdk)
+ "add_single_choice_poll_response" -> addSingleChoicePollResponse(call,result,hmssdk,polls)
+ "add_multi_choice_poll_response" -> addMultiChoicePollResponse(call,result,hmssdk,polls)
+ "stop_poll" -> stopPoll(call,result,hmssdk,polls)
+ }
+ }
+
+ private fun quickStartPoll(call: MethodCall, result: MethodChannel.Result, hmssdk: HMSSDK){
+
+ val pollBuilderMap = call.argument?>("poll_builder")
+
+ val pollBuilder = HMSPollBuilderExtension.toHMSPollBuilder(pollBuilderMap,hmssdk)
+
+ pollBuilder?.let {
+ hmssdk.getHmsInteractivityCenter().quickStartPoll( pollBuilder, HMSCommonAction.getActionListener(result))
+ }?:run{
+ HMSErrorLogger.returnArgumentsError("pollBuilder parsing failed")
+ }
+
+ }
+
+ private fun addSingleChoicePollResponse(call: MethodCall, methodChannelResult: MethodChannel.Result, hmssdk: HMSSDK, currentPolls: ArrayList?){
+
+ val pollId = call.argument("poll_id")
+ val index = call.argument("question_index")
+ val userId = call.argument("user_id")
+ val answer = call.argument>("answer")
+
+ /*
+ * Here we get index for the option selected by the user
+ * if the option doesn't exist we return the arguments error
+ */
+ val optionIndex = answer?.let {
+ it["index"] as Int
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("Invalid option index")
+ return
+ }
+
+ /*
+ * We fetch the polls which are currently active and find the poll matching the pollId
+ * passed from flutter.
+ * We use the poll object and find the question which the user has answered based on the
+ * index passed from flutter.
+ * After getting the question object we use the index from above to get the HMSPollQuestionOption
+ * object
+ * Finally we build the response builder and add the response.
+ *
+ * If anywhere the sdk is unable to find the property we return the error
+ */
+ currentPolls?.find { it.pollId == pollId }?.let {poll ->
+ index?.let {questionIndex ->
+
+ poll.questions?.get(questionIndex)?.let { currentQuestion ->
+ /*
+ * Here the index needs to be subtracted by 1
+ * since the HMSPollQuestionOption object has indexing with 1
+ */
+ val questionOption = currentQuestion.options?.get(optionIndex - 1)
+ questionOption?.let {selectedOption ->
+ val response = HMSPollResponseBuilder(poll, userId).addResponse(currentQuestion,selectedOption)
+ hmssdk.getHmsInteractivityCenter().add(response, object : HmsTypedActionResultListener{
+ override fun onSuccess(result: PollAnswerResponse) {
+ methodChannelResult.success(HMSResultExtension.toDictionary(true,HMSPollAnswerResponseExtension.toDictionary(result)))
+ }
+ override fun onError(error: HMSException) {
+ methodChannelResult.success(HMSResultExtension.toDictionary(false,HMSExceptionExtension.toDictionary(error)))
+ }
+ })
+ }
+
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("Question not found")
+ return
+ }
+ }?: run {
+ HMSErrorLogger.returnArgumentsError("Incorrect question index")
+ return
+ }
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("No poll with given pollId found")
+ return
+ }
+ }
+
+ private fun addMultiChoicePollResponse(call: MethodCall, methodChannelResult: MethodChannel.Result, hmssdk: HMSSDK, currentPolls: ArrayList?){
+
+ val pollId = call.argument("poll_id")
+ val index = call.argument("question_index")
+ val userId = call.argument("user_id")
+ val answer = call.argument?>>("answer")
+
+ /*
+ * We fetch the polls which are currently active and find the poll matching the pollId
+ * passed from flutter.
+ * We use the poll object and find the question which the user has answered based on the
+ * index passed from flutter.
+ * After getting the question object we use the index from above to get the HMSPollQuestionOption
+ * object
+ * Finally we build the response builder and add the response.
+ *
+ * If anywhere the sdk is unable to find the property we return the error
+ */
+ currentPolls?.find { it.pollId == pollId }?.let { poll ->
+ index?.let {questionIndex ->
+ poll.questions?.get(questionIndex)?.let { currentQuestion ->
+ val questionOptions = ArrayList()
+ answer?.forEach { selectedOptions ->
+ selectedOptions as HashMap
+ /*
+ * Here the index needs to be subtracted by 1
+ * since the HMSPollQuestionOption object has indexing with 1
+ */
+ selectedOptions["index"]?.let {
+ index -> index as Int
+ val questionOption = currentQuestion.options?.get(index - 1)
+ questionOption?.let {option ->
+ questionOptions.add(option)
+ }
+ }
+ }
+
+ val response = HMSPollResponseBuilder(poll, userId).addResponse(currentQuestion,questionOptions)
+ hmssdk.getHmsInteractivityCenter().add(response, object : HmsTypedActionResultListener{
+ override fun onSuccess(result: PollAnswerResponse) {
+ methodChannelResult.success(HMSResultExtension.toDictionary(true,HMSPollAnswerResponseExtension.toDictionary(result)))
+ }
+ override fun onError(error: HMSException) {
+ methodChannelResult.success(HMSResultExtension.toDictionary(false,HMSExceptionExtension.toDictionary(error)))
+ }
+ })
+
+ }?: run {
+ HMSErrorLogger.returnArgumentsError("Question not found")
+ return
+ }
+ }?:run{
+ HMSErrorLogger.returnArgumentsError("Incorrect question index")
+ return
+ }
+
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("No poll with given pollId found")
+ return
+ }
+ }
+
+ private fun stopPoll(call: MethodCall, result: MethodChannel.Result, hmssdk: HMSSDK, currentPolls: ArrayList?){
+ val pollId = call.argument("poll_id")
+
+ val poll = currentPolls?.first{
+ it.pollId == pollId
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("No Poll with given pollId found")
+ return
+ }
+ hmssdk.getHmsInteractivityCenter().stop(poll,HMSCommonAction.getActionListener(result))
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollAnswerExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollAnswerExtension.kt
new file mode 100644
index 000000000..0d4fa4718
--- /dev/null
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollAnswerExtension.kt
@@ -0,0 +1,97 @@
+package live.hms.hmssdk_flutter.poll_extension
+
+import live.hms.hmssdk_flutter.HMSErrorLogger
+import live.hms.hmssdk_flutter.poll_extension.HMSPollQuestionExtension.Companion.getStringFromPollQuestionType
+import live.hms.video.polls.models.answer.HmsPollAnswer
+import live.hms.video.polls.models.network.HMSPollQuestionResponse
+
+class HMSPollAnswerExtension {
+
+ companion object {
+
+ fun toDictionary(answer: HmsPollAnswer?): HashMap? {
+ answer?.let {
+ val map = HashMap()
+ map["answer"] = it.answerText
+ map["duration"] = it.durationMillis
+ map["question_id"] = it.questionId
+ map["question_type"] = getStringFromPollQuestionType(it.questionType)
+ map["selected_option"] = it.selectedOption
+ map["selected_options"] = it.selectedOptions
+ map["skipped"] = it.skipped
+ map["update"] = it.update
+ return map
+ } ?: run {
+ return null
+ }
+ }
+
+ fun toHMSPollAnswer(answerMap: HashMap?): HmsPollAnswer?{
+
+ answerMap?.let {
+
+ val answer = answerMap["answer"]?.let {
+ it as String
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("answer should not be null")
+ return null
+ }
+
+ val duration = answerMap["duration"]?.let {
+ it as Long
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("duration should not be null")
+ return null
+ }
+
+ val questionId = answerMap["question_id"]?.let {
+ it as Int
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("questionId should not be null")
+ return null
+ }
+
+ val questionType = answerMap["question_type"]?.let {
+ HMSPollQuestionExtension.getPollQuestionTypeFromString(it as String)
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("questionType should not be null")
+ return null
+ }
+
+ val selectedOption = answerMap["selected_option"]?.let {
+ it as Int
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("selectedOption should not be null")
+ return null
+ }
+
+ val selectedOptions = answerMap["selected_options"]?.let {
+ it as ArrayList
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("selectedOptions should not be null")
+ return null
+ }
+
+ val skipped = answerMap["skipped"]?.let {
+ it as Boolean
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("skipped should not be null")
+ return null
+ }
+
+ val update = answerMap["update"]?.let {
+ it as Boolean
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("update should not be null")
+ return null
+ }
+
+ return HmsPollAnswer(questionId,questionType,skipped,selectedOption,selectedOptions,answer,update,duration)
+
+
+ }?:run {
+ return null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollAnswerResponseExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollAnswerResponseExtension.kt
new file mode 100644
index 000000000..cf0c2e38e
--- /dev/null
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollAnswerResponseExtension.kt
@@ -0,0 +1,27 @@
+package live.hms.hmssdk_flutter.poll_extension
+
+import live.hms.hmssdk_flutter.HMSExceptionExtension
+import live.hms.video.polls.models.answer.PollAnswerResponse
+
+class HMSPollAnswerResponseExtension {
+
+ companion object{
+ fun toDictionary(pollAnswerResponse: PollAnswerResponse):HashMap?{
+
+ val map = HashMap()
+ val resultMapList = ArrayList>()
+ pollAnswerResponse.result.forEach{
+ val resultMap = HashMap()
+ resultMap["correct"] = it.correct
+ resultMap["error"] = HMSExceptionExtension.toDictionary(it.error)
+ resultMap["question_index"] = it.questionIndex
+
+ resultMapList.add(resultMap)
+ }
+
+ map["result"] = resultMapList
+
+ return map
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollBuilderExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollBuilderExtension.kt
new file mode 100644
index 000000000..ab86242c6
--- /dev/null
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollBuilderExtension.kt
@@ -0,0 +1,241 @@
+package live.hms.hmssdk_flutter.poll_extension
+
+import live.hms.hmssdk_flutter.HMSErrorLogger
+import live.hms.hmssdk_flutter.HMSRoleExtension
+import live.hms.video.polls.HMSPollBuilder
+import live.hms.video.polls.HMSPollQuestionBuilder
+import live.hms.video.polls.models.HmsPollCategory
+import live.hms.video.polls.models.HmsPollUserTrackingMode
+import live.hms.video.polls.models.question.HMSPollQuestion
+import live.hms.video.polls.models.question.HMSPollQuestionType
+import live.hms.video.sdk.HMSSDK
+import live.hms.video.sdk.models.role.HMSRole
+import okhttp3.internal.notify
+
+class HMSPollBuilderExtension {
+
+ companion object{
+
+ fun toHMSPollBuilder(pollBuilderMap: HashMap?,hmssdk: HMSSDK):HMSPollBuilder?{
+
+ pollBuilderMap?.let {
+ val pollBuilder = HMSPollBuilder.Builder()
+ pollBuilderMap["anonymous"]?.let{
+ pollBuilder.withAnonymous(it as Boolean)
+ }
+ pollBuilderMap["duration"]?.let {
+ pollBuilder.withDuration(it as Long)
+ }
+ pollBuilderMap["mode"]?.let {
+ pollBuilder.withUserTrackingMode(getPollUserTrackingModeFromString(it as String))
+ }
+ pollBuilderMap["poll_category"]?.let {
+ pollBuilder.withCategory(getPollCategoryFromString(it as String))
+ }
+
+ pollBuilderMap["poll_id"]?.let {
+ pollBuilder.withPollId(it as String)
+ }
+
+ pollBuilderMap["questions"]?.let {
+ val questions = it as ArrayList<*>
+ val pollQuestions = ArrayList()
+ questions.forEach { pollQuestion ->
+ pollQuestion as HashMap?
+ val pollQuestionBuilder = getPollQuestionBuilder(pollQuestion)
+ pollQuestionBuilder?.let {questionBuilder ->
+ pollBuilder.addQuestion(questionBuilder)
+ }
+ }
+
+ }
+
+ val availableRoles = hmssdk.getRoles()
+ pollBuilderMap["roles_that_can_view_responses"]?.let {
+ val roles = it as ArrayList<*>
+ val rolesThatCanViewResponses = ArrayList()
+ roles.forEach { forEveryRole ->
+ val role = availableRoles.find { role -> role.name == forEveryRole }
+ role?.let { currentRole ->
+ rolesThatCanViewResponses.add(currentRole)
+ }
+ }
+ pollBuilder.withRolesThatCanViewResponses(rolesThatCanViewResponses)
+ }
+
+ pollBuilderMap["roles_that_can_vote"]?.let {
+ val roles = it as ArrayList<*>
+ val rolesThatCanVote = ArrayList()
+ roles.forEach{ forEveryRole ->
+ val role = availableRoles.find { role -> role.name == forEveryRole }
+ role?.let {currentRole ->
+ rolesThatCanVote.add(currentRole)
+ }
+ }
+ pollBuilder.withRolesThatCanVote(rolesThatCanVote)
+ }
+
+ pollBuilderMap["title"]?.let {
+ pollBuilder.withTitle(it as String)
+ }
+
+ return pollBuilder.build()
+
+ }?: run {
+ return null
+ }
+
+
+ }
+
+ private fun getPollCategoryFromString(category: String): HmsPollCategory {
+ return when(category) {
+ "poll" -> HmsPollCategory.POLL
+ "quiz" -> HmsPollCategory.QUIZ
+ else -> HmsPollCategory.POLL
+ }
+ }
+
+
+ private fun getPollUserTrackingModeFromString(pollUserTrackingMode: String):HmsPollUserTrackingMode{
+ return when(pollUserTrackingMode){
+ "user_id" -> HmsPollUserTrackingMode.USER_ID
+ "peer_id" -> HmsPollUserTrackingMode.PEER_ID
+ "username"-> HmsPollUserTrackingMode.USERNAME
+ else -> HmsPollUserTrackingMode.USER_ID
+
+ }
+ }
+
+ private fun getPollQuestionTypeFromString(pollQuestionType: String): HMSPollQuestionType{
+ return when(pollQuestionType){
+ "multi_choice" -> HMSPollQuestionType.multiChoice
+ "short_answer" -> HMSPollQuestionType.shortAnswer
+ "long_answer" -> HMSPollQuestionType.longAnswer
+ "single_choice" -> HMSPollQuestionType.singleChoice
+ else -> HMSPollQuestionType.singleChoice
+ }
+ }
+
+ private fun getPollQuestionBuilder(pollQuestion: HashMap?):HMSPollQuestionBuilder?{
+
+ val pollQuestionBuilder : HMSPollQuestionBuilder.Builder
+ pollQuestion?.let {
+
+ val type = pollQuestion["type"]?.let {type ->
+ getPollQuestionTypeFromString(type as String)
+ }?:run{
+ HMSErrorLogger.returnArgumentsError("type should not be null")
+ return null
+ }
+
+ type.let {questionType ->
+ pollQuestionBuilder = HMSPollQuestionBuilder.Builder(questionType)
+ }
+
+ val canSkip = pollQuestion["can_skip"]?.let {canSkipQuestion ->
+ canSkipQuestion as Boolean
+ }
+
+ canSkip?.let { canSkipQuestion ->
+ pollQuestionBuilder.withCanBeSkipped(canSkipQuestion)
+ }
+
+ val text = pollQuestion["text"]?.let { text ->
+ text as String
+ }
+
+ text?.let {
+ pollQuestionBuilder.withTitle(text)
+ }
+
+ val duration = pollQuestion["duration"]?.let { duration ->
+ duration as Long
+ }
+
+ duration?.let { duration ->
+ pollQuestionBuilder.withDuration(duration)
+ }
+
+ val weight = pollQuestion["weight"]?.let {weight ->
+ weight as Int
+ }
+
+ weight?.let {
+ pollQuestionBuilder.withWeight(weight)
+ }
+
+ val answerHidden = pollQuestion["answer_hidden"]?.let {answerHidden ->
+ answerHidden as Boolean
+ }
+
+ answerHidden?.let {
+ pollQuestionBuilder.withAnswerHidden(answerHidden)
+ }
+
+ val maxLength = pollQuestion["max_length"]?.let { maxLength ->
+ maxLength as Long
+ }
+
+ maxLength?.let {
+ pollQuestionBuilder.withMaxLength(maxLength)
+ }
+
+
+ val minLength = pollQuestion["min_length"]?.let { minLength ->
+ minLength as Long
+ }
+
+ minLength?.let {
+ pollQuestionBuilder.withMinLength(minLength)
+ }
+
+ val pollOptions = pollQuestion["poll_options"]?.let { options ->
+ options as ArrayList
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("pollOptions should not be null")
+ null
+ }
+
+ pollOptions?.let {
+ pollOptions.forEach {
+ pollQuestionBuilder.addOption(it)
+ }
+ }
+
+ val option = pollQuestion["options"]?.let { options ->
+
+ options as ArrayList>
+ val optionMap = ArrayList>()
+
+ options.forEach {
+ optionMap.add(Pair(it.keys.first(),it.values.first()))
+ }
+ optionMap
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("options should not be null")
+ null
+ }
+
+ option?.let {
+ option.forEach {
+ pollQuestionBuilder.addQuizOption(it.first,it.second)
+ }
+ }
+
+ val canChangeResponse = pollQuestion["can_change_response"]?.let { canChangeResponse ->
+ canChangeResponse as Boolean
+ }
+
+ canChangeResponse?.let {
+ pollQuestionBuilder.withCanChangeResponse(it)
+ }
+
+ return pollQuestionBuilder.build()
+
+ }?:run{
+ return null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollExtension.kt
new file mode 100644
index 000000000..e70358047
--- /dev/null
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollExtension.kt
@@ -0,0 +1,91 @@
+package live.hms.hmssdk_flutter.poll_extension
+
+import live.hms.hmssdk_flutter.HMSPeerExtension
+import live.hms.hmssdk_flutter.HMSRoleExtension
+import live.hms.video.polls.models.*
+
+class HMSPollExtension {
+
+ companion object {
+ fun toDictionary(poll: HmsPoll): HashMap {
+
+ val map = HashMap()
+
+ map["anonymous"] = poll.anonymous
+ map["category"] = getPollCategory(poll.category)
+ map["created_by"] = HMSPeerExtension.toDictionary(poll.createdBy)
+ map["duration"] = poll.duration
+ map["mode"] = getPollUserTrackingMode(poll.mode)
+ map["poll_id"] = poll.pollId
+ map["question_count"] = poll.questionCount
+
+ val questions = ArrayList?>()
+ poll.questions?.forEach{
+ questions.add(HMSPollQuestionExtension.toDictionary(it))
+ }
+ map["questions"] = questions
+
+ map["result"] = HMSPollResultDisplayExtension.toDictionary(poll.result)
+
+ val rolesThatCanViewResponses = ArrayList?>()
+ poll.rolesThatCanViewResponses.forEach{
+ rolesThatCanViewResponses.add(HMSRoleExtension.toDictionary(it))
+ }
+ map["roles_that_can_view_responses"] = rolesThatCanViewResponses
+
+ val rolesThatCanVote = ArrayList?>()
+ poll.rolesThatCanVote.forEach{
+ rolesThatCanVote.add(HMSRoleExtension.toDictionary(it))
+ }
+ map["roles_that_can_vote"] = rolesThatCanVote
+
+ map["started_at"] = poll.startedAt * 1000
+ map["started_by"] = HMSPeerExtension.toDictionary(poll.startedBy)
+ map["state"] = getPollState(poll.state)
+ map["stopped_at"] = poll.stoppedAt?.let {
+ it * 1000
+ }?:run {
+ null
+ }
+ map["title"] = poll.title
+
+ return map
+ }
+
+ private fun getPollCategory(pollCategory: HmsPollCategory):String?{
+ return when(pollCategory){
+ HmsPollCategory.POLL -> "poll"
+ HmsPollCategory.QUIZ -> "quiz"
+ else -> null
+ }
+ }
+
+ private fun getPollUserTrackingMode(pollUserTrackingMode: HmsPollUserTrackingMode?):String?{
+ return when(pollUserTrackingMode){
+ HmsPollUserTrackingMode.USER_ID -> "user_id"
+ HmsPollUserTrackingMode.PEER_ID -> "peer_id"
+ HmsPollUserTrackingMode.USERNAME -> "username"
+ else -> null
+ }
+ }
+
+ private fun getPollState(pollState: HmsPollState):String?{
+ return when(pollState){
+ HmsPollState.CREATED -> "created"
+ HmsPollState.STARTED -> "started"
+ HmsPollState.STOPPED -> "stopped"
+ else -> null
+ }
+ }
+
+ fun getPollUpdateType(hmsPollUpdateType: HMSPollUpdateType):String?{
+ return when(hmsPollUpdateType){
+ HMSPollUpdateType.started -> "started"
+ HMSPollUpdateType.stopped -> "stopped"
+ HMSPollUpdateType.resultsupdated -> "results_updated"
+ else -> null
+ }
+ }
+ }
+}
+
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollQuestionAnswerExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollQuestionAnswerExtension.kt
new file mode 100644
index 000000000..e46e44a35
--- /dev/null
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollQuestionAnswerExtension.kt
@@ -0,0 +1,50 @@
+package live.hms.hmssdk_flutter.poll_extension
+
+import live.hms.hmssdk_flutter.HMSErrorLogger
+import live.hms.video.polls.models.answer.HMSPollQuestionAnswer
+
+class HMSPollQuestionAnswerExtension {
+
+ companion object {
+ fun toDictionary(answer: HMSPollQuestionAnswer?):HashMap?{
+ answer?.let {
+ val map = HashMap()
+ map["hidden"] = it.hidden
+ map["option"] = it.option
+ map["options"] = it.options
+ return map
+ }?:run{
+ return null
+ }
+ }
+
+ fun toPollQuestionAnswer(pollQuestionAnswer: HashMap?): HMSPollQuestionAnswer?{
+
+ pollQuestionAnswer?.let {
+
+ val hidden = pollQuestionAnswer["hidden"]?.let {
+ it as Boolean
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("hidden should not be null")
+ return null
+ }
+ val option = pollQuestionAnswer["option"]?.let {
+ it as Int
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("option should not be null")
+ return null
+ }
+ val options = pollQuestionAnswer["options"]?.let {
+ it as ArrayList
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("options should not be null")
+ return null
+ }
+
+ return HMSPollQuestionAnswer(hidden,option,options)
+ }?:run {
+ return null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollQuestionExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollQuestionExtension.kt
new file mode 100644
index 000000000..9414555e9
--- /dev/null
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollQuestionExtension.kt
@@ -0,0 +1,152 @@
+package live.hms.hmssdk_flutter.poll_extension
+
+import live.hms.hmssdk_flutter.HMSErrorLogger
+import live.hms.video.polls.models.answer.HmsPollAnswer
+import live.hms.video.polls.models.question.HMSPollQuestion
+import live.hms.video.polls.models.question.HMSPollQuestionOption
+import live.hms.video.polls.models.question.HMSPollQuestionType
+
+class HMSPollQuestionExtension {
+ companion object{
+ fun toDictionary(question:HMSPollQuestion?):HashMap?{
+
+ question?.let {
+ val map = HashMap()
+ map["question_id"] = it.questionID
+ map["can_skip"] = it.canSkip
+ map["correct_answer"] = HMSPollQuestionAnswerExtension.toDictionary(it.correctAnswer)
+ map["duration"] = it.duration
+
+ val responses = ArrayList?>()
+ it.myResponses.forEach {
+ response -> responses.add(HMSPollAnswerExtension.toDictionary(response))
+ }
+ map["my_responses"] = responses
+
+ val options = ArrayList?>()
+ it.options?.forEach{
+ option -> options.add(HMSPollQuestionOptionExtension.toDictionary(option))
+ }
+ map["options"] = options
+
+ map["text"] = it.text
+ map["type"] = getStringFromPollQuestionType(it.type)
+ map["voted"] = it.voted
+ map["weight"] = it.weight
+ map["can_change_response"] = it.canChangeResponse
+ return map
+ }?:run {
+ return null
+ }
+ }
+
+ fun toHMSPollQuestion(pollQuestion: HashMap?):HMSPollQuestion?{
+
+ pollQuestion?.let {
+
+ val questionID = pollQuestion["question_id"]?.let {
+ it as Int
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("questionID should not be null")
+ return null
+ }
+
+ val canSkip = pollQuestion["can_skip"]?.let {
+ it as Boolean
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("canSkip should not be null")
+ return null
+ }
+ val correctAnswer = pollQuestion["correct_answer"]?.let {
+ HMSPollQuestionAnswerExtension.toPollQuestionAnswer(it as HashMap?)
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("correctAnswer should not be null")
+ return null
+ }
+ val duration = pollQuestion["duration"]?.let {
+ it as Long
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("duration should not be null")
+ return null
+ }
+
+ val myResponses = pollQuestion["my_responses"]?.let {
+ val responses = it as ArrayList<*>
+ val myResponses = ArrayList()
+ responses.forEach { response ->
+ val answer = HMSPollAnswerExtension.toHMSPollAnswer(response as HashMap?)
+ answer?.let { value ->
+ myResponses.add(value)
+ }
+ }
+ myResponses
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("myResponses should not be null")
+ return null
+ }
+
+ val options = pollQuestion["options"]?.let {
+ val options = it as ArrayList<*>
+ val pollOptions = ArrayList()
+ options.forEach { option ->
+ val pollOption = HMSPollQuestionOptionExtension.toHMSPollQuestionOption(option as HashMap?)
+ pollOption?.let { value ->
+ pollOptions.add(value)
+ }
+ }
+ pollOptions
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("options should not be null")
+ return null
+ }
+
+ val text = pollQuestion["text"]?.let {
+ it as String
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("text should not be null")
+ return null
+ }
+
+ val type = pollQuestion["type"]?.let {
+ getPollQuestionTypeFromString(it as String)
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("type should not be null")
+ return null
+ }
+
+ val weight = pollQuestion["weight"]?.let {
+ it as Int
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("weight should not be null")
+ return null
+ }
+
+ return HMSPollQuestion(questionID,type,text,canSkip,false,duration,weight,null,null,options,correctAnswer,false,myResponses)
+
+
+ }?:run {
+ return null
+ }
+ }
+
+ fun getStringFromPollQuestionType(pollQuestionType: HMSPollQuestionType):String?{
+ return when(pollQuestionType){
+ HMSPollQuestionType.multiChoice -> "multi_choice"
+ HMSPollQuestionType.shortAnswer -> "short_answer"
+ HMSPollQuestionType.longAnswer -> "long_answer"
+ HMSPollQuestionType.singleChoice -> "single_choice"
+ else -> null
+ }
+ }
+
+ fun getPollQuestionTypeFromString(pollQuestionType: String):HMSPollQuestionType?{
+ return when(pollQuestionType){
+ "multi_choice" -> HMSPollQuestionType.multiChoice
+ "short_answer" -> HMSPollQuestionType.shortAnswer
+ "long_answer" -> HMSPollQuestionType.longAnswer
+ "single_choice" -> HMSPollQuestionType.singleChoice
+ else -> null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollQuestionOptionExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollQuestionOptionExtension.kt
new file mode 100644
index 000000000..6ae6fcbd9
--- /dev/null
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollQuestionOptionExtension.kt
@@ -0,0 +1,67 @@
+package live.hms.hmssdk_flutter.poll_extension
+
+import live.hms.hmssdk_flutter.HMSErrorLogger
+import live.hms.video.polls.models.question.HMSPollQuestionOption
+
+class HMSPollQuestionOptionExtension {
+
+ companion object{
+
+ fun toDictionary(pollOptions: HMSPollQuestionOption?): HashMap?{
+
+ pollOptions?.let {
+ val map = HashMap()
+ map["index"] = it.index
+ map["text"] = it.text
+ map["vote_count"] = it.voteCount
+ map["weight"] = it.weight
+ return map
+ }?:run {
+ return null
+ }
+ }
+
+ fun toHMSPollQuestionOption(pollQuestionOptionMap: HashMap?):HMSPollQuestionOption?{
+
+ pollQuestionOptionMap?.let {
+
+ val index = pollQuestionOptionMap["index"]?.let {
+ it as Int
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("index should not be null")
+ return null
+ }
+
+ val text = pollQuestionOptionMap["text"]?.let {
+ it as String
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("text should not be null")
+ return null
+ }
+
+ val voteCount = pollQuestionOptionMap["vote_count"]?.let {
+ it as Long
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("voteCount should not be null")
+ return null
+ }
+
+ val weight = pollQuestionOptionMap["weight"]?.let {
+ it as Int
+ }?:run {
+ HMSErrorLogger.returnArgumentsError("weight should not be null")
+ return null
+ }
+
+ return HMSPollQuestionOption(index,text,weight,
+ case = false,
+ trim = false,
+ voteCount = voteCount
+ )
+
+ }?: run {
+ return null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollResultDisplayExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollResultDisplayExtension.kt
new file mode 100644
index 000000000..18aeef3b9
--- /dev/null
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollResultDisplayExtension.kt
@@ -0,0 +1,28 @@
+package live.hms.hmssdk_flutter.poll_extension
+
+import live.hms.video.polls.network.PollResultsDisplay
+
+class HMSPollResultDisplayExtension {
+
+ companion object{
+
+ fun toDictionary(pollResultsDisplay: PollResultsDisplay?):HashMap?{
+
+ pollResultsDisplay?.let {
+ val map = HashMap()
+
+ val questions = ArrayList?>()
+ it.questions.forEach{ question -> questions.add(HMSPollStatsQuestionsExtension.toDictionary(question))}
+ map["questions"] = questions
+
+ map["total_distinct_users"] = it.totalDistinctUsers
+ map["total_responses"] = it.totalResponses
+ map["voting_users"] = it.votingUsers
+
+ return map
+ }?:run {
+ return null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollStatsQuestionsExtension.kt b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollStatsQuestionsExtension.kt
new file mode 100644
index 000000000..104976624
--- /dev/null
+++ b/packages/hmssdk_flutter/android/src/main/kotlin/live/hms/hmssdk_flutter/poll_extension/HMSPollStatsQuestionsExtension.kt
@@ -0,0 +1,24 @@
+package live.hms.hmssdk_flutter.poll_extension
+
+import live.hms.video.polls.models.PollStatsQuestions
+
+class HMSPollStatsQuestionsExtension {
+
+ companion object{
+
+ fun toDictionary(pollStatsQuestions: PollStatsQuestions?):HashMap?{
+
+ pollStatsQuestions?.let {
+ val map = HashMap()
+ map["attempted_times"] = pollStatsQuestions.attemptedTimes
+ map["correct"] = pollStatsQuestions.correct
+ map["options"] = pollStatsQuestions.options
+ map["question_type"] = HMSPollQuestionExtension.getStringFromPollQuestionType(pollStatsQuestions.questionType)
+ map["skipped"] = pollStatsQuestions.skipped
+ return map
+ }?:run {
+ return null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/hmssdk_flutter/example/ExampleAppChangelog.txt b/packages/hmssdk_flutter/example/ExampleAppChangelog.txt
index 90051131e..751b44559 100644
--- a/packages/hmssdk_flutter/example/ExampleAppChangelog.txt
+++ b/packages/hmssdk_flutter/example/ExampleAppChangelog.txt
@@ -1,12 +1,33 @@
Board: https://100ms.atlassian.net/jira/software/projects/FLUT/boards/34/
-- Resolve issues reported by VAPT testing
-https://100ms.atlassian.net/browse/FLUT-218
+- Polls: Create Polls initial Screen
+https://100ms.atlassian.net/browse/FLUT-194
-- Resolve crash on Android 14 devices due to Broadcast Receiver in HMSVideoView
-https://100ms.atlassian.net/browse/FLUT-219
+- Polls: Ask a Question Screen
+https://100ms.atlassian.net/browse/FLUT-195
-Room Kit: 1.0.11
-Core SDK: 1.9.8
-Android SDK: 2.8.8
-iOS SDK: 1.4.2
+- Polls: Single Choice Question Screen
+https://100ms.atlassian.net/browse/FLUT-196
+
+- Polls: Multiple Choice Question Screen
+https://100ms.atlassian.net/browse/FLUT-197
+
+- Polls: Launch Poll Screen
+https://100ms.atlassian.net/browse/FLUT-202
+
+- Polls: Poll Panel with a live Poll Screen
+https://100ms.atlassian.net/browse/FLUT-204
+
+- Created poll with hide count, can see count as VNRT
+https://100ms.atlassian.net/browse/FLUT-233
+
+- host created poll but VNRT getting notification instantly even when timed metadata is ON
+https://100ms.atlassian.net/browse/FLUT-220
+
+- VNRT user not able to see poll if rejoin session
+https://100ms.atlassian.net/browse/FLUT-235
+
+Room Kit: 1.0.12
+Core SDK: 1.9.9
+Android SDK: 2.9.0
+iOS SDK: 1.5.0
diff --git a/packages/hmssdk_flutter/example/android/Gemfile.lock b/packages/hmssdk_flutter/example/android/Gemfile.lock
index 1d185f94f..99a461c55 100644
--- a/packages/hmssdk_flutter/example/android/Gemfile.lock
+++ b/packages/hmssdk_flutter/example/android/Gemfile.lock
@@ -13,8 +13,8 @@ GEM
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.3.0)
- aws-partitions (1.885.0)
- aws-sdk-core (3.191.0)
+ aws-partitions (1.889.0)
+ aws-sdk-core (3.191.1)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.8)
@@ -111,11 +111,11 @@ GEM
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
- fastlane-plugin-firebase_app_distribution (0.8.1)
+ fastlane-plugin-firebase_app_distribution (0.9.0)
google-apis-firebaseappdistribution_v1 (~> 0.3.0)
google-apis-firebaseappdistribution_v1alpha (~> 0.2.0)
gh_inspector (1.1.3)
- google-apis-androidpublisher_v3 (0.55.0)
+ google-apis-androidpublisher_v3 (0.57.0)
google-apis-core (>= 0.12.0, < 2.a)
google-apis-core (0.13.0)
addressable (~> 2.5, >= 2.5.1)
@@ -138,7 +138,7 @@ GEM
google-cloud-core (1.6.1)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
- google-cloud-env (2.1.0)
+ google-cloud-env (2.1.1)
faraday (>= 1.0, < 3.a)
google-cloud-errors (1.3.1)
google-cloud-storage (1.48.1)
@@ -150,7 +150,7 @@ GEM
google-cloud-core (~> 1.6)
googleauth (~> 1.9)
mini_mime (~> 1.0)
- googleauth (1.9.2)
+ googleauth (1.10.0)
faraday (>= 1.0, < 3.a)
google-cloud-env (~> 2.1)
jwt (>= 1.4, < 3.0)
@@ -168,9 +168,9 @@ GEM
jwt (2.7.1)
mini_magick (4.12.0)
mini_mime (1.1.5)
- minitest (5.21.2)
+ minitest (5.22.2)
multi_json (1.15.0)
- multipart-post (2.3.0)
+ multipart-post (2.4.0)
nanaimo (0.3.0)
naturally (2.2.1)
optparse (0.4.0)
@@ -222,7 +222,6 @@ GEM
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
- arm64-darwin-22
arm64-darwin-23
DEPENDENCIES
@@ -231,4 +230,4 @@ DEPENDENCIES
fastlane-plugin-firebase_app_distribution
BUNDLED WITH
- 2.4.22
+ 2.5.6
diff --git a/packages/hmssdk_flutter/example/android/app/build.gradle b/packages/hmssdk_flutter/example/android/app/build.gradle
index f11e098ed..3082e60b0 100644
--- a/packages/hmssdk_flutter/example/android/app/build.gradle
+++ b/packages/hmssdk_flutter/example/android/app/build.gradle
@@ -31,8 +31,8 @@ android {
applicationId "live.hms.flutter"
minSdkVersion 21
targetSdkVersion 34
- versionCode 430
- versionName "1.5.130"
+ versionCode 439
+ versionName "1.5.139"
}
signingConfigs {
diff --git a/packages/hmssdk_flutter/example/ios/Gemfile.lock b/packages/hmssdk_flutter/example/ios/Gemfile.lock
index d08516818..90a3dbf12 100644
--- a/packages/hmssdk_flutter/example/ios/Gemfile.lock
+++ b/packages/hmssdk_flutter/example/ios/Gemfile.lock
@@ -13,8 +13,8 @@ GEM
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.3.0)
- aws-partitions (1.885.0)
- aws-sdk-core (3.191.0)
+ aws-partitions (1.889.0)
+ aws-sdk-core (3.191.1)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.8)
@@ -111,12 +111,12 @@ GEM
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
- fastlane-plugin-firebase_app_distribution (0.8.1)
+ fastlane-plugin-firebase_app_distribution (0.9.0)
google-apis-firebaseappdistribution_v1 (~> 0.3.0)
google-apis-firebaseappdistribution_v1alpha (~> 0.2.0)
fastlane-plugin-versioning (0.5.2)
gh_inspector (1.1.3)
- google-apis-androidpublisher_v3 (0.55.0)
+ google-apis-androidpublisher_v3 (0.57.0)
google-apis-core (>= 0.12.0, < 2.a)
google-apis-core (0.13.0)
addressable (~> 2.5, >= 2.5.1)
@@ -139,7 +139,7 @@ GEM
google-cloud-core (1.6.1)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
- google-cloud-env (2.1.0)
+ google-cloud-env (2.1.1)
faraday (>= 1.0, < 3.a)
google-cloud-errors (1.3.1)
google-cloud-storage (1.48.1)
@@ -151,7 +151,7 @@ GEM
google-cloud-core (~> 1.6)
googleauth (~> 1.9)
mini_mime (~> 1.0)
- googleauth (1.9.2)
+ googleauth (1.10.0)
faraday (>= 1.0, < 3.a)
google-cloud-env (~> 2.1)
jwt (>= 1.4, < 3.0)
@@ -169,9 +169,9 @@ GEM
jwt (2.7.1)
mini_magick (4.12.0)
mini_mime (1.1.5)
- minitest (5.21.2)
+ minitest (5.22.2)
multi_json (1.15.0)
- multipart-post (2.3.0)
+ multipart-post (2.4.0)
nanaimo (0.3.0)
naturally (2.2.1)
optparse (0.4.0)
@@ -223,7 +223,6 @@ GEM
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
- arm64-darwin-22
arm64-darwin-23
DEPENDENCIES
diff --git a/packages/hmssdk_flutter/example/ios/Podfile b/packages/hmssdk_flutter/example/ios/Podfile
index b2636e014..81e6580ff 100644
--- a/packages/hmssdk_flutter/example/ios/Podfile
+++ b/packages/hmssdk_flutter/example/ios/Podfile
@@ -46,7 +46,7 @@ post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
- config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
+ config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "x86_64"
config.build_settings['ENABLE_BITCODE'] = 'NO'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
diff --git a/packages/hmssdk_flutter/example/ios/Podfile.lock b/packages/hmssdk_flutter/example/ios/Podfile.lock
index 85f7c2846..875258308 100644
--- a/packages/hmssdk_flutter/example/ios/Podfile.lock
+++ b/packages/hmssdk_flutter/example/ios/Podfile.lock
@@ -97,14 +97,14 @@ PODS:
- HMSBroadcastExtensionSDK (0.0.9)
- HMSHLSPlayerSDK (0.0.2):
- HMSAnalyticsSDK (= 0.0.2)
- - HMSSDK (1.4.2):
+ - HMSSDK (1.5.0):
- HMSAnalyticsSDK (= 0.0.2)
- HMSWebRTC (= 1.0.5116)
- hmssdk_flutter (1.9.6):
- Flutter
- HMSBroadcastExtensionSDK (= 0.0.9)
- HMSHLSPlayerSDK (= 0.0.2)
- - HMSSDK (= 1.4.2)
+ - HMSSDK (= 1.5.0)
- HMSWebRTC (1.0.5116)
- MTBBarcodeScanner (5.0.11)
- nanopb (2.30909.1):
@@ -228,29 +228,29 @@ SPEC CHECKSUMS:
FirebaseRemoteConfig: b873a427a48159082361343a85649eed3f5377ea
FirebaseSessions: 2f348975f6d1c139231c180e12194161da2e0cd6
FirebaseSharedSwift: 2fbf73618288b7a36b2014b957745dcdd781389e
- Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
+ Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_foreground_task: 21ef182ab0a29a3005cc72cd70e5f45cb7f7f817
GoogleDataTransport: 57c22343ab29bc686febbf7cbb13bad167c2d8fe
GoogleUtilities: 0759d1a57ebb953965c2dfe0ba4c82e95ccc2e34
HMSAnalyticsSDK: 4d2a88a729b1eb42f3d25f217c28937ec318a5b7
HMSBroadcastExtensionSDK: d80fe325f6c928bd8e5176290b5a4b7ae15d6fbb
HMSHLSPlayerSDK: 6a54ad4d12f3dc2270d1ecd24019d71282a4f6a3
- HMSSDK: 7be2385e9e3ddbd5c03a228934622d679d033e15
- hmssdk_flutter: 1cd1e55001bdfd1b015eebced25ba3a7fb64649b
+ HMSSDK: 0d1901d64faf2661d1183c1ba2881e2531a5eeba
+ hmssdk_flutter: 9f3b16d9bfc1e9a2ccd63f5d9b6a6d51669ed5ac
HMSWebRTC: ae54e9dd91b869051b283b43b14f57d43b7bf8e1
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
- path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
+ path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4
PromisesSwift: 28dca69a9c40779916ac2d6985a0192a5cb4a265
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
- shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
+ shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
- url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
+ url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
-PODFILE CHECKSUM: 919064996fff867cd85dbf9e7730ff45bac23884
+PODFILE CHECKSUM: 9fb9f6e431a2c6c79942252e94b241ac7972ac90
-COCOAPODS: 1.15.0
+COCOAPODS: 1.14.3
diff --git a/packages/hmssdk_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/hmssdk_flutter/example/ios/Runner.xcodeproj/project.pbxproj
index 3f12e3fba..dd0e12912 100644
--- a/packages/hmssdk_flutter/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/hmssdk_flutter/example/ios/Runner.xcodeproj/project.pbxproj
@@ -672,6 +672,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 5N85PP82A9;
ENABLE_BITCODE = NO;
+ "EXCLUDED_ARCHS[sdk=*]" = x86_64;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
@@ -745,7 +746,8 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 5N85PP82A9;
ENABLE_BITCODE = NO;
- "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64";
+ EXCLUDED_ARCHS = x86_64;
+ "EXCLUDED_ARCHS[sdk=*]" = x86_64;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = FlutterBroadcastUploadExtension/Info.plist;
@@ -789,6 +791,7 @@
CURRENT_PROJECT_VERSION = 185;
DEVELOPMENT_TEAM = 5N85PP82A9;
ENABLE_BITCODE = NO;
+ EXCLUDED_ARCHS = x86_64;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = FlutterBroadcastUploadExtension/Info.plist;
@@ -829,6 +832,7 @@
CURRENT_PROJECT_VERSION = 185;
DEVELOPMENT_TEAM = 5N85PP82A9;
ENABLE_BITCODE = NO;
+ EXCLUDED_ARCHS = x86_64;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = FlutterBroadcastUploadExtension/Info.plist;
diff --git a/packages/hmssdk_flutter/example/ios/Runner/Info.plist b/packages/hmssdk_flutter/example/ios/Runner/Info.plist
index df8e2a6ba..d7533962e 100644
--- a/packages/hmssdk_flutter/example/ios/Runner/Info.plist
+++ b/packages/hmssdk_flutter/example/ios/Runner/Info.plist
@@ -21,7 +21,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.5.130
+ 1.5.139
CFBundleSignature
????
CFBundleURLTypes
@@ -48,7 +48,7 @@
CFBundleVersion
- 430
+ 439
ITSAppUsesNonExemptEncryption
LSApplicationCategoryType
@@ -88,6 +88,8 @@
Main
UIRequiresFullScreen
+ UIStatusBarHidden
+
UIStatusBarStyle
UIStatusBarStyleDarkContent
UISupportedInterfaceOrientations
diff --git a/packages/hmssdk_flutter/example/lib/main.dart b/packages/hmssdk_flutter/example/lib/main.dart
index cf391ed08..f4e7dddac 100644
--- a/packages/hmssdk_flutter/example/lib/main.dart
+++ b/packages/hmssdk_flutter/example/lib/main.dart
@@ -1,5 +1,6 @@
//Dart imports
import 'dart:async';
+import 'dart:ui';
//Package imports
import 'package:firebase_core/firebase_core.dart';
diff --git a/packages/hmssdk_flutter/example/pubspec.lock b/packages/hmssdk_flutter/example/pubspec.lock
index 409983a24..3f81f85e8 100644
--- a/packages/hmssdk_flutter/example/pubspec.lock
+++ b/packages/hmssdk_flutter/example/pubspec.lock
@@ -13,10 +13,10 @@ packages:
dependency: transitive
description:
name: archive
- sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
+ sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
url: "https://pub.dev"
source: hosted
- version: "3.4.9"
+ version: "3.4.10"
args:
dependency: transitive
description:
@@ -286,15 +286,15 @@ packages:
path: "../../hms_room_kit"
relative: true
source: path
- version: "1.0.11"
+ version: "1.0.12"
hmssdk_flutter:
dependency: transitive
description:
name: hmssdk_flutter
- sha256: ff1697824b42d31cb093fd4319c8fa3ba6872b7877707630062ed3cd9cf40813
+ sha256: bad4ff87c677970f0c9acfcd4e84b60089461e1c1eef23e3a3ae6ab191484b95
url: "https://pub.dev"
source: hosted
- version: "1.9.8"
+ version: "1.9.9"
http:
dependency: transitive
description:
@@ -419,10 +419,10 @@ packages:
dependency: transitive
description:
name: path_provider
- sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
+ sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
url: "https://pub.dev"
source: hosted
- version: "2.1.1"
+ version: "2.1.2"
path_provider_android:
dependency: transitive
description:
@@ -435,10 +435,10 @@ packages:
dependency: transitive
description:
name: path_provider_foundation
- sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
+ sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
url: "https://pub.dev"
source: hosted
- version: "2.3.1"
+ version: "2.3.2"
path_provider_linux:
dependency: transitive
description:
@@ -451,10 +451,10 @@ packages:
dependency: transitive
description:
name: path_provider_platform_interface
- sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
+ sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
- version: "2.1.1"
+ version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
@@ -515,26 +515,26 @@ packages:
dependency: transitive
description:
name: platform
- sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
+ sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
source: hosted
- version: "3.1.3"
+ version: "3.1.4"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
- sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
- version: "2.1.7"
+ version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
- sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
+ sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
url: "https://pub.dev"
source: hosted
- version: "3.7.3"
+ version: "3.7.4"
provider:
dependency: transitive
description:
@@ -587,10 +587,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_foundation
- sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
+ sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
url: "https://pub.dev"
source: hosted
- version: "2.3.4"
+ version: "2.3.5"
shared_preferences_linux:
dependency: transitive
description:
@@ -603,10 +603,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_platform_interface
- sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
+ sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
url: "https://pub.dev"
source: hosted
- version: "2.3.1"
+ version: "2.3.2"
shared_preferences_web:
dependency: transitive
description:
@@ -728,26 +728,26 @@ packages:
dependency: "direct main"
description:
name: url_launcher
- sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86
+ sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c
url: "https://pub.dev"
source: hosted
- version: "6.2.2"
+ version: "6.2.4"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
- sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def"
+ sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f"
url: "https://pub.dev"
source: hosted
- version: "6.2.0"
+ version: "6.2.2"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
- sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3
+ sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03"
url: "https://pub.dev"
source: hosted
- version: "6.2.1"
+ version: "6.2.4"
url_launcher_linux:
dependency: transitive
description:
@@ -768,10 +768,10 @@ packages:
dependency: transitive
description:
name: url_launcher_platform_interface
- sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50"
+ sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f
url: "https://pub.dev"
source: hosted
- version: "2.2.0"
+ version: "2.3.1"
url_launcher_web:
dependency: transitive
description:
@@ -800,26 +800,26 @@ packages:
dependency: transitive
description:
name: vector_graphics
- sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43"
+ sha256: "18f6690295af52d081f6808f2f7c69f0eed6d7e23a71539d75f4aeb8f0062172"
url: "https://pub.dev"
source: hosted
- version: "1.1.9+1"
+ version: "1.1.9+2"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
- sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7"
+ sha256: "531d20465c10dfac7f5cd90b60bbe4dd9921f1ec4ca54c83ebb176dbacb7bb2d"
url: "https://pub.dev"
source: hosted
- version: "1.1.9+1"
+ version: "1.1.9+2"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
- sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26
+ sha256: "03012b0a33775c5530576b70240308080e1d5050f0faf000118c20e6463bc0ad"
url: "https://pub.dev"
source: hosted
- version: "1.1.9+1"
+ version: "1.1.9+2"
vector_math:
dependency: transitive
description:
@@ -856,10 +856,10 @@ packages:
dependency: transitive
description:
name: xdg_directories
- sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
+ sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev"
source: hosted
- version: "1.0.3"
+ version: "1.0.4"
xml:
dependency: transitive
description:
diff --git a/packages/hmssdk_flutter/example/pubspec.yaml b/packages/hmssdk_flutter/example/pubspec.yaml
index 91957822f..d9065c665 100644
--- a/packages/hmssdk_flutter/example/pubspec.yaml
+++ b/packages/hmssdk_flutter/example/pubspec.yaml
@@ -4,7 +4,7 @@ description: Demonstrates how to use the hmssdk_flutter plugin.
# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: "none" # Remove this line if you wish to publish to pub.dev
-version: 1.9.8
+version: 1.9.9
environment:
sdk: ">=2.16.0 <4.0.0"
diff --git a/packages/hmssdk_flutter/ios/Classes/Actions/HMSPollAction.swift b/packages/hmssdk_flutter/ios/Classes/Actions/HMSPollAction.swift
new file mode 100644
index 000000000..26793b472
--- /dev/null
+++ b/packages/hmssdk_flutter/ios/Classes/Actions/HMSPollAction.swift
@@ -0,0 +1,194 @@
+//
+// HMSPollAction.swift
+// hmssdk_flutter
+//
+// Created by Pushpam on 19/01/24.
+//
+
+import Foundation
+import HMSSDK
+
+class HMSPollAction{
+
+ static func pollActions(_ call: FlutterMethodCall, _ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?, _ polls: [HMSPoll]?){
+ switch call.method{
+ case "quick_start_poll":
+ quickStartPoll(call, result, hmsSDK)
+ break
+ case "add_single_choice_poll_response":
+ addSingleChoicePollResponse(call, result, hmsSDK, polls)
+ break
+ case "add_multi_choice_poll_response":
+ addMultiChoicePollResponse(call, result, hmsSDK, polls)
+ break
+ case "stop_poll":
+ stopPoll(call, result, hmsSDK, polls)
+ default:
+ result(FlutterMethodNotImplemented)
+ }
+ }
+
+ static private func quickStartPoll(_ call: FlutterMethodCall,_ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?){
+
+ let arguments = call.arguments as? [AnyHashable: Any]
+
+ guard let pollBuilderMap = arguments?["poll_builder"] as? [String:Any?] else{
+ result(HMSErrorExtension.getError("No pollBuilder found in \(#function)"))
+ return
+ }
+
+ if let hmsSDK{
+ guard let pollBuilder = HMSPollBuilderExtension.toHMSPollBuilder(pollBuilderMap, hmsSDK)
+ else{
+ HMSErrorLogger.returnArgumentsError("pollBuilder parsing failed")
+ return
+ }
+
+ hmsSDK.interactivityCenter.quickStartPoll(with: (pollBuilder), completion: {_ , error in
+ if let error = error {
+ result(HMSErrorExtension.toDictionary(error))
+ } else {
+ result(nil)
+ }})
+ } else{
+ result(HMSErrorExtension.getError("hmsSDK is nil in \(#function)"))
+ return
+ }
+
+ }
+
+
+ static private func addSingleChoicePollResponse(_ call: FlutterMethodCall,_ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?,_ currentPolls: [HMSPoll]?){
+
+ let arguments = call.arguments as? [AnyHashable: Any]
+
+ guard let pollId = arguments?["poll_id"] as? String,
+ let index = arguments?["question_index"] as? Int,
+ let answer = arguments?["answer"] as? [String:Any?]
+ else {
+ HMSErrorLogger.returnArgumentsError("Invalid arguments")
+ return
+ }
+
+ if let optionIndex = answer["index"] as? Int {
+
+ if let poll = currentPolls?.first(where: {$0.pollID == pollId}){
+
+ if let question = poll.questions?[index]{
+
+ if let optionSelected = question.options?[optionIndex - 1]{
+ let response = HMSPollResponseBuilder(poll: poll)
+ response.addResponse(for: question, options: [optionSelected])
+ hmsSDK?.interactivityCenter.add(response: response){ pollResult, error in
+
+ if let error = error{
+ result(HMSResultExtension.toDictionary(false, HMSErrorExtension.toDictionary(error)))
+ }else{
+ var pollResults = [[String: Any?]]()
+
+ pollResult?.forEach{
+ pollResults.append(HMSPollAnswerResponseExtension.toDictionary(pollAnswerResponse: $0))
+ }
+ var map = [String:Any?]()
+ map["result"] = pollResults
+ result(HMSResultExtension.toDictionary(true, map))
+ }
+ }
+ }else{
+ HMSErrorLogger.returnArgumentsError("No option found at given index")
+ }
+
+ }else{
+ HMSErrorLogger.returnArgumentsError("No question found at given index")
+ }
+
+ }else{
+ HMSErrorLogger.returnArgumentsError("No poll with given pollId found")
+ return
+ }
+ } else {
+ HMSErrorLogger.returnArgumentsError("Invalid option index")
+ return
+ }
+ }
+
+
+ static private func addMultiChoicePollResponse(_ call: FlutterMethodCall,_ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?,_ currentPolls: [HMSPoll]?){
+
+ let arguments = call.arguments as? [AnyHashable: Any]
+
+ guard let pollId = arguments?["poll_id"] as? String,
+ let index = arguments?["question_index"] as? Int,
+ let answer = arguments?["answer"] as? [[String:Any?]]
+ else {
+ HMSErrorLogger.returnArgumentsError("Invalid arguments")
+ return
+ }
+ if let poll = currentPolls?.first(where: {$0.pollID == pollId}){
+
+ if let question = poll.questions?[index]{
+ var selectedOptions = [HMSPollQuestionOption]()
+ answer.forEach{
+ if let optionIndex = $0["index"] as? Int{
+ if let option = question.options?[optionIndex - 1] as? HMSPollQuestionOption{
+ selectedOptions.append(option)
+ }else{
+ HMSErrorLogger.returnArgumentsError("Invalid option index")
+ return
+ }
+ }else{
+ HMSErrorLogger.returnArgumentsError("Invalid index")
+ return
+ }
+ }
+ let response = HMSPollResponseBuilder(poll: poll)
+ response.addResponse(for: question, options: selectedOptions)
+ hmsSDK?.interactivityCenter.add(response: response){ pollResult, error in
+
+ if let error = error{
+ result(HMSResultExtension.toDictionary(false, HMSErrorExtension.toDictionary(error)))
+ }else{
+ var pollResults = [[String: Any?]]()
+
+ pollResult?.forEach{
+ pollResults.append(HMSPollAnswerResponseExtension.toDictionary(pollAnswerResponse: $0))
+ }
+ var map = [String:Any?]()
+ map["result"] = pollResults
+ result(HMSResultExtension.toDictionary(true, map))
+ }
+ }
+ }else{
+ HMSErrorLogger.returnArgumentsError("No question found at given index")
+ return
+ }
+ }else{
+ HMSErrorLogger.returnArgumentsError("No poll with given pollId found")
+ return
+ }
+ }
+
+ static private func stopPoll(_ call: FlutterMethodCall,_ result: @escaping FlutterResult, _ hmsSDK: HMSSDK?,_ currentPolls: [HMSPoll]?){
+
+ let arguments = call.arguments as? [AnyHashable: Any]
+
+ guard let pollId = arguments?["poll_id"] as? String
+ else{
+ HMSErrorLogger.returnArgumentsError("pollId can't be null")
+ return
+ }
+
+ if let poll = currentPolls?.first(where: {
+ $0.pollID == pollId
+ }){
+ hmsSDK?.interactivityCenter.stop(poll: poll){
+ _, error in
+ if let error = error{
+ result(HMSErrorExtension.toDictionary(error))
+ }else{
+ result(nil)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/hmssdk_flutter/ios/Classes/Models/HMSPermissionExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/HMSPermissionExtension.swift
index b643c4e65..bd4a755db 100644
--- a/packages/hmssdk_flutter/ios/Classes/Models/HMSPermissionExtension.swift
+++ b/packages/hmssdk_flutter/ios/Classes/Models/HMSPermissionExtension.swift
@@ -19,8 +19,10 @@ class HMSPermissionExtension {
"mute": permission.mute ?? false,
"remove_others": permission.removeOthers ?? false,
"rtmp_streaming": permission.rtmpStreaming ?? false,
- "un_mute": permission.unmute ?? false
-
+ "un_mute": permission.unmute ?? false,
+ "poll_read": permission.pollRead ?? false,
+ "poll_write": permission.pollWrite ?? false
+
]
}
}
diff --git a/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollAnswerExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollAnswerExtension.swift
new file mode 100644
index 000000000..d326e3d45
--- /dev/null
+++ b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollAnswerExtension.swift
@@ -0,0 +1,28 @@
+//
+// HMSPollAnswerExtension.swift
+// hmssdk_flutter
+//
+// Created by Pushpam on 18/01/24.
+//
+
+import Foundation
+import HMSSDK
+
+class HMSPollAnswerExtension{
+
+ static func toDictionary(answer: HMSPollQuestionResponse) -> [String:Any]{
+
+ var map = [String:Any]()
+
+ map["answer"] = answer.text
+ map["duration"] = answer.duration
+ map["question_id"] = answer.questionID
+ map["question_type"] = HMSPollQuestionExtension.getPollQuestionType(pollQuestionType: answer.type)
+ map["selected_option"] = answer.option
+ map["selected_options"] = answer.options
+ map["skipped"] = answer.skipped
+ map["update"] = answer.update
+
+ return map
+ }
+}
diff --git a/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollAnswerResponseExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollAnswerResponseExtension.swift
new file mode 100644
index 000000000..7c90a6fb8
--- /dev/null
+++ b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollAnswerResponseExtension.swift
@@ -0,0 +1,28 @@
+//
+// HMSPollAnswerResponseExtension.swift
+// hmssdk_flutter
+//
+// Created by Pushpam on 31/01/24.
+//
+
+import Foundation
+import HMSSDK
+
+class HMSPollAnswerResponseExtension{
+
+ static func toDictionary(pollAnswerResponse: HMSPollQuestionResponseResult) -> [String: Any?]{
+
+ var map = [String:Any?]()
+
+ map["question_index"] = pollAnswerResponse.question
+ if let error = pollAnswerResponse.error{
+ map["error"] = HMSErrorExtension.toDictionary(error)
+ }
+ map["correct"] = pollAnswerResponse.correct
+
+ return map
+
+ }
+
+
+}
diff --git a/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollBuilderExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollBuilderExtension.swift
new file mode 100644
index 000000000..ec455e3f1
--- /dev/null
+++ b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollBuilderExtension.swift
@@ -0,0 +1,178 @@
+//
+// HMSPollBuilderExtension.swift
+// hmssdk_flutter
+//
+// Created by Pushpam on 23/01/24.
+//
+
+import Foundation
+import HMSSDK
+
+class HMSPollBuilderExtension{
+
+ static func toHMSPollBuilder(_ pollBuilderMap:[String:Any?]?, _ hmssdk:HMSSDK) -> HMSPollBuilder?{
+
+ guard let pollBuilderMap = pollBuilderMap else{
+ return nil
+ }
+
+ let pollBuilder = HMSPollBuilder()
+
+ if let anonymous = pollBuilderMap["anonymous"] as? Bool{
+ pollBuilder.withAnonymous(anonymous)
+ }
+
+ if let duration = pollBuilderMap["duration"] as? Int {
+ pollBuilder.withDuration(duration)
+ }
+
+ if let mode = pollBuilderMap["mode"] as? String {
+ pollBuilder.withUserTrackingMode(getPollUserTrackingModeFromString(mode))
+ }
+
+ if let pollCategory = pollBuilderMap["poll_category"] as? String {
+ pollBuilder.withCategory(getPollCategoryFromString(pollCategory))
+ }
+
+ if let pollId = pollBuilderMap["poll_id"] as? String {
+ pollBuilder.withPollID(pollId)
+ }
+
+ let availableRoles = hmssdk.roles
+ if let rolesThatCanViewResponses = pollBuilderMap["roles_that_can_view_responses"] as? [String] {
+ let roles = rolesThatCanViewResponses.compactMap { roleName in
+ availableRoles.first { $0.name == roleName }
+ }
+ pollBuilder.withRolesThatCanViewResponses(roles)
+ }
+
+ if let rolesThatCanVote = pollBuilderMap["roles_that_can_vote"] as? [String] {
+ let roles = rolesThatCanVote.compactMap { roleName in
+ availableRoles.first { $0.name == roleName }
+ }
+ pollBuilder.withRolesThatCanVote(roles)
+ }
+
+ if let title = pollBuilderMap["title"] as? String {
+ pollBuilder.withTitle(title)
+ }
+
+ if let questions = pollBuilderMap["questions"] as? [[String: Any?]]{
+
+ questions.forEach{
+ if let questionBuilder = getPollQuestionBuilder($0){
+ pollBuilder.addQuestion(with: questionBuilder)
+ }
+ }
+
+ }
+ return pollBuilder
+ }
+
+ private static func getPollCategoryFromString(_ pollCategory:String) -> HMSPollCategory{
+
+ switch pollCategory{
+ case "poll":
+ return .poll
+ case "quiz":
+ return .quiz
+ default:
+ return .poll
+ }
+ }
+
+ private static func getPollUserTrackingModeFromString(_ pollUserTrackingMode: String) -> HMSPollUserTrackingMode {
+ switch pollUserTrackingMode {
+ case "user_id":
+ return .customerUserID
+ case "peer_id":
+ return .peerID
+ case "username":
+ return .userName
+ default:
+ return .customerUserID
+ }
+ }
+
+ private static func getPollQuestionTypeFromString(_ pollQuestionType: String) -> HMSPollQuestionType {
+ switch pollQuestionType {
+ case "multi_choice":
+ return .multipleChoice
+ case "short_answer":
+ return .shortAnswer
+ case "long_answer":
+ return .longAnswer
+ case "single_choice":
+ return .singleChoice
+ default:
+ return .singleChoice
+ }
+ }
+
+ private static func getPollQuestionBuilder(_ pollQuestion: [String: Any?]?) -> HMSPollQuestionBuilder? {
+ guard let pollQuestion = pollQuestion else {
+ return nil
+ }
+
+ let pollQuestionBuilder = HMSPollQuestionBuilder()
+
+ if let typeString = pollQuestion["type"] as? String{
+ let type = getPollQuestionTypeFromString(typeString)
+ pollQuestionBuilder.withType(type)
+ } else {
+ HMSErrorLogger.returnArgumentsError("type should not be null")
+ return nil
+ }
+
+
+ if let canSkip = pollQuestion["can_skip"] as? Bool {
+ pollQuestionBuilder.withCanBeSkipped(canSkip)
+ }
+
+ if let text = pollQuestion["text"] as? String {
+ pollQuestionBuilder.withTitle(text)
+ }
+
+ if let duration = pollQuestion["duration"] as? Int {
+ pollQuestionBuilder.withDuration(duration)
+ }
+
+ if let weight = pollQuestion["weight"] as? Int {
+ pollQuestionBuilder.withWeight(weight: weight)
+ }
+
+ if let answerHidden = pollQuestion["answer_hidden"] as? Bool {
+ pollQuestionBuilder.withAnswerHidden(answerHidden: answerHidden)
+ }
+
+ if let maxLength = pollQuestion["max_length"] as? Int {
+ pollQuestionBuilder.withMaxLength(maxLength: maxLength)
+ }
+
+ if let minLength = pollQuestion["min_length"] as? Int {
+ pollQuestionBuilder.withMinLength(minLength: minLength)
+ }
+
+ if let pollOptions = pollQuestion["poll_options"] as? [String] {
+ pollOptions.forEach { option in
+ pollQuestionBuilder.addOption(with: option)
+ }
+ }
+
+ if let options = pollQuestion["options"] as? [[String: Bool]] {
+ options.forEach { option in
+ if let key = option.keys.first, let value = option.values.first {
+ pollQuestionBuilder.addQuizOption(with: key, isCorrect: value)
+ }
+ }
+ }
+
+ if let canChangeResponse = pollQuestion["can_change_response"] as? Bool{
+ pollQuestionBuilder.withCanChangeResponse(canChangeResponse: canChangeResponse)
+ }
+
+ return pollQuestionBuilder
+ }
+
+
+}
diff --git a/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollExtension.swift
new file mode 100644
index 000000000..fd52f54c7
--- /dev/null
+++ b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollExtension.swift
@@ -0,0 +1,114 @@
+//
+// File.swift
+// hmssdk_flutter
+//
+// Created by Pushpam on 18/01/24.
+//
+
+import Foundation
+import HMSSDK
+
+class HMSPollExtension {
+
+ static func toDictionary(poll: HMSPoll) -> [String: Any?] {
+ var map = [String: Any?]()
+
+ map["anonymous"] = poll.anonymous
+ map["category"] = getPollCategory(pollCategory: poll.category)
+
+ if let createdBy = poll.createdBy{
+ map["created_by"] = HMSPeerExtension.toDictionary(createdBy)
+ }
+
+ map["duration"] = poll.duration
+
+ if let mode = poll.mode{
+ map["mode"] = getPollUserTrackingMode(mode: mode)
+ }
+
+ map["poll_id"] = poll.pollID
+ map["question_count"] = poll.questionCount
+
+ let questions = poll.questions?.map { HMSPollQuestionExtension.toDictionary(question: $0) }
+ map["questions"] = questions
+
+ if let result = poll.result{
+ map["result"] = HMSPollResultExtension.toDictionary(pollResult: result)
+ }
+
+ let rolesThatCanViewResponses = poll.rolesThatCanViewResponses.map { HMSRoleExtension.toDictionary($0) }
+ map["roles_that_can_view_responses"] = rolesThatCanViewResponses
+
+ let rolesThatCanVote = poll.rolesThatCanVote.map { HMSRoleExtension.toDictionary($0) }
+ map["roles_that_can_vote"] = rolesThatCanVote
+
+ if let startedAt = poll.startedAt{
+ map["started_at"] = Int(startedAt.timeIntervalSince1970 * 1000)
+ }
+
+ if let startedBy = poll.startedBy{
+ map["started_by"] = HMSPeerExtension.toDictionary(startedBy)
+ }
+
+ map["state"] = getPollState(state: poll.state)
+
+ if let stoppedAt = poll.stoppedAt{
+ map["stopped_at"] = Int(stoppedAt.timeIntervalSince1970 * 1000)
+ }
+
+ map["title"] = poll.title
+
+ return map
+ }
+
+ private static func getPollCategory(pollCategory: HMSPollCategory) -> String? {
+ switch pollCategory {
+ case .poll:
+ return "poll"
+ case .quiz:
+ return "quiz"
+ default:
+ return nil
+ }
+ }
+
+ private static func getPollUserTrackingMode(mode: HMSPollUserTrackingMode) -> String? {
+ switch mode {
+ case .customerUserID:
+ return "user_id"
+ case .peerID:
+ return "peer_id"
+ case .userName:
+ return "username"
+ default:
+ return nil
+ }
+ }
+
+ private static func getPollState(state: HMSPollState) -> String? {
+ switch state {
+ case .created:
+ return "created"
+ case .started:
+ return "started"
+ case .stopped:
+ return "stopped"
+ default:
+ return nil
+ }
+ }
+
+ static func getPollUpdateType(updateType: HMSPollUpdateType) -> String? {
+ switch updateType {
+ case .started:
+ return "started"
+ case .stopped:
+ return "stopped"
+ case .resultsUpdated:
+ return "results_updated"
+ default:
+ return nil
+ }
+ }
+}
+
diff --git a/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionAnswerExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionAnswerExtension.swift
new file mode 100644
index 000000000..870c71e0a
--- /dev/null
+++ b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionAnswerExtension.swift
@@ -0,0 +1,23 @@
+//
+// HMSPollQuestionAnswerExtension.swift
+// hmssdk_flutter
+//
+// Created by Pushpam on 18/01/24.
+//
+
+import Foundation
+import HMSSDK
+
+class HMSPollQuestionAnswerExtension{
+
+ static func toDictionary(answer: HMSPollQuestionAnswer) -> [String:Any]{
+
+ var map = [String:Any]()
+
+ map["hidden"] = answer.hidden
+ map["option"] = answer.option
+ map["options"] = answer.options
+
+ return map
+ }
+}
diff --git a/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionExtension.swift
new file mode 100644
index 000000000..79129307c
--- /dev/null
+++ b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionExtension.swift
@@ -0,0 +1,54 @@
+//
+// HMSPollQuestionExtension.swift
+// hmssdk_flutter
+//
+// Created by Pushpam on 18/01/24.
+//
+
+import Foundation
+import HMSSDK
+
+class HMSPollQuestionExtension{
+
+ static func toDictionary(question: HMSPollQuestion) -> [String:Any?]{
+ var map = [String: Any?]()
+
+ map["question_id"] = question.index
+ map["can_skip"] = question.skippable
+
+ if let answer = question.answer{
+ map["correct_answer"] = HMSPollQuestionAnswerExtension.toDictionary(answer: answer)
+ }
+
+ map["duration"] = question.duration
+
+ let myResponses = question.myResponses.map{ (HMSPollAnswerExtension.toDictionary(answer:$0)) }
+ map["my_responses"] = myResponses
+
+ let options = question.options?.map{ (HMSPollQuestionOptionExtension.toDictionary(pollOptions: $0)) }
+ map["options"] = options
+
+ map["text"] = question.text
+ map["type"] = getPollQuestionType(pollQuestionType: question.type)
+ map["voted"] = question.voted
+ map["weight"] = question.weight
+ map["can_change_response"] = question.once
+ return map
+
+ }
+
+ static func getPollQuestionType(pollQuestionType: HMSPollQuestionType) -> String?{
+ switch pollQuestionType {
+ case .longAnswer:
+ return "long_answer"
+ case .multipleChoice:
+ return "multi_choice"
+ case .shortAnswer:
+ return "short_answer"
+ case .singleChoice:
+ return "single_choice"
+ default:
+ return nil
+ }
+ }
+}
diff --git a/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionOptionExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionOptionExtension.swift
new file mode 100644
index 000000000..5bead12e2
--- /dev/null
+++ b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionOptionExtension.swift
@@ -0,0 +1,24 @@
+//
+// HMSPollQuestionOptionExtension.swift
+// hmssdk_flutter
+//
+// Created by Pushpam on 18/01/24.
+//
+
+import Foundation
+import HMSSDK
+
+class HMSPollQuestionOptionExtension{
+
+ static func toDictionary(pollOptions: HMSPollQuestionOption) -> [String: Any] {
+
+ var map = [String: Any]()
+
+ map["index"] = pollOptions.index
+ map["text"] = pollOptions.text
+ map["vote_count"] = pollOptions.voteCount
+ map["weight"] = pollOptions.weight
+
+ return map
+ }
+}
diff --git a/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionResultExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionResultExtension.swift
new file mode 100644
index 000000000..2889979d3
--- /dev/null
+++ b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollQuestionResultExtension.swift
@@ -0,0 +1,25 @@
+//
+// HMSPollQuestionResultExtension.swift
+// hmssdk_flutter
+//
+// Created by Pushpam on 18/01/24.
+//
+
+import Foundation
+import HMSSDK
+
+class HMSPollQuestionResultExtension{
+
+ static func toDictionary(pollQuestionResult: HMSPollQuestionResult)-> [String:Any]{
+
+ var map = [String:Any]()
+
+ map["attempted_times"] = pollQuestionResult.totalVotes
+ map["correct"] = pollQuestionResult.correctVotes
+ map["options"] = pollQuestionResult.optionVoteCounts
+ map["question_type"] = pollQuestionResult.type
+ map["skipped"] = pollQuestionResult.skippedVotes
+
+ return map
+ }
+}
diff --git a/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollResultExtension.swift b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollResultExtension.swift
new file mode 100644
index 000000000..1b0d78136
--- /dev/null
+++ b/packages/hmssdk_flutter/ios/Classes/Models/polls/HMSPollResultExtension.swift
@@ -0,0 +1,26 @@
+//
+// HMSPollResultDisplayExtension.swift
+// hmssdk_flutter
+//
+// Created by Pushpam on 18/01/24.
+//
+
+import Foundation
+import HMSSDK
+
+class HMSPollResultExtension{
+
+ static func toDictionary(pollResult: HMSPollResult) -> [String:Any]{
+
+ var map = [String:Any]()
+
+ var questions = pollResult.questions.map{( HMSPollQuestionResultExtension.toDictionary(pollQuestionResult: $0))}
+ map["questions"] = questions
+
+ map["total_distinct_users"] = pollResult.maxUserCount
+ map["total_responses"] = pollResult.totalResponse
+ map["voting_users"] = pollResult.userCount
+
+ return map
+ }
+}
diff --git a/packages/hmssdk_flutter/ios/Classes/SwiftHmssdkFlutterPlugin.swift b/packages/hmssdk_flutter/ios/Classes/SwiftHmssdkFlutterPlugin.swift
index ef9e71cb5..62ebacd91 100644
--- a/packages/hmssdk_flutter/ios/Classes/SwiftHmssdkFlutterPlugin.swift
+++ b/packages/hmssdk_flutter/ios/Classes/SwiftHmssdkFlutterPlugin.swift
@@ -16,13 +16,15 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene
var rtcStatsEventChannel: FlutterEventChannel?
var sessionEventChannel: FlutterEventChannel?
var hlsPlayerChannel: FlutterEventChannel?
-
+ var pollsEventChannel: FlutterEventChannel?
+
var eventSink: FlutterEventSink?
var previewSink: FlutterEventSink?
var logsSink: FlutterEventSink?
var rtcSink: FlutterEventSink?
var sessionSink: FlutterEventSink?
var hlsPlayerSink: FlutterEventSink?
+ var pollsEventSink: FlutterEventSink?
var roleChangeRequest: HMSRoleChangeRequest?
@@ -54,14 +56,16 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene
let rtcChannel = FlutterEventChannel(name: "rtc_event_channel", binaryMessenger: registrar.messenger())
let sessionChannel = FlutterEventChannel(name: "session_event_channel", binaryMessenger: registrar.messenger())
let hlsChannel = FlutterEventChannel(name: "hls_player_channel", binaryMessenger: registrar.messenger())
-
+ let pollsChannel = FlutterEventChannel(name: "polls_event_channel",binaryMessenger: registrar.messenger())
+
let instance = SwiftHmssdkFlutterPlugin(channel: channel,
meetingEventChannel: eventChannel,
previewEventChannel: previewChannel,
logsEventChannel: logsChannel,
rtcStatsEventChannel: rtcChannel,
sessionEventChannel: sessionChannel,
- hlsPlayerChannel: hlsChannel)
+ hlsPlayerChannel: hlsChannel,
+ pollsEventChannel: pollsChannel)
let videoViewFactory = HMSFlutterPlatformViewFactory(plugin: instance)
registrar.register(videoViewFactory, withId: "HMSFlutterPlatformView")
@@ -75,6 +79,7 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene
rtcChannel.setStreamHandler(instance)
sessionChannel.setStreamHandler(instance)
hlsChannel.setStreamHandler(instance)
+ pollsChannel.setStreamHandler(instance)
registrar.addMethodCallDelegate(instance, channel: channel)
}
@@ -85,7 +90,8 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene
logsEventChannel: FlutterEventChannel,
rtcStatsEventChannel: FlutterEventChannel,
sessionEventChannel: FlutterEventChannel,
- hlsPlayerChannel: FlutterEventChannel) {
+ hlsPlayerChannel: FlutterEventChannel,
+ pollsEventChannel: FlutterEventChannel) {
self.channel = channel
self.meetingEventChannel = meetingEventChannel
@@ -94,6 +100,7 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene
self.rtcStatsEventChannel = rtcStatsEventChannel
self.sessionEventChannel = sessionEventChannel
self.hlsPlayerChannel = hlsPlayerChannel
+ self.pollsEventChannel = pollsEventChannel
}
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
@@ -116,6 +123,8 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene
sessionSink = events
case "hls_player":
hlsPlayerSink = events
+ case "polls":
+ pollsEventSink = events
default:
return FlutterError(code: #function, message: "invalid event sink name", details: arguments)
}
@@ -164,6 +173,12 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene
} else {
print(#function, "hlsPlayerChannel not found")
}
+ if pollsEventChannel != nil {
+ pollsEventChannel!.setStreamHandler(nil)
+ pollsEventSink = nil
+ } else{
+ print(#function, "pollsEventChannel not found")
+ }
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
@@ -294,9 +309,15 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene
case "get_room_layout":
getRoomLayout(call, result)
+ // MARK: - Large room APIs
case "get_peer_list_iterator", "peer_list_iterator_has_next", "peer_list_iterator_next":
HMSPeerListIteratorAction.peerListIteratorAction(call, result, hmsSDK)
+ // MARK: - Polls
+
+ case "add_poll_update_listener", "remove_poll_update_listener", "quick_start_poll", "add_single_choice_poll_response", "add_multi_choice_poll_response", "stop_poll":
+ pollsAction(call, result)
+
default:
result(FlutterMethodNotImplemented)
}
@@ -401,6 +422,43 @@ public class SwiftHmssdkFlutterPlugin: NSObject, FlutterPlugin, HMSUpdateListene
result(FlutterMethodNotImplemented)
}
}
+
+ var currentPolls = [HMSPoll]()
+ private func pollsAction(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
+ switch call.method {
+ case "add_poll_update_listener":
+ var listener : HMSInteractivityCenter.HMSPollListener = { [weak self] hmsPoll, hmsPollUpdateType in
+
+ guard let self = self else { return }
+
+ var map = ["event_name": "on_poll_update",
+ "data": [
+ "poll": HMSPollExtension.toDictionary(poll: hmsPoll),
+ "poll_update_type": HMSPollExtension.getPollUpdateType(updateType: hmsPollUpdateType)
+ ]
+ ] as [String : Any]
+ if(hmsPollUpdateType == .started){
+ currentPolls.append(hmsPoll)
+ }else if(hmsPollUpdateType == .stopped){
+ currentPolls.removeAll(where: {
+ $0.pollID == hmsPoll.pollID
+ })
+ }
+ else if(hmsPollUpdateType == .resultsUpdated){
+ if let index = currentPolls.firstIndex(where: {$0.pollID == hmsPoll.pollID}){
+ currentPolls[index] = hmsPoll
+ }
+ }
+ self.pollsEventSink?(map)
+ }
+ hmsSDK?.interactivityCenter.addPollUpdateListner(listener)
+ break
+ case "remove_poll_update_listener":
+ break
+ default:
+ HMSPollAction.pollActions(call, result, hmsSDK, currentPolls)
+ }
+ }
// MARK: - Track Setting
var audioMixerSourceMap = [String: HMSAudioNode]()
diff --git a/packages/hmssdk_flutter/lib/assets/sdk-versions.json b/packages/hmssdk_flutter/lib/assets/sdk-versions.json
index f78aa5d02..669853ab3 100644
--- a/packages/hmssdk_flutter/lib/assets/sdk-versions.json
+++ b/packages/hmssdk_flutter/lib/assets/sdk-versions.json
@@ -1,7 +1,7 @@
{
- "flutter": "1.9.6",
- "ios": "1.4.2",
+ "flutter": "1.9.9",
+ "ios": "1.5.0",
"iOSBroadcastExtension": "0.0.9",
"iOSHLSPlayerSDK": "0.0.2",
- "android": "2.8.8"
+ "android": "2.9.0"
}
diff --git a/packages/hmssdk_flutter/lib/hmssdk_flutter.dart b/packages/hmssdk_flutter/lib/hmssdk_flutter.dart
index 121adadd8..5ab479460 100644
--- a/packages/hmssdk_flutter/lib/hmssdk_flutter.dart
+++ b/packages/hmssdk_flutter/lib/hmssdk_flutter.dart
@@ -18,10 +18,11 @@ export 'src/enum/hms_message_recipient_type.dart';
export 'src/enum/hms_log_level.dart';
export 'src/enum/hms_stats_listener_method.dart';
export 'src/enum/hms_track_init_state.dart';
-export 'src/enum/hms_Quality_limitation_reason.dart';
+export 'src/enum/hms_quality_limitation_reason_enum.dart';
export 'src/enum/hms_simulcast_layer.dart';
export 'src/enum/hms_audio_mode.dart';
export 'src/enum/hms_hls_playback_state.dart';
+export 'src/enum/hms_poll_enum.dart';
//EXCEPTIONS
export 'src/exceptions/hms_exception.dart';
@@ -62,7 +63,6 @@ export 'src/model/hms_peer_removed_from_room.dart';
export 'src/model/hms_message_recipient.dart';
export 'src/model/hms_logs_listener.dart';
export 'src/model/hms_actions_result_listener.dart';
-export 'src/model/hms_message_result_listener.dart';
export 'src/enum/hms_action_result_listener_method.dart';
export 'src/model/hms_remote_peer.dart';
export 'src/model/hms_hls_config.dart';
@@ -102,6 +102,12 @@ export 'src/model/hms_peer_list_iterator.dart';
export 'src/model/peer_list_iterator_options.dart';
export 'src/enum/hms_recording_state.dart';
export 'src/enum/hms_streaming_state.dart';
+export 'src/model/polls/hms_poll_interactivity_center.dart';
+export 'src/model/polls/hms_poll_listener.dart';
+export 'src/model/polls/hms_poll_question_builder.dart';
+export 'src/model/polls/hms_poll.dart';
+export 'src/model/polls/hms_poll_question.dart';
+export 'src/model/polls/hms_poll_question_option.dart';
//Views
export 'src/ui/meeting/hms_texture_view.dart';
diff --git a/packages/hmssdk_flutter/lib/src/common/platform_methods.dart b/packages/hmssdk_flutter/lib/src/common/platform_methods.dart
index 1d22ec82c..2a67661bc 100644
--- a/packages/hmssdk_flutter/lib/src/common/platform_methods.dart
+++ b/packages/hmssdk_flutter/lib/src/common/platform_methods.dart
@@ -193,14 +193,26 @@ enum PlatformMethod {
getPeerListIterator,
peerListIteratorHasNext,
peerListIteratorNext,
+
+ ///Raise/Lower Hand
lowerLocalPeerHand,
lowerRemotePeerHand,
raiseLocalPeerHand,
+
+ ///TextureView methods
createTextureView,
disposeTextureView,
addTrack,
removeTrack,
- setDisplayResolution
+ setDisplayResolution,
+
+ ///Poll methods
+ addPollUpdateListener,
+ removePollUpdateListener,
+ quickStartPoll,
+ addSingleChoicePollResponse,
+ addMultiChoicePollResponse,
+ stopPoll
}
extension PlatformMethodValues on PlatformMethod {
@@ -487,12 +499,15 @@ extension PlatformMethodValues on PlatformMethod {
case PlatformMethod.peerListIteratorNext:
return "peer_list_iterator_next";
+ ///Raise/Lower Hand
case PlatformMethod.lowerLocalPeerHand:
return "lower_local_peer_hand";
case PlatformMethod.lowerRemotePeerHand:
return "lower_remote_peer_hand";
case PlatformMethod.raiseLocalPeerHand:
return "raise_local_peer_hand";
+
+ ///TextureView methods
case PlatformMethod.createTextureView:
return "create_texture_view";
case PlatformMethod.disposeTextureView:
@@ -503,6 +518,20 @@ extension PlatformMethodValues on PlatformMethod {
return "remove_track";
case PlatformMethod.setDisplayResolution:
return "set_display_resolution";
+
+ ///Poll Methods
+ case PlatformMethod.addPollUpdateListener:
+ return "add_poll_update_listener";
+ case PlatformMethod.removePollUpdateListener:
+ return "remove_poll_update_listener";
+ case PlatformMethod.quickStartPoll:
+ return "quick_start_poll";
+ case PlatformMethod.addSingleChoicePollResponse:
+ return "add_single_choice_poll_response";
+ case PlatformMethod.addMultiChoicePollResponse:
+ return "add_multi_choice_poll_response";
+ case PlatformMethod.stopPoll:
+ return "stop_poll";
default:
return 'unknown';
}
@@ -546,9 +575,6 @@ extension PlatformMethodValues on PlatformMethod {
case 'on_re_connected':
return PlatformMethod.onReconnected;
- case 'on_re_connected':
- return PlatformMethod.onReconnected;
-
case 'switch_audio':
return PlatformMethod.switchAudio;
@@ -791,12 +817,15 @@ extension PlatformMethodValues on PlatformMethod {
case "peer_list_iterator_next":
return PlatformMethod.peerListIteratorNext;
+ ///raise/lower hand
case "lower_local_peer_hand":
return PlatformMethod.lowerLocalPeerHand;
case "lower_remote_peer_hand":
return PlatformMethod.lowerRemotePeerHand;
case "raise_local_peer_hand":
return PlatformMethod.raiseLocalPeerHand;
+
+ ///TextureView methods
case "create_texture_view":
return PlatformMethod.createTextureView;
case "dispose_texture_view":
@@ -807,6 +836,20 @@ extension PlatformMethodValues on PlatformMethod {
return PlatformMethod.removeTrack;
case "set_display_resolution":
return PlatformMethod.setDisplayResolution;
+
+ ///Poll methods
+ case "add_poll_update_listener":
+ return PlatformMethod.addPollUpdateListener;
+ case "remove_poll_update_listener":
+ return PlatformMethod.removePollUpdateListener;
+ case "quick_start_poll":
+ return PlatformMethod.quickStartPoll;
+ case "add_single_choice_poll_response":
+ return PlatformMethod.addSingleChoicePollResponse;
+ case "add_multi_choice_poll_response":
+ return PlatformMethod.addMultiChoicePollResponse;
+ case "stop_poll":
+ return PlatformMethod.stopPoll;
default:
return PlatformMethod.unknown;
}
diff --git a/packages/hmssdk_flutter/lib/src/enum/hms_action_result_listener_method.dart b/packages/hmssdk_flutter/lib/src/enum/hms_action_result_listener_method.dart
index 13c400c4b..54beb1935 100644
--- a/packages/hmssdk_flutter/lib/src/enum/hms_action_result_listener_method.dart
+++ b/packages/hmssdk_flutter/lib/src/enum/hms_action_result_listener_method.dart
@@ -26,5 +26,8 @@ enum HMSActionResultListenerMethod {
lowerLocalPeerHand,
lowerRemotePeerHand,
raiseLocalPeerHand,
+ quickStartPoll,
+ addSingleChoicePollResponse,
+ addMultiChoicePollResponse,
unknown
}
diff --git a/packages/hmssdk_flutter/lib/src/enum/hms_poll_enum.dart b/packages/hmssdk_flutter/lib/src/enum/hms_poll_enum.dart
new file mode 100644
index 000000000..322137b9b
--- /dev/null
+++ b/packages/hmssdk_flutter/lib/src/enum/hms_poll_enum.dart
@@ -0,0 +1,160 @@
+///[HMSPollUserTrackingMode] is the mode based on which the app identifies the user
+enum HMSPollUserTrackingMode { user_id, peer_id, username }
+
+extension HMSPollUserTrackingModeValues on HMSPollUserTrackingMode {
+ static HMSPollUserTrackingMode getHMSPollUserTrackingModeFromString(
+ String pollTrackingMode) {
+ switch (pollTrackingMode) {
+ case "user_id":
+ return HMSPollUserTrackingMode.user_id;
+ case "peer_id":
+ return HMSPollUserTrackingMode.peer_id;
+ case "username":
+ return HMSPollUserTrackingMode.username;
+ default:
+ return HMSPollUserTrackingMode.user_id;
+ }
+ }
+
+ static String getStringFromHMSPollUserTrackingMode(
+ HMSPollUserTrackingMode hmsPollUserTrackingMode) {
+ switch (hmsPollUserTrackingMode) {
+ case HMSPollUserTrackingMode.user_id:
+ return "user_id";
+ case HMSPollUserTrackingMode.peer_id:
+ return "peer_id";
+ case HMSPollUserTrackingMode.username:
+ return "username";
+ default:
+ return "user_id";
+ }
+ }
+}
+
+///The [HMSPollCategory] enum categorizes whether a poll or quiz is being represented.
+enum HMSPollCategory { poll, quiz }
+
+extension HMSPollCategoryValues on HMSPollCategory {
+ static HMSPollCategory getHMSPollCategoryFromString(String pollCategory) {
+ switch (pollCategory) {
+ case "poll":
+ return HMSPollCategory.poll;
+ case "quiz":
+ return HMSPollCategory.quiz;
+ default:
+ return HMSPollCategory.poll;
+ }
+ }
+
+ static String getStringFromHMSPollCategory(HMSPollCategory hmsPollCategory) {
+ switch (hmsPollCategory) {
+ case HMSPollCategory.poll:
+ return "poll";
+ case HMSPollCategory.quiz:
+ return "quiz";
+ default:
+ return "poll";
+ }
+ }
+}
+
+///[HMSPollQuestionType] enum categorizes the type of question
+enum HMSPollQuestionType { singleChoice, multiChoice, shortAnswer, longAnswer }
+
+extension HMSPollQuestionTypeValues on HMSPollQuestionType {
+ static HMSPollQuestionType getHMSPollQuestionTypeFromString(
+ String pollQuestionType) {
+ switch (pollQuestionType) {
+ case "single_choice":
+ return HMSPollQuestionType.singleChoice;
+ case "multi_choice":
+ return HMSPollQuestionType.multiChoice;
+ case "short_answer":
+ return HMSPollQuestionType.shortAnswer;
+ case "long_answer":
+ return HMSPollQuestionType.longAnswer;
+ default:
+ return HMSPollQuestionType.singleChoice;
+ }
+ }
+
+ static String getStringFromHMSPollQuestionType(
+ HMSPollQuestionType hmsPollQuestionType) {
+ switch (hmsPollQuestionType) {
+ case HMSPollQuestionType.singleChoice:
+ return "single_choice";
+ case HMSPollQuestionType.multiChoice:
+ return "multi_choice";
+ case HMSPollQuestionType.shortAnswer:
+ return "short_answer";
+ case HMSPollQuestionType.longAnswer:
+ return "long_answer";
+ default:
+ return "single_choice";
+ }
+ }
+}
+
+///[HMSPollState] enum represents the different states a poll can be in.
+enum HMSPollState { started, stopped, created }
+
+extension HMSPollStateValues on HMSPollState {
+ static HMSPollState getHMSPollStateFromString(String pollState) {
+ switch (pollState) {
+ case "started":
+ return HMSPollState.started;
+ case "stopped":
+ return HMSPollState.stopped;
+ case "created":
+ return HMSPollState.created;
+ default:
+ return HMSPollState.created;
+ }
+ }
+
+ static String getStringFromHMSPollState(HMSPollState hmsPollState) {
+ switch (hmsPollState) {
+ case HMSPollState.started:
+ return "started";
+ case HMSPollState.stopped:
+ return "stopped";
+ case HMSPollState.created:
+ return "created";
+ default:
+ return "created";
+ }
+ }
+}
+
+///[HMSPollUpdateType] enum represents different types of updates that can occur in a poll.
+enum HMSPollUpdateType { started, stopped, resultsupdated }
+
+extension HMSPollUpdateTypeValues on HMSPollUpdateType {
+ static HMSPollUpdateType getHMSPollUpdateTypeFromString(
+ String pollUpdateType) {
+ switch (pollUpdateType) {
+ case "started":
+ return HMSPollUpdateType.started;
+ case "stopped":
+ return HMSPollUpdateType.stopped;
+ case "results_updated":
+ return HMSPollUpdateType.resultsupdated;
+ default:
+ return HMSPollUpdateType.resultsupdated;
+ }
+ }
+}
+
+///[HMSPollListenerMethod] contains the [HMSPollListener] methods
+enum HMSPollListenerMethod { onPollUpdate, unknown }
+
+extension HMSPollListenerMethodValues on HMSPollListenerMethod {
+ static HMSPollListenerMethod getMethodFromName(String name) {
+ switch (name) {
+ case 'on_poll_update':
+ return HMSPollListenerMethod.onPollUpdate;
+ default:
+ return HMSPollListenerMethod.unknown;
+ }
+ }
+}
diff --git a/packages/hmssdk_flutter/lib/src/enum/hms_Quality_limitation_reason.dart b/packages/hmssdk_flutter/lib/src/enum/hms_quality_limitation_reason_enum.dart
similarity index 100%
rename from packages/hmssdk_flutter/lib/src/enum/hms_Quality_limitation_reason.dart
rename to packages/hmssdk_flutter/lib/src/enum/hms_quality_limitation_reason_enum.dart
diff --git a/packages/hmssdk_flutter/lib/src/model/hms_message_result_listener.dart b/packages/hmssdk_flutter/lib/src/model/hms_message_result_listener.dart
deleted file mode 100644
index 1e33a3d28..000000000
--- a/packages/hmssdk_flutter/lib/src/model/hms_message_result_listener.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-// Project imports:
-import '../../hmssdk_flutter.dart';
-
-abstract class HMSMessageResultListener {
- void onSuccess({required HMSMessage hmsMessage});
-
- void onError({HMSException? hmsException});
-}
diff --git a/packages/hmssdk_flutter/lib/src/model/hms_permissions.dart b/packages/hmssdk_flutter/lib/src/model/hms_permissions.dart
index d4f60d2d1..bc1792034 100644
--- a/packages/hmssdk_flutter/lib/src/model/hms_permissions.dart
+++ b/packages/hmssdk_flutter/lib/src/model/hms_permissions.dart
@@ -10,6 +10,8 @@ class HMSPermissions {
final bool? removeOthers;
final bool? rtmpStreaming;
final bool? unMute;
+ final bool? pollRead;
+ final bool? pollWrite;
HMSPermissions(
{this.endRoom,
@@ -19,19 +21,22 @@ class HMSPermissions {
this.browserRecording,
this.mute,
this.unMute,
- this.changeRole});
+ this.changeRole,
+ this.pollRead,
+ this.pollWrite});
factory HMSPermissions.fromMap(Map map) {
return HMSPermissions(
- browserRecording: map["browser_recording"],
- changeRole: map['change_role'],
- endRoom: map['end_room'],
- hlsStreaming: map['hls_streaming'],
- mute: map['mute'],
- removeOthers: map['remove_others'],
- rtmpStreaming: map['rtmp_streaming'],
- unMute: map['un_mute'],
- );
+ browserRecording: map["browser_recording"],
+ changeRole: map['change_role'],
+ endRoom: map['end_room'],
+ hlsStreaming: map['hls_streaming'],
+ mute: map['mute'],
+ removeOthers: map['remove_others'],
+ rtmpStreaming: map['rtmp_streaming'],
+ unMute: map['un_mute'],
+ pollRead: map['poll_read'],
+ pollWrite: map['poll_write']);
}
Map toJson() {
@@ -43,7 +48,9 @@ class HMSPermissions {
'un_mute': unMute,
'hls_streaming': hlsStreaming,
'rtmp_streaming': rtmpStreaming,
- 'change_role': changeRole
+ 'change_role': changeRole,
+ 'poll_read': pollRead,
+ 'poll_write': pollWrite
};
}
}
diff --git a/packages/hmssdk_flutter/lib/src/model/hms_quality_limitation_reasons.dart b/packages/hmssdk_flutter/lib/src/model/hms_quality_limitation_reasons.dart
index b401d1c60..e951f4beb 100644
--- a/packages/hmssdk_flutter/lib/src/model/hms_quality_limitation_reasons.dart
+++ b/packages/hmssdk_flutter/lib/src/model/hms_quality_limitation_reasons.dart
@@ -1,4 +1,4 @@
-import 'package:hmssdk_flutter/src/enum/hms_Quality_limitation_reason.dart';
+import 'package:hmssdk_flutter/src/enum/hms_quality_limitation_reason_enum.dart';
class HMSQualityLimitationReasons {
double? bandWidth;
diff --git a/packages/hmssdk_flutter/lib/src/model/platform_method_response.dart b/packages/hmssdk_flutter/lib/src/model/platform_method_response.dart
index 130cb25cd..223fe02f6 100644
--- a/packages/hmssdk_flutter/lib/src/model/platform_method_response.dart
+++ b/packages/hmssdk_flutter/lib/src/model/platform_method_response.dart
@@ -82,3 +82,13 @@ class HMSHLSPlayerPlaybackEventResponse {
HMSHLSPlayerPlaybackEventResponse({required this.method, required this.data});
}
+
+///HMSPollListenerMethodResponse contains all the responses sent from the poll channel
+///
+/// Checkout different responses in [HMSPollListenerMethod] enum
+class HMSPollListenerMethodResponse {
+ final HMSPollListenerMethod method;
+ final Map data;
+
+ HMSPollListenerMethodResponse({required this.method, required this.data});
+}
diff --git a/packages/hmssdk_flutter/lib/src/model/polls/hms_poll.dart b/packages/hmssdk_flutter/lib/src/model/polls/hms_poll.dart
new file mode 100644
index 000000000..973a538fc
--- /dev/null
+++ b/packages/hmssdk_flutter/lib/src/model/polls/hms_poll.dart
@@ -0,0 +1,92 @@
+///Project imports
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+import 'package:hmssdk_flutter/src/model/hms_date_extension.dart';
+import 'package:hmssdk_flutter/src/model/polls/hms_poll_result_display.dart';
+
+///[HMSPoll] class represents poll
+///
+///This class encapsulates various properties and methods related to a poll, including its ID, title, anonymity status,
+///category (poll or quiz), creator, duration, user tracking mode, question count, questions, result display settings,
+///roles that can view responses, roles that can vote, start time, starter, current state, stop time, and stopper.
+class HMSPoll {
+ final String pollId;
+ final String title;
+ final bool anonymous;
+ final HMSPollCategory category;
+ final HMSPeer? createdBy;
+ final Duration? duration;
+ final HMSPollUserTrackingMode? pollUserTrackingMode;
+ final int? questionCount;
+ final List? questions;
+ final HMSPollResultDisplay? result;
+ final List rolesThatCanViewResponses;
+ final List rolesThatCanVote;
+ final DateTime? startedAt;
+ final HMSPeer? startedBy;
+ final HMSPollState state;
+ final DateTime? stoppedAt;
+ final HMSPeer? stoppedBy;
+
+ HMSPoll({
+ required this.pollId,
+ required this.title,
+ required this.anonymous,
+ required this.category,
+ required this.createdBy,
+ required this.duration,
+ required this.pollUserTrackingMode,
+ required this.questionCount,
+ required this.questions,
+ required this.result,
+ required this.rolesThatCanViewResponses,
+ required this.rolesThatCanVote,
+ required this.startedAt,
+ required this.startedBy,
+ required this.state,
+ required this.stoppedAt,
+ required this.stoppedBy,
+ });
+
+ factory HMSPoll.fromMap(Map map) {
+ return HMSPoll(
+ pollId: map['poll_id'],
+ title: map['title'],
+ anonymous: map['anonymous'],
+ category:
+ HMSPollCategoryValues.getHMSPollCategoryFromString(map['category']),
+ createdBy:
+ map['created_by'] != null ? HMSPeer.fromMap(map['created_by']) : null,
+ duration: map['duration'] != null
+ ? Duration(milliseconds: map['duration'])
+ : null,
+ pollUserTrackingMode: map['mode'] != null
+ ? HMSPollUserTrackingModeValues.getHMSPollUserTrackingModeFromString(
+ map['mode'])
+ : null,
+ questionCount: map['question_count'],
+ questions: (map['questions'] as List)
+ .map((e) => HMSPollQuestion.fromMap(e))
+ .toList(),
+ result: map['result'] != null
+ ? HMSPollResultDisplay.fromMap(map['result'])
+ : null,
+ rolesThatCanViewResponses: (map['roles_that_can_view_responses'] as List)
+ .map((e) => HMSRole.fromMap(e))
+ .toList(),
+ rolesThatCanVote: (map['roles_that_can_vote'] as List)
+ .map((e) => HMSRole.fromMap(e))
+ .toList(),
+ startedAt: map.containsKey("started_at")
+ ? HMSDateExtension.convertDateFromEpoch(map['started_at'])
+ : null,
+ startedBy:
+ map['started_by'] != null ? HMSPeer.fromMap(map['started_by']) : null,
+ state: HMSPollStateValues.getHMSPollStateFromString(map['state']),
+ stoppedAt: map['stopped_at'] != null
+ ? HMSDateExtension.convertDateFromEpoch(map['stopped_at'])
+ : null,
+ stoppedBy:
+ map['stopped_by'] != null ? HMSPeer.fromMap(map['stopped_by']) : null,
+ );
+ }
+}
diff --git a/packages/hmssdk_flutter/lib/src/model/polls/hms_poll_answer.dart b/packages/hmssdk_flutter/lib/src/model/polls/hms_poll_answer.dart
new file mode 100644
index 000000000..7e00f466c
--- /dev/null
+++ b/packages/hmssdk_flutter/lib/src/model/polls/hms_poll_answer.dart
@@ -0,0 +1,56 @@
+///Project imports
+import 'package:hmssdk_flutter/src/enum/hms_poll_enum.dart';
+
+///[HMSPollAnswer] class represents answer to poll questions
+class HMSPollAnswer {
+ final String? answerText;
+ final Duration duration;
+ final int questionId;
+ final HMSPollQuestionType questionType;
+ final int? selectedOption;
+ final List? selectedOptions;
+ final bool skipped;
+ final bool update;
+
+ HMSPollAnswer({
+ this.answerText,
+ required this.duration,
+ required this.questionId,
+ required this.questionType,
+ this.selectedOption,
+ this.selectedOptions,
+ required this.skipped,
+ required this.update,
+ });
+
+ ///Method to get HMSPollAnswer from map
+ factory HMSPollAnswer.fromMap(Map map) {
+ return HMSPollAnswer(
+ answerText: map['answer'],
+ duration: Duration(seconds: map['duration']),
+ questionId: map['question_id'],
+ questionType: HMSPollQuestionTypeValues.getHMSPollQuestionTypeFromString(
+ map['question_type']),
+ selectedOption: map['selected_option'],
+ selectedOptions: map["selected_options"] != null
+ ? List.from(map['selected_options'])
+ : null,
+ skipped: map['skipped'],
+ update: map['update'],
+ );
+ }
+
+ ///Method to get map from HMSPollAnswer Object
+ Map toMap() {
+ return {
+ 'answer': answerText,
+ 'duration': duration.inSeconds,
+ 'question_id': questionId,
+ 'question_type': questionType.toString(),
+ 'selected_option': selectedOption,
+ 'selected_options': selectedOptions,
+ 'skipped': skipped,
+ 'update': update,
+ };
+ }
+}
diff --git a/packages/hmssdk_flutter/lib/src/model/polls/hms_poll_answer_response.dart b/packages/hmssdk_flutter/lib/src/model/polls/hms_poll_answer_response.dart
new file mode 100644
index 000000000..88cc899cd
--- /dev/null
+++ b/packages/hmssdk_flutter/lib/src/model/polls/hms_poll_answer_response.dart
@@ -0,0 +1,19 @@
+///Project imports
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+
+///The[HMSPollAnswerResponse] class represents the poll answer
+class HMSPollAnswerResponse {
+ final bool correct;
+ final HMSException? error;
+ final int questionIndex;
+
+ HMSPollAnswerResponse(this.error, this.questionIndex,
+ {required this.correct});
+
+ factory HMSPollAnswerResponse.fromMap(Map map) {
+ return HMSPollAnswerResponse(
+ map["error"] == null ? null : HMSException.fromMap(map["error"]),
+ map["question_index"],
+ correct: map["correct"]);
+ }
+}
diff --git a/packages/hmssdk_flutter/lib/src/model/polls/hms_poll_interactivity_center.dart b/packages/hmssdk_flutter/lib/src/model/polls/hms_poll_interactivity_center.dart
new file mode 100644
index 000000000..4e4c0e846
--- /dev/null
+++ b/packages/hmssdk_flutter/lib/src/model/polls/hms_poll_interactivity_center.dart
@@ -0,0 +1,267 @@
+import 'package:hmssdk_flutter/hmssdk_flutter.dart';
+import 'package:hmssdk_flutter/src/model/polls/hms_poll_answer_response.dart';
+import 'package:hmssdk_flutter/src/service/platform_service.dart';
+
+abstract class HMSPollInteractivityCenter {
+ ///[addPollUpdateListener] adds the poll update listener to send
+ ///the poll events to the application
+ ///**Parameters**:
+ ///
+ ///**listener** - [listener]
+ static void addPollUpdateListener({required HMSPollListener listener}) {
+ PlatformService.addPollUpdateListener(listener);
+ }
+
+ ///[removePollUpdateListener] removes the poll update listener
+ ///Thus the application no longer receives poll events
+ static void removePollUpdateListener() {
+ PlatformService.removePollUpdateListener();
+ }
+
+ ///[quickStartPoll] starts a quick poll with supplied arguments
+ ///
+ ///**Parameters**:
+ ///
+ ///**pollBuilder** - [pollBuilder] is an object of HMSPollBuilder containing the poll configurations
+ ///
+ ///**hmsActionResultListener** - [hmsActionResultListener] is a callback whose [HMSActionResultListener.onSuccess] will be called when the action completes successfully.
+ ///
+ ///**Returns**
+ ///
+ /// Future