Merge branch 'master' of https://gitlab.com/Cloud_Solution/diplomatic-quarter into dashboard
Conflicts: lib/config/localized_values.dart lib/locator.dart lib/pages/landing/home_page.dart lib/pages/landing/landing_page.dart lib/uitl/translations_delegate_base.dartmerge-requests/34/head
commit
30c0963063
Binary file not shown.
@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICkDCCAXgCAQAwSzEjMCEGCSqGSIb3DQEJARYUaGFyb29uNjEzOEBnbWFpbC5j
|
||||
b20xFzAVBgNVBAMMDk1vaGFtZWQgTWVrYXd5MQswCQYDVQQGEwJBRTCCASIwDQYJ
|
||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnHqsyE7WfiVcE1Lpa4t4OVO6qlll2q
|
||||
1djs0XG06R/dlDtIqv4940/XLj+hU93mzAcVvFW4DSIEdD3InM3+T6oMTjPu6meU
|
||||
69h9ryaVkluQRrT/tdGI1EKO4MWGMe4MDIt7DqMhMfAcxTwekwdxdKaCEhaw3qnA
|
||||
l/64AelY6URW1pHHJMA0VV7j+pE3jVNai+muMXPrhQ1VrOrV8FftpY3bEeRJR2Cl
|
||||
T0tv0LhEMu4SfLnVWCzGQQC82hilDw3rH3ZDs8DFxF9agNVdwKlYamarh1dQXwRq
|
||||
Yx2+sjY1/51r9L4VS+GAh9ECxz0e+43NpzfZ/N+mTeDYKDepaBwPQ6kCAwEAAaAA
|
||||
MA0GCSqGSIb3DQEBCwUAA4IBAQB89OyLfywKT7ftmpEqCmgsmaJexb580q9w8wOk
|
||||
1JhJkNV5ec+p1dnge2NZeJ4LGII/5wmPj1vANNW0GZdmJDgnC+2gg9toq1QLCAsF
|
||||
rW7/LMpgAoEH+P5bhrHV9RRv6BQi0ZmN0apBHjp/pqZfm2Cl5jQPEWjUEf2tIF4l
|
||||
LSKdok6IPO9n4Fgyk0XdUNSEhgVhsLtZkGiXnkI1YovKDnupTFYPXMLp103bc9zP
|
||||
xDxwscvOysNDijlzZAkJPg2z8NrJIRDrKvLRHzxQwZ/1LHVB/51bp/1iyks3vOjh
|
||||
qw5XVsrtGAjCjU9md7q3XkPSyKzhK9UqPdOxdvl1OY0KKIIY
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
@ -0,0 +1,8 @@
|
||||
{\rtf1\ansi\ansicpg1252\cocoartf2513
|
||||
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
|
||||
{\colortbl;\red255\green255\blue255;}
|
||||
{\*\expandedcolortbl;;}
|
||||
\paperw11900\paperh16840\margl1440\margr1440\vieww10800\viewh8400\viewkind0
|
||||
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
|
||||
|
||||
\f0\fs24 \cf0 Hmg54321}
|
||||
Binary file not shown.
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CLIENT_ID</key>
|
||||
<string>864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r.apps.googleusercontent.com</string>
|
||||
<key>REVERSED_CLIENT_ID</key>
|
||||
<string>com.googleusercontent.apps.864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r</string>
|
||||
<key>API_KEY</key>
|
||||
<string>AIzaSyA_6ayGCk4fly7o7eTVBrj9kuHBYHMAOfs</string>
|
||||
<key>GCM_SENDER_ID</key>
|
||||
<string>864393916058</string>
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>com.cloud.diplomaticquarterapp</string>
|
||||
<key>PROJECT_ID</key>
|
||||
<string>diplomaticquarter-d2385</string>
|
||||
<key>STORAGE_BUCKET</key>
|
||||
<string>diplomaticquarter-d2385.appspot.com</string>
|
||||
<key>IS_ADS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_ANALYTICS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_APPINVITE_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_GCM_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_SIGNIN_ENABLED</key>
|
||||
<true></true>
|
||||
<key>GOOGLE_APP_ID</key>
|
||||
<string>1:864393916058:ios:13f787bbfe6051f8b97923</string>
|
||||
<key>DATABASE_URL</key>
|
||||
<string>https://diplomaticquarter-d2385.firebaseio.com</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -0,0 +1,40 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "864393916058",
|
||||
"firebase_url": "https://diplomaticquarter-d2385.firebaseio.com",
|
||||
"project_id": "diplomaticquarter-d2385",
|
||||
"storage_bucket": "diplomaticquarter-d2385.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:864393916058:android:5b5a65cd6d8c18b4b97923",
|
||||
"android_client_info": {
|
||||
"package_name": "com.cloud.diplomaticquarterapp"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "864393916058-tphjrn8j39ntevt32ekcvmll8aue7qql.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyBdV3mos1BPhUzNKCj2KANJtiO3o2zh9IM"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "864393916058-tphjrn8j39ntevt32ekcvmll8aue7qql.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,40 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "864393916058",
|
||||
"firebase_url": "https://diplomaticquarter-d2385.firebaseio.com",
|
||||
"project_id": "diplomaticquarter-d2385",
|
||||
"storage_bucket": "diplomaticquarter-d2385.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:864393916058:android:5b5a65cd6d8c18b4b97923",
|
||||
"android_client_info": {
|
||||
"package_name": "com.cloud.diplomaticquarterapp"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "864393916058-tphjrn8j39ntevt32ekcvmll8aue7qql.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyBdV3mos1BPhUzNKCj2KANJtiO3o2zh9IM"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "864393916058-tphjrn8j39ntevt32ekcvmll8aue7qql.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CLIENT_ID</key>
|
||||
<string>864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r.apps.googleusercontent.com</string>
|
||||
<key>REVERSED_CLIENT_ID</key>
|
||||
<string>com.googleusercontent.apps.864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r</string>
|
||||
<key>API_KEY</key>
|
||||
<string>AIzaSyA_6ayGCk4fly7o7eTVBrj9kuHBYHMAOfs</string>
|
||||
<key>GCM_SENDER_ID</key>
|
||||
<string>864393916058</string>
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>com.cloud.diplomaticquarterapp</string>
|
||||
<key>PROJECT_ID</key>
|
||||
<string>diplomaticquarter-d2385</string>
|
||||
<key>STORAGE_BUCKET</key>
|
||||
<string>diplomaticquarter-d2385.appspot.com</string>
|
||||
<key>IS_ADS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_ANALYTICS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_APPINVITE_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_GCM_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_SIGNIN_ENABLED</key>
|
||||
<true></true>
|
||||
<key>GOOGLE_APP_ID</key>
|
||||
<string>1:864393916058:ios:13f787bbfe6051f8b97923</string>
|
||||
<key>DATABASE_URL</key>
|
||||
<string>https://diplomaticquarter-d2385.firebaseio.com</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CLIENT_ID</key>
|
||||
<string>864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r.apps.googleusercontent.com</string>
|
||||
<key>REVERSED_CLIENT_ID</key>
|
||||
<string>com.googleusercontent.apps.864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r</string>
|
||||
<key>API_KEY</key>
|
||||
<string>AIzaSyA_6ayGCk4fly7o7eTVBrj9kuHBYHMAOfs</string>
|
||||
<key>GCM_SENDER_ID</key>
|
||||
<string>864393916058</string>
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>com.cloud.diplomaticquarterapp</string>
|
||||
<key>PROJECT_ID</key>
|
||||
<string>diplomaticquarter-d2385</string>
|
||||
<key>STORAGE_BUCKET</key>
|
||||
<string>diplomaticquarter-d2385.appspot.com</string>
|
||||
<key>IS_ADS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_ANALYTICS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_APPINVITE_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_GCM_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_SIGNIN_ENABLED</key>
|
||||
<true></true>
|
||||
<key>GOOGLE_APP_ID</key>
|
||||
<string>1:864393916058:ios:13f787bbfe6051f8b97923</string>
|
||||
<key>DATABASE_URL</key>
|
||||
<string>https://diplomaticquarter-d2385.firebaseio.com</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
||||
@ -0,0 +1,108 @@
|
||||
class PatientERVirtualHistoryResponse {
|
||||
List<ErRequestHistoryList> erRequestHistoryList;
|
||||
|
||||
PatientERVirtualHistoryResponse({this.erRequestHistoryList});
|
||||
|
||||
PatientERVirtualHistoryResponse.fromJson(Map<String, dynamic> json) {
|
||||
if (json['ErRequestHistoryList'] != null) {
|
||||
erRequestHistoryList = new List<ErRequestHistoryList>();
|
||||
json['ErRequestHistoryList'].forEach((v) {
|
||||
erRequestHistoryList.add(new ErRequestHistoryList.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
if (this.erRequestHistoryList != null) {
|
||||
data['ErRequestHistoryList'] =
|
||||
this.erRequestHistoryList.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class ErRequestHistoryList {
|
||||
dynamic appointmentNo;
|
||||
String arrivalTime;
|
||||
int callDuration;
|
||||
int callStatus;
|
||||
String clientRequestID;
|
||||
String doctorID;
|
||||
String doctorName;
|
||||
String doctorNameN;
|
||||
String doctorTitle;
|
||||
String exWaitingTime;
|
||||
bool isAppointmentHaveRating;
|
||||
int patCount;
|
||||
int projectID;
|
||||
String sArrivalTime;
|
||||
int serviceID;
|
||||
String stringCallStatus;
|
||||
int vCID;
|
||||
int watingtimeInteger;
|
||||
|
||||
ErRequestHistoryList(
|
||||
{this.appointmentNo,
|
||||
this.arrivalTime,
|
||||
this.callDuration,
|
||||
this.callStatus,
|
||||
this.clientRequestID,
|
||||
this.doctorID,
|
||||
this.doctorName,
|
||||
this.doctorNameN,
|
||||
this.doctorTitle,
|
||||
this.exWaitingTime,
|
||||
this.isAppointmentHaveRating,
|
||||
this.patCount,
|
||||
this.projectID,
|
||||
this.sArrivalTime,
|
||||
this.serviceID,
|
||||
this.stringCallStatus,
|
||||
this.vCID,
|
||||
this.watingtimeInteger});
|
||||
|
||||
ErRequestHistoryList.fromJson(Map<String, dynamic> json) {
|
||||
appointmentNo = json['AppointmentNo'] != null ? json['AppointmentNo'] : "0";
|
||||
arrivalTime = json['ArrivalTime'];
|
||||
callDuration = json['CallDuration'];
|
||||
callStatus = json['CallStatus'];
|
||||
clientRequestID = json['ClientRequestID'];
|
||||
doctorID = json['DoctorID'];
|
||||
doctorName = json['DoctorName'];
|
||||
doctorNameN = json['DoctorNameN'];
|
||||
doctorTitle = json['DoctorTitle'];
|
||||
exWaitingTime = json['Ex_WaitingTime'];
|
||||
isAppointmentHaveRating = json['IsAppointmentHaveRating'];
|
||||
patCount = json['Pat_Count'];
|
||||
projectID = json['ProjectID'];
|
||||
sArrivalTime = json['SArrivalTime'];
|
||||
serviceID = json['ServiceID'];
|
||||
stringCallStatus = json['StringCallStatus'];
|
||||
vCID = json['VC_ID'];
|
||||
watingtimeInteger = json['WatingtimeInteger'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['AppointmentNo'] = this.appointmentNo;
|
||||
data['ArrivalTime'] = this.arrivalTime;
|
||||
data['CallDuration'] = this.callDuration;
|
||||
data['CallStatus'] = this.callStatus;
|
||||
data['ClientRequestID'] = this.clientRequestID;
|
||||
data['DoctorID'] = this.doctorID;
|
||||
data['DoctorName'] = this.doctorName;
|
||||
data['DoctorNameN'] = this.doctorNameN;
|
||||
data['DoctorTitle'] = this.doctorTitle;
|
||||
data['Ex_WaitingTime'] = this.exWaitingTime;
|
||||
data['IsAppointmentHaveRating'] = this.isAppointmentHaveRating;
|
||||
data['Pat_Count'] = this.patCount;
|
||||
data['ProjectID'] = this.projectID;
|
||||
data['SArrivalTime'] = this.sArrivalTime;
|
||||
data['ServiceID'] = this.serviceID;
|
||||
data['StringCallStatus'] = this.stringCallStatus;
|
||||
data['VC_ID'] = this.vCID;
|
||||
data['WatingtimeInteger'] = this.watingtimeInteger;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,122 @@
|
||||
class ClinicsServiceTimingsResponse {
|
||||
List<PatientERGetClinicsServiceTimingsList>
|
||||
patientERGetClinicsServiceTimingsList;
|
||||
|
||||
ClinicsServiceTimingsResponse({this.patientERGetClinicsServiceTimingsList});
|
||||
|
||||
ClinicsServiceTimingsResponse.fromJson(Map<String, dynamic> json) {
|
||||
if (json['PatientER_GetClinicsServiceTimingsList'] != null) {
|
||||
patientERGetClinicsServiceTimingsList =
|
||||
new List<PatientERGetClinicsServiceTimingsList>();
|
||||
json['PatientER_GetClinicsServiceTimingsList'].forEach((v) {
|
||||
patientERGetClinicsServiceTimingsList
|
||||
.add(new PatientERGetClinicsServiceTimingsList.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
if (this.patientERGetClinicsServiceTimingsList != null) {
|
||||
data['PatientER_GetClinicsServiceTimingsList'] = this
|
||||
.patientERGetClinicsServiceTimingsList
|
||||
.map((v) => v.toJson())
|
||||
.toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class PatientERGetClinicsServiceTimingsList {
|
||||
int iD;
|
||||
int serviceID;
|
||||
Null shiftID;
|
||||
int dayOfWeek;
|
||||
String dayOfWeekStr;
|
||||
Null startTime;
|
||||
Null endTime;
|
||||
bool isActive;
|
||||
String createdOn;
|
||||
String createdBy;
|
||||
bool projectOutSA;
|
||||
String dayOfWeekStrN;
|
||||
List<ShiftTimings> shiftTimings;
|
||||
|
||||
PatientERGetClinicsServiceTimingsList(
|
||||
{this.iD,
|
||||
this.serviceID,
|
||||
this.shiftID,
|
||||
this.dayOfWeek,
|
||||
this.dayOfWeekStr,
|
||||
this.startTime,
|
||||
this.endTime,
|
||||
this.isActive,
|
||||
this.createdOn,
|
||||
this.createdBy,
|
||||
this.projectOutSA,
|
||||
this.dayOfWeekStrN,
|
||||
this.shiftTimings});
|
||||
|
||||
PatientERGetClinicsServiceTimingsList.fromJson(Map<String, dynamic> json) {
|
||||
iD = json['ID'];
|
||||
serviceID = json['ServiceID'];
|
||||
shiftID = json['ShiftID'];
|
||||
dayOfWeek = json['DayOfWeek'];
|
||||
dayOfWeekStr = json['DayOfWeekStr'];
|
||||
startTime = json['StartTime'];
|
||||
endTime = json['EndTime'];
|
||||
isActive = json['IsActive'];
|
||||
createdOn = json['CreatedOn'];
|
||||
createdBy = json['CreatedBy'];
|
||||
projectOutSA = json['ProjectOutSA'];
|
||||
dayOfWeekStrN = json['DayOfWeekStrN'];
|
||||
if (json['ShiftTimings'] != null) {
|
||||
shiftTimings = new List<ShiftTimings>();
|
||||
json['ShiftTimings'].forEach((v) {
|
||||
shiftTimings.add(new ShiftTimings.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['ID'] = this.iD;
|
||||
data['ServiceID'] = this.serviceID;
|
||||
data['ShiftID'] = this.shiftID;
|
||||
data['DayOfWeek'] = this.dayOfWeek;
|
||||
data['DayOfWeekStr'] = this.dayOfWeekStr;
|
||||
data['StartTime'] = this.startTime;
|
||||
data['EndTime'] = this.endTime;
|
||||
data['IsActive'] = this.isActive;
|
||||
data['CreatedOn'] = this.createdOn;
|
||||
data['CreatedBy'] = this.createdBy;
|
||||
data['ProjectOutSA'] = this.projectOutSA;
|
||||
data['DayOfWeekStrN'] = this.dayOfWeekStrN;
|
||||
if (this.shiftTimings != null) {
|
||||
data['ShiftTimings'] = this.shiftTimings.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class ShiftTimings {
|
||||
String endTime;
|
||||
int shiftID;
|
||||
String startTime;
|
||||
|
||||
ShiftTimings({this.endTime, this.shiftID, this.startTime});
|
||||
|
||||
ShiftTimings.fromJson(Map<String, dynamic> json) {
|
||||
endTime = json['EndTime'];
|
||||
shiftID = json['ShiftID'];
|
||||
startTime = json['StartTime'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['EndTime'] = this.endTime;
|
||||
data['ShiftID'] = this.shiftID;
|
||||
data['StartTime'] = this.startTime;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
class ERAppointmentFeesResponse {
|
||||
GetERAppointmentFeesList getERAppointmentFeesList;
|
||||
|
||||
ERAppointmentFeesResponse({this.getERAppointmentFeesList});
|
||||
|
||||
ERAppointmentFeesResponse.fromJson(Map<String, dynamic> json) {
|
||||
getERAppointmentFeesList = json['GetERAppointmentFeesList'] != null
|
||||
? new GetERAppointmentFeesList.fromJson(
|
||||
json['GetERAppointmentFeesList'])
|
||||
: null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
if (this.getERAppointmentFeesList != null) {
|
||||
data['GetERAppointmentFeesList'] = this.getERAppointmentFeesList.toJson();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class GetERAppointmentFeesList {
|
||||
String amount;
|
||||
String companyName;
|
||||
bool isInsured;
|
||||
bool isShowInsuranceUpdateModule;
|
||||
String tax;
|
||||
String total;
|
||||
String currency;
|
||||
|
||||
GetERAppointmentFeesList(
|
||||
{this.amount,
|
||||
this.companyName,
|
||||
this.isInsured,
|
||||
this.isShowInsuranceUpdateModule,
|
||||
this.tax,
|
||||
this.total,
|
||||
this.currency});
|
||||
|
||||
GetERAppointmentFeesList.fromJson(Map<String, dynamic> json) {
|
||||
amount = json['Amount'];
|
||||
companyName = json['CompanyName'];
|
||||
isInsured = json['IsInsured'];
|
||||
isShowInsuranceUpdateModule = json['IsShowInsuranceUpdateModule'];
|
||||
tax = json['Tax'];
|
||||
total = json['Total'];
|
||||
currency = json['currency'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['Amount'] = this.amount;
|
||||
data['CompanyName'] = this.companyName;
|
||||
data['IsInsured'] = this.isInsured;
|
||||
data['IsShowInsuranceUpdateModule'] = this.isShowInsuranceUpdateModule;
|
||||
data['Tax'] = this.tax;
|
||||
data['Total'] = this.total;
|
||||
data['currency'] = this.currency;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
class IncomingCallData {
|
||||
String msgID;
|
||||
String notfID;
|
||||
String notificationForeground;
|
||||
String count;
|
||||
String message;
|
||||
String appointmentNo;
|
||||
String title;
|
||||
String projectID;
|
||||
String notificationType;
|
||||
String background;
|
||||
String doctorname;
|
||||
String clinicname;
|
||||
String speciality;
|
||||
String appointmentdate;
|
||||
String appointmenttime;
|
||||
String type;
|
||||
String sessionId;
|
||||
String identity;
|
||||
String name;
|
||||
String videoUrl;
|
||||
String picture;
|
||||
String isCall;
|
||||
String sound;
|
||||
|
||||
IncomingCallData(
|
||||
{this.msgID,
|
||||
this.notfID,
|
||||
this.notificationForeground,
|
||||
this.count,
|
||||
this.message,
|
||||
this.appointmentNo,
|
||||
this.title,
|
||||
this.projectID,
|
||||
this.notificationType,
|
||||
this.background,
|
||||
this.doctorname,
|
||||
this.clinicname,
|
||||
this.speciality,
|
||||
this.appointmentdate,
|
||||
this.appointmenttime,
|
||||
this.type,
|
||||
this.sessionId,
|
||||
this.identity,
|
||||
this.name,
|
||||
this.videoUrl,
|
||||
this.picture,
|
||||
this.isCall,
|
||||
this.sound});
|
||||
|
||||
IncomingCallData.fromJson(Map<String, dynamic> json) {
|
||||
msgID = json['msgID'];
|
||||
notfID = json['notfID'];
|
||||
notificationForeground = json['notification_foreground'];
|
||||
count = json['count'];
|
||||
message = json['message'];
|
||||
appointmentNo = json['AppointmentNo'];
|
||||
title = json['title'];
|
||||
projectID = json['ProjectID'];
|
||||
notificationType = json['NotificationType'];
|
||||
background = json['background'];
|
||||
doctorname = json['doctorname'];
|
||||
clinicname = json['clinicname'];
|
||||
speciality = json['speciality'];
|
||||
appointmentdate = json['appointmentdate'];
|
||||
appointmenttime = json['appointmenttime'];
|
||||
type = json['type'];
|
||||
sessionId = json['session_id'];
|
||||
identity = json['identity'];
|
||||
name = json['name'];
|
||||
videoUrl = json['videoUrl'];
|
||||
picture = json['picture'];
|
||||
isCall = json['is_call'];
|
||||
sound = json['sound'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['msgID'] = this.msgID;
|
||||
data['notfID'] = this.notfID;
|
||||
data['notification_foreground'] = this.notificationForeground;
|
||||
data['count'] = this.count;
|
||||
data['message'] = this.message;
|
||||
data['AppointmentNo'] = this.appointmentNo;
|
||||
data['title'] = this.title;
|
||||
data['ProjectID'] = this.projectID;
|
||||
data['NotificationType'] = this.notificationType;
|
||||
data['background'] = this.background;
|
||||
data['doctorname'] = this.doctorname;
|
||||
data['clinicname'] = this.clinicname;
|
||||
data['speciality'] = this.speciality;
|
||||
data['appointmentdate'] = this.appointmentdate;
|
||||
data['appointmenttime'] = this.appointmenttime;
|
||||
data['type'] = this.type;
|
||||
data['session_id'] = this.sessionId;
|
||||
data['identity'] = this.identity;
|
||||
data['name'] = this.name;
|
||||
data['videoUrl'] = this.videoUrl;
|
||||
data['picture'] = this.picture;
|
||||
data['is_call'] = this.isCall;
|
||||
data['sound'] = this.sound;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
class LiveCareClinicsListResponse {
|
||||
List<PatientERGetClinicsList> patientERGetClinicsList;
|
||||
|
||||
LiveCareClinicsListResponse({this.patientERGetClinicsList});
|
||||
|
||||
LiveCareClinicsListResponse.fromJson(Map<String, dynamic> json) {
|
||||
if (json['PatientER_GetClinicsList'] != null) {
|
||||
patientERGetClinicsList = new List<PatientERGetClinicsList>();
|
||||
json['PatientER_GetClinicsList'].forEach((v) {
|
||||
patientERGetClinicsList.add(new PatientERGetClinicsList.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
if (this.patientERGetClinicsList != null) {
|
||||
data['PatientER_GetClinicsList'] =
|
||||
this.patientERGetClinicsList.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class PatientERGetClinicsList {
|
||||
int iD;
|
||||
int serviceID;
|
||||
String serviceName;
|
||||
String serviceNameN;
|
||||
int clinicID;
|
||||
int age;
|
||||
bool isCheckAgeBelow;
|
||||
int gender;
|
||||
bool isActive;
|
||||
String createdOn;
|
||||
String createdBy;
|
||||
int isOnline;
|
||||
Null endTime;
|
||||
bool projectOutSA;
|
||||
List<ShiftTimings> shiftTimings;
|
||||
Null startTime;
|
||||
|
||||
PatientERGetClinicsList(
|
||||
{this.iD,
|
||||
this.serviceID,
|
||||
this.serviceName,
|
||||
this.serviceNameN,
|
||||
this.clinicID,
|
||||
this.age,
|
||||
this.isCheckAgeBelow,
|
||||
this.gender,
|
||||
this.isActive,
|
||||
this.createdOn,
|
||||
this.createdBy,
|
||||
this.isOnline,
|
||||
this.endTime,
|
||||
this.projectOutSA,
|
||||
this.shiftTimings,
|
||||
this.startTime});
|
||||
|
||||
PatientERGetClinicsList.fromJson(Map<String, dynamic> json) {
|
||||
iD = json['ID'];
|
||||
serviceID = json['ServiceID'];
|
||||
serviceName = json['ServiceName'];
|
||||
serviceNameN = json['ServiceNameN'];
|
||||
clinicID = json['ClinicID'];
|
||||
age = json['Age'];
|
||||
isCheckAgeBelow = json['IsCheckAgeBelow'];
|
||||
gender = json['Gender'];
|
||||
isActive = json['IsActive'];
|
||||
createdOn = json['CreatedOn'];
|
||||
createdBy = json['CreatedBy'];
|
||||
isOnline = json['IsOnline'];
|
||||
endTime = json['EndTime'];
|
||||
projectOutSA = json['ProjectOutSA'];
|
||||
if (json['ShiftTimings'] != null) {
|
||||
shiftTimings = new List<ShiftTimings>();
|
||||
json['ShiftTimings'].forEach((v) {
|
||||
shiftTimings.add(new ShiftTimings.fromJson(v));
|
||||
});
|
||||
}
|
||||
startTime = json['StartTime'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['ID'] = this.iD;
|
||||
data['ServiceID'] = this.serviceID;
|
||||
data['ServiceName'] = this.serviceName;
|
||||
data['ServiceNameN'] = this.serviceNameN;
|
||||
data['ClinicID'] = this.clinicID;
|
||||
data['Age'] = this.age;
|
||||
data['IsCheckAgeBelow'] = this.isCheckAgeBelow;
|
||||
data['Gender'] = this.gender;
|
||||
data['IsActive'] = this.isActive;
|
||||
data['CreatedOn'] = this.createdOn;
|
||||
data['CreatedBy'] = this.createdBy;
|
||||
data['IsOnline'] = this.isOnline;
|
||||
data['EndTime'] = this.endTime;
|
||||
data['ProjectOutSA'] = this.projectOutSA;
|
||||
if (this.shiftTimings != null) {
|
||||
data['ShiftTimings'] = this.shiftTimings.map((v) => v.toJson()).toList();
|
||||
}
|
||||
data['StartTime'] = this.startTime;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class ShiftTimings {
|
||||
String endTime;
|
||||
int shiftID;
|
||||
String startTime;
|
||||
|
||||
ShiftTimings({this.endTime, this.shiftID, this.startTime});
|
||||
|
||||
ShiftTimings.fromJson(Map<String, dynamic> json) {
|
||||
endTime = json['EndTime'];
|
||||
shiftID = json['ShiftID'];
|
||||
startTime = json['StartTime'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['EndTime'] = this.endTime;
|
||||
data['ShiftID'] = this.shiftID;
|
||||
data['StartTime'] = this.startTime;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/room_validators.dart';
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/twilio_enums.dart';
|
||||
|
||||
class RoomModel with RoomValidators {
|
||||
final String name;
|
||||
final bool isLoading;
|
||||
final bool isSubmitted;
|
||||
final String token;
|
||||
final String identity;
|
||||
final TwilioRoomType type;
|
||||
|
||||
RoomModel({
|
||||
this.name,
|
||||
this.isLoading = false,
|
||||
this.isSubmitted = false,
|
||||
this.token,
|
||||
this.identity,
|
||||
this.type = TwilioRoomType.groupSmall,
|
||||
});
|
||||
|
||||
static String getTypeText(TwilioRoomType type) {
|
||||
switch (type) {
|
||||
case TwilioRoomType.peerToPeer:
|
||||
return 'peer 2 peer';
|
||||
break;
|
||||
case TwilioRoomType.group:
|
||||
return 'large (max 50 participants)';
|
||||
break;
|
||||
case TwilioRoomType.groupSmall:
|
||||
return 'small (max 4 participants)';
|
||||
break;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
String get nameErrorText {
|
||||
return isSubmitted && !nameValidator.isValid(name) ? invalidNameErrorText : null;
|
||||
}
|
||||
|
||||
String get typeText {
|
||||
return RoomModel.getTypeText(type);
|
||||
}
|
||||
|
||||
bool get canSubmit {
|
||||
return nameValidator.isValid(name);
|
||||
}
|
||||
|
||||
RoomModel copyWith({
|
||||
String name,
|
||||
bool isLoading,
|
||||
bool isSubmitted,
|
||||
String token,
|
||||
String identity,
|
||||
TwilioRoomType type,
|
||||
}) {
|
||||
return RoomModel(
|
||||
name: name ?? this.name,
|
||||
token: token ?? this.token,
|
||||
identity: identity ?? this.identity,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
isSubmitted: isSubmitted ?? this.isSubmitted,
|
||||
type: type ?? this.type,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/validators.dart';
|
||||
|
||||
mixin RoomValidators {
|
||||
final StringValidator nameValidator = NonEmptyStringValidator();
|
||||
final String invalidNameErrorText = 'Room name can\'t be empty';
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
enum TwilioRoomType {
|
||||
peerToPeer,
|
||||
group,
|
||||
groupSmall,
|
||||
}
|
||||
|
||||
enum TwilioRoomStatus {
|
||||
completed,
|
||||
inProgress,
|
||||
}
|
||||
|
||||
enum TwilioStatusCallbackMethod {
|
||||
GET,
|
||||
POST,
|
||||
}
|
||||
|
||||
enum TwilioVideoCodec { VP8, H264 }
|
||||
@ -0,0 +1,13 @@
|
||||
abstract class StringValidator {
|
||||
bool isValid(String value);
|
||||
}
|
||||
|
||||
class NonEmptyStringValidator implements StringValidator {
|
||||
@override
|
||||
bool isValid(String value) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
return value.isNotEmpty;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ClippedVideo extends StatefulWidget {
|
||||
final double width;
|
||||
final double height;
|
||||
final Widget child;
|
||||
|
||||
const ClippedVideo({
|
||||
Key key,
|
||||
@required this.width,
|
||||
@required this.height,
|
||||
@required this.child,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ClippedVideoState createState() => _ClippedVideoState();
|
||||
}
|
||||
|
||||
class _ClippedVideoState extends State<ClippedVideo> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
border: Border.all(
|
||||
color: Colors.white24,
|
||||
),
|
||||
),
|
||||
child: ClipRRect(
|
||||
child: widget.child,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,238 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:after_layout/after_layout.dart';
|
||||
import 'package:diplomaticquarterapp/pages/conference/widgets/circle_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ConferenceButtonBar extends StatefulWidget {
|
||||
final VoidCallback onVideoEnabled;
|
||||
final VoidCallback onAudioEnabled;
|
||||
final VoidCallback onHangup;
|
||||
final VoidCallback onSwitchCamera;
|
||||
final VoidCallback onPersonAdd;
|
||||
final VoidCallback onPersonRemove;
|
||||
final void Function(double) onHeight;
|
||||
final VoidCallback onHide;
|
||||
final VoidCallback onShow;
|
||||
final Stream<bool> videoEnabled;
|
||||
final Stream<bool> audioEnabled;
|
||||
|
||||
const ConferenceButtonBar({
|
||||
Key key,
|
||||
this.onVideoEnabled,
|
||||
this.onAudioEnabled,
|
||||
this.onHangup,
|
||||
this.onSwitchCamera,
|
||||
this.onPersonAdd,
|
||||
this.onPersonRemove,
|
||||
@required this.videoEnabled,
|
||||
@required this.audioEnabled,
|
||||
this.onHeight,
|
||||
this.onHide,
|
||||
this.onShow,
|
||||
}) : assert(videoEnabled != null),
|
||||
assert(audioEnabled != null),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
_ConferenceButtonBarState createState() => _ConferenceButtonBarState();
|
||||
}
|
||||
|
||||
class _ConferenceButtonBarState extends State<ConferenceButtonBar> with AfterLayoutMixin<ConferenceButtonBar> {
|
||||
var _bottom = -100.0;
|
||||
Timer _timer;
|
||||
int _remaining;
|
||||
var _videoEnabled = true;
|
||||
var _audioEnabled = true;
|
||||
double _hidden;
|
||||
double _visible;
|
||||
final _keyButtonBarHeight = GlobalKey();
|
||||
|
||||
final Duration timeout = const Duration(seconds: 5);
|
||||
final Duration ms = const Duration(milliseconds: 1);
|
||||
final Duration periodicDuration = const Duration(milliseconds: 100);
|
||||
|
||||
Timer startTimeout([int milliseconds]) {
|
||||
final duration = milliseconds == null ? timeout : ms * milliseconds;
|
||||
_remaining = duration.inMilliseconds;
|
||||
return Timer.periodic(periodicDuration, (Timer timer) {
|
||||
_remaining -= periodicDuration.inMilliseconds;
|
||||
if (_remaining <= 0) {
|
||||
timer.cancel();
|
||||
_toggleBar();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _pauseTimer() {
|
||||
if (_timer == null) {
|
||||
return;
|
||||
}
|
||||
_timer.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
void _resumeTimer() {
|
||||
// resume the timer only when there is no timer active or when
|
||||
// the bar is not already hidden.
|
||||
if ((_timer != null && _timer.isActive) || _bottom == _hidden) {
|
||||
return;
|
||||
}
|
||||
_timer = startTimeout(_remaining);
|
||||
}
|
||||
|
||||
void _toggleBar() {
|
||||
setState(() {
|
||||
_bottom = _bottom == _visible ? _hidden : _visible;
|
||||
if (_bottom == _visible && widget.onShow != null) {
|
||||
widget.onShow();
|
||||
}
|
||||
if (_bottom == _hidden && widget.onHide != null) {
|
||||
widget.onHide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _toggleBarOnEnd() {
|
||||
if (_timer != null) {
|
||||
if (_timer.isActive) {
|
||||
_timer.cancel();
|
||||
}
|
||||
_timer = null;
|
||||
}
|
||||
if (_bottom == 0) {
|
||||
_timer = startTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_timer = startTimeout();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
_visible = MediaQuery.of(context).viewPadding.bottom;
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
void afterFirstLayout(BuildContext context) {
|
||||
final RenderBox renderBoxButtonBar = _keyButtonBarHeight.currentContext.findRenderObject();
|
||||
final heightButtonBar = renderBoxButtonBar.size.height;
|
||||
// Because the `didChangeDependencies` fires before the `afterFirstLayout`, we can use the `_visible` property here.
|
||||
_hidden = -(heightButtonBar + _visible);
|
||||
widget.onHeight(heightButtonBar);
|
||||
_toggleBar();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
if (_timer != null && _timer.isActive) {
|
||||
_timer.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: GestureDetector(
|
||||
key: Key('show-hide-button-bar-gesture'),
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTapDown: (_) => _pauseTimer(),
|
||||
onTapUp: (_) => _toggleBar(),
|
||||
onTapCancel: () => _resumeTimer(),
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
AnimatedPositioned(
|
||||
key: Key('button-bar'),
|
||||
bottom: _bottom,
|
||||
left: 0,
|
||||
right: 0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.linear,
|
||||
child: _buildRow(context),
|
||||
onEnd: _toggleBarOnEnd,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onPressed(VoidCallback callback) {
|
||||
if (callback != null) {
|
||||
callback();
|
||||
}
|
||||
if (_timer != null && _timer.isActive) {
|
||||
_timer.cancel();
|
||||
}
|
||||
_timer = startTimeout();
|
||||
}
|
||||
|
||||
Widget _buildRow(BuildContext context) {
|
||||
return Padding(
|
||||
key: _keyButtonBarHeight,
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
CircleButton(
|
||||
child: StreamBuilder<bool>(
|
||||
stream: widget.videoEnabled,
|
||||
initialData: _videoEnabled,
|
||||
builder: (context, snapshot) {
|
||||
_videoEnabled = snapshot.data;
|
||||
return Icon(
|
||||
_videoEnabled ? Icons.videocam : Icons.videocam_off,
|
||||
color: Colors.white,
|
||||
);
|
||||
}),
|
||||
key: Key('camera-button'),
|
||||
onPressed: () => _onPressed(widget.onVideoEnabled),
|
||||
),
|
||||
CircleButton(
|
||||
child: StreamBuilder<bool>(
|
||||
stream: widget.audioEnabled,
|
||||
initialData: _audioEnabled,
|
||||
builder: (context, snapshot) {
|
||||
_audioEnabled = snapshot.data;
|
||||
return Icon(
|
||||
_audioEnabled ? Icons.mic : Icons.mic_off,
|
||||
color: Colors.white,
|
||||
);
|
||||
}),
|
||||
key: Key('microphone-button'),
|
||||
onPressed: () => _onPressed(widget.onAudioEnabled),
|
||||
),
|
||||
CircleButton(
|
||||
radius: 35,
|
||||
child: const RotationTransition(
|
||||
turns: AlwaysStoppedAnimation<double>(135 / 360),
|
||||
child: Icon(
|
||||
Icons.phone,
|
||||
color: Colors.white,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
color: Colors.red.withAlpha(200),
|
||||
key: Key('hangup-button'),
|
||||
onPressed: () => _onPressed(widget.onHangup),
|
||||
),
|
||||
CircleButton(
|
||||
child: const Icon(Icons.switch_camera, color: Colors.white),
|
||||
key: Key('switch-camera-button'),
|
||||
onPressed: () => _onPressed(widget.onSwitchCamera),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,388 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/room_model.dart';
|
||||
import 'package:diplomaticquarterapp/pages/conference/conference_button_bar.dart';
|
||||
import 'package:diplomaticquarterapp/pages/conference/conference_room.dart';
|
||||
import 'package:diplomaticquarterapp/pages/conference/draggable_publisher.dart';
|
||||
import 'package:diplomaticquarterapp/pages/conference/participant_widget.dart';
|
||||
import 'package:diplomaticquarterapp/pages/conference/widgets/noise_box.dart';
|
||||
import 'package:diplomaticquarterapp/pages/conference/widgets/platform_alert_dialog.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
|
||||
class ConferencePage extends StatefulWidget {
|
||||
final RoomModel roomModel;
|
||||
|
||||
const ConferencePage({Key key, this.roomModel}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ConferencePageState createState() => _ConferencePageState();
|
||||
}
|
||||
|
||||
class _ConferencePageState extends State<ConferencePage> {
|
||||
final StreamController<bool> _onButtonBarVisibleStreamController = StreamController<bool>.broadcast();
|
||||
final StreamController<double> _onButtonBarHeightStreamController = StreamController<double>.broadcast();
|
||||
ConferenceRoom _conferenceRoom;
|
||||
StreamSubscription _onConferenceRoomException;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_lockInPortrait();
|
||||
_connectToRoom();
|
||||
_wakeLock(true);
|
||||
}
|
||||
|
||||
void _connectToRoom() async {
|
||||
try {
|
||||
final conferenceRoom = ConferenceRoom(
|
||||
name: widget.roomModel.name,
|
||||
token: widget.roomModel.token,
|
||||
identity: widget.roomModel.identity,
|
||||
);
|
||||
await conferenceRoom.connect();
|
||||
setState(() {
|
||||
_conferenceRoom = conferenceRoom;
|
||||
_onConferenceRoomException = _conferenceRoom.onException.listen((err) async {
|
||||
await PlatformAlertDialog(
|
||||
title: err is PlatformException ? err.message : 'An error occured',
|
||||
content: err is PlatformException ? err.details : err.toString(),
|
||||
defaultActionText: 'OK',
|
||||
).show(context);
|
||||
});
|
||||
_conferenceRoom.addListener(_conferenceRoomUpdated);
|
||||
});
|
||||
} catch (err) {
|
||||
print(err);
|
||||
await PlatformAlertDialog(
|
||||
title: err is PlatformException ? err.message : 'An error occured',
|
||||
content: err is PlatformException ? err.details : err.toString(),
|
||||
defaultActionText: 'OK',
|
||||
).show(context);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _lockInPortrait() async {
|
||||
await SystemChrome.setPreferredOrientations(<DeviceOrientation>[
|
||||
DeviceOrientation.portraitUp,
|
||||
DeviceOrientation.portraitDown,
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_freePortraitLock();
|
||||
_wakeLock(false);
|
||||
_disposeStreamsAndSubscriptions();
|
||||
if (_conferenceRoom != null) _conferenceRoom.removeListener(_conferenceRoomUpdated);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _freePortraitLock() async {
|
||||
await SystemChrome.setPreferredOrientations(<DeviceOrientation>[
|
||||
DeviceOrientation.landscapeRight,
|
||||
DeviceOrientation.landscapeLeft,
|
||||
DeviceOrientation.portraitUp,
|
||||
DeviceOrientation.portraitDown,
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> _disposeStreamsAndSubscriptions() async {
|
||||
if (_onButtonBarVisibleStreamController != null) await _onButtonBarVisibleStreamController.close();
|
||||
if (_onButtonBarHeightStreamController != null) await _onButtonBarHeightStreamController.close();
|
||||
if (_onConferenceRoomException != null) await _onConferenceRoomException.cancel();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async => false,
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
body: _conferenceRoom == null ? showProgress() : buildLayout(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
LayoutBuilder buildLayout() {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
_buildParticipants(context, constraints.biggest, _conferenceRoom),
|
||||
ConferenceButtonBar(
|
||||
audioEnabled: _conferenceRoom.onAudioEnabled,
|
||||
videoEnabled: _conferenceRoom.onVideoEnabled,
|
||||
onAudioEnabled: _conferenceRoom.toggleAudioEnabled,
|
||||
onVideoEnabled: _conferenceRoom.toggleVideoEnabled,
|
||||
onHangup: _onHangup,
|
||||
onSwitchCamera: _conferenceRoom.switchCamera,
|
||||
onPersonAdd: _onPersonAdd,
|
||||
onPersonRemove: _onPersonRemove,
|
||||
onHeight: _onHeightBar,
|
||||
onShow: _onShowBar,
|
||||
onHide: _onHideBar,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget showProgress() {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Center(child: CircularProgressIndicator()),
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Text(
|
||||
'Connecting to the call...',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onHangup() async {
|
||||
print('onHangup');
|
||||
await _conferenceRoom.disconnect();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
void _onPersonAdd() {
|
||||
print('onPersonAdd');
|
||||
try {
|
||||
_conferenceRoom.addDummy(
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
const Placeholder(),
|
||||
Center(
|
||||
child: Text(
|
||||
(_conferenceRoom.participants.length + 1).toString(),
|
||||
style: const TextStyle(
|
||||
shadows: <Shadow>[
|
||||
Shadow(
|
||||
blurRadius: 3.0,
|
||||
color: Color.fromARGB(255, 0, 0, 0),
|
||||
),
|
||||
Shadow(
|
||||
blurRadius: 8.0,
|
||||
color: Color.fromARGB(255, 255, 255, 255),
|
||||
),
|
||||
],
|
||||
fontSize: 80,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} on PlatformException catch (err) {
|
||||
PlatformAlertDialog(
|
||||
title: err.message,
|
||||
content: err.details,
|
||||
defaultActionText: 'OK',
|
||||
).show(context);
|
||||
}
|
||||
}
|
||||
|
||||
void _onPersonRemove() {
|
||||
print('onPersonRemove');
|
||||
_conferenceRoom.removeDummy();
|
||||
}
|
||||
|
||||
Widget _buildParticipants(BuildContext context, Size size, ConferenceRoom conferenceRoom) {
|
||||
final children = <Widget>[];
|
||||
final length = conferenceRoom.participants.length;
|
||||
|
||||
if (length <= 2) {
|
||||
_buildOverlayLayout(context, size, children);
|
||||
return Stack(children: children);
|
||||
}
|
||||
|
||||
void buildInCols(bool removeLocalBeforeChunking, bool moveLastOfEachRowToNextRow, int columns) {
|
||||
_buildLayoutInGrid(
|
||||
context,
|
||||
size,
|
||||
children,
|
||||
removeLocalBeforeChunking: removeLocalBeforeChunking,
|
||||
moveLastOfEachRowToNextRow: moveLastOfEachRowToNextRow,
|
||||
columns: columns,
|
||||
);
|
||||
}
|
||||
|
||||
if (length <= 3) {
|
||||
buildInCols(true, false, 1);
|
||||
} else if (length == 5) {
|
||||
buildInCols(false, true, 2);
|
||||
} else if (length <= 6 || length == 8) {
|
||||
buildInCols(false, false, 2);
|
||||
} else if (length == 7 || length == 9) {
|
||||
buildInCols(true, false, 2);
|
||||
} else if (length == 10) {
|
||||
buildInCols(false, true, 3);
|
||||
} else if (length == 13 || length == 16) {
|
||||
buildInCols(true, false, 3);
|
||||
} else if (length <= 18) {
|
||||
buildInCols(false, false, 3);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
|
||||
void _buildOverlayLayout(BuildContext context, Size size, List<Widget> children) {
|
||||
final participants = _conferenceRoom.participants;
|
||||
if (participants.length == 1) {
|
||||
children.add(_buildNoiseBox());
|
||||
} else {
|
||||
final remoteParticipant = participants.firstWhere((ParticipantWidget participant) => participant.isRemote, orElse: () => null);
|
||||
if (remoteParticipant != null) {
|
||||
children.add(remoteParticipant);
|
||||
}
|
||||
}
|
||||
|
||||
final localParticipant = participants.firstWhere((ParticipantWidget participant) => !participant.isRemote, orElse: () => null);
|
||||
if (localParticipant != null) {
|
||||
children.add(DraggablePublisher(
|
||||
key: Key('publisher'),
|
||||
child: localParticipant,
|
||||
availableScreenSize: size,
|
||||
onButtonBarVisible: _onButtonBarVisibleStreamController.stream,
|
||||
onButtonBarHeight: _onButtonBarHeightStreamController.stream,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void _buildLayoutInGrid(
|
||||
BuildContext context,
|
||||
Size size,
|
||||
List<Widget> children, {
|
||||
bool removeLocalBeforeChunking = false,
|
||||
bool moveLastOfEachRowToNextRow = false,
|
||||
int columns = 2,
|
||||
}) {
|
||||
final participants = _conferenceRoom.participants;
|
||||
ParticipantWidget localParticipant;
|
||||
if (removeLocalBeforeChunking) {
|
||||
localParticipant = participants.firstWhere((ParticipantWidget participant) => !participant.isRemote, orElse: () => null);
|
||||
if (localParticipant != null) {
|
||||
participants.remove(localParticipant);
|
||||
}
|
||||
}
|
||||
final chunkedParticipants = chunk(array: participants, size: columns);
|
||||
if (localParticipant != null) {
|
||||
chunkedParticipants.last.add(localParticipant);
|
||||
participants.add(localParticipant);
|
||||
}
|
||||
|
||||
if (moveLastOfEachRowToNextRow) {
|
||||
for (var i = 0; i < chunkedParticipants.length - 1; i++) {
|
||||
var participant = chunkedParticipants[i].removeLast();
|
||||
chunkedParticipants[i + 1].insert(0, participant);
|
||||
}
|
||||
}
|
||||
|
||||
for (final participantChunk in chunkedParticipants) {
|
||||
final rowChildren = <Widget>[];
|
||||
for (final participant in participantChunk) {
|
||||
rowChildren.add(
|
||||
Container(
|
||||
width: size.width / participantChunk.length,
|
||||
height: size.height / chunkedParticipants.length,
|
||||
child: participant,
|
||||
),
|
||||
);
|
||||
}
|
||||
children.add(
|
||||
Container(
|
||||
height: size.height / chunkedParticipants.length,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: rowChildren,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NoiseBox _buildNoiseBox() {
|
||||
return NoiseBox(
|
||||
density: NoiseBoxDensity.xLow,
|
||||
backgroundColor: Colors.grey.shade900,
|
||||
child: Center(
|
||||
child: Container(
|
||||
color: Colors.black54,
|
||||
width: double.infinity,
|
||||
height: 40,
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Waiting for another participant to connect to the call...',
|
||||
key: Key('text-wait'),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<List<T>> chunk<T>({@required List<T> array, @required int size}) {
|
||||
final result = <List<T>>[];
|
||||
if (array.isEmpty || size <= 0) {
|
||||
return result;
|
||||
}
|
||||
var first = 0;
|
||||
var last = size;
|
||||
final totalLoop = array.length % size == 0 ? array.length ~/ size : array.length ~/ size + 1;
|
||||
for (var i = 0; i < totalLoop; i++) {
|
||||
if (last > array.length) {
|
||||
result.add(array.sublist(first, array.length));
|
||||
} else {
|
||||
result.add(array.sublist(first, last));
|
||||
}
|
||||
first = last;
|
||||
last = last + size;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void _onHeightBar(double height) {
|
||||
_onButtonBarHeightStreamController.add(height);
|
||||
}
|
||||
|
||||
void _onShowBar() {
|
||||
setState(() {
|
||||
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom, SystemUiOverlay.top]);
|
||||
});
|
||||
_onButtonBarVisibleStreamController.add(true);
|
||||
}
|
||||
|
||||
void _onHideBar() {
|
||||
setState(() {
|
||||
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]);
|
||||
});
|
||||
_onButtonBarVisibleStreamController.add(false);
|
||||
}
|
||||
|
||||
Future<void> _wakeLock(bool enable) async {
|
||||
try {
|
||||
return await (enable ? Wakelock.enable() : Wakelock.disable());
|
||||
} catch (err) {
|
||||
print('Unable to change the Wakelock and set it to $enable');
|
||||
print(err);
|
||||
}
|
||||
}
|
||||
|
||||
void _conferenceRoomUpdated() {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,530 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:diplomaticquarterapp/pages/conference/participant_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:twilio_programmable_video/twilio_programmable_video.dart';
|
||||
|
||||
class ConferenceRoom with ChangeNotifier {
|
||||
final String name;
|
||||
final String token;
|
||||
final String identity;
|
||||
|
||||
final StreamController<bool> _onAudioEnabledStreamController = StreamController<bool>.broadcast();
|
||||
Stream<bool> onAudioEnabled;
|
||||
final StreamController<bool> _onVideoEnabledStreamController = StreamController<bool>.broadcast();
|
||||
Stream<bool> onVideoEnabled;
|
||||
final StreamController<Exception> _onExceptionStreamController = StreamController<Exception>.broadcast();
|
||||
Stream<Exception> onException;
|
||||
|
||||
final Completer<Room> _completer = Completer<Room>();
|
||||
|
||||
final List<ParticipantWidget> _participants = [];
|
||||
final List<ParticipantBuffer> _participantBuffer = [];
|
||||
final List<StreamSubscription> _streamSubscriptions = [];
|
||||
final List<RemoteDataTrack> _dataTracks = [];
|
||||
final List<String> _messages = [];
|
||||
|
||||
CameraCapturer _cameraCapturer;
|
||||
Room _room;
|
||||
Timer _timer;
|
||||
|
||||
ConferenceRoom({
|
||||
@required this.name,
|
||||
@required this.token,
|
||||
@required this.identity,
|
||||
}) {
|
||||
onAudioEnabled = _onAudioEnabledStreamController.stream;
|
||||
onVideoEnabled = _onVideoEnabledStreamController.stream;
|
||||
onException = _onExceptionStreamController.stream;
|
||||
}
|
||||
|
||||
List<ParticipantWidget> get participants {
|
||||
return [..._participants];
|
||||
}
|
||||
|
||||
Future<Room> connect() async {
|
||||
print('ConferenceRoom.connect()');
|
||||
try {
|
||||
await TwilioProgrammableVideo.debug(dart: true, native: true);
|
||||
await TwilioProgrammableVideo.setSpeakerphoneOn(true);
|
||||
|
||||
_cameraCapturer = CameraCapturer(CameraSource.FRONT_CAMERA);
|
||||
var connectOptions = ConnectOptions(
|
||||
token,
|
||||
roomName: name,
|
||||
preferredAudioCodecs: [OpusCodec()],
|
||||
audioTracks: [LocalAudioTrack(true)],
|
||||
dataTracks: [LocalDataTrack()],
|
||||
videoTracks: [LocalVideoTrack(true, _cameraCapturer)],
|
||||
enableDominantSpeaker: true,
|
||||
);
|
||||
|
||||
_room = await TwilioProgrammableVideo.connect(connectOptions);
|
||||
|
||||
_streamSubscriptions.add(_room.onConnected.listen(_onConnected));
|
||||
_streamSubscriptions.add(_room.onConnectFailure.listen(_onConnectFailure));
|
||||
|
||||
return _completer.future;
|
||||
} catch (err) {
|
||||
print(err);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> disconnect() async {
|
||||
print('ConferenceRoom.disconnect()');
|
||||
if (_timer != null) {
|
||||
_timer.cancel();
|
||||
}
|
||||
await _room.disconnect();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
print('ConferenceRoom.dispose()');
|
||||
_disposeStreamsAndSubscriptions();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _disposeStreamsAndSubscriptions() async {
|
||||
await _onAudioEnabledStreamController.close();
|
||||
await _onVideoEnabledStreamController.close();
|
||||
await _onExceptionStreamController.close();
|
||||
for (var streamSubscription in _streamSubscriptions) {
|
||||
await streamSubscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> sendMessage(String message) async {
|
||||
final tracks = _room.localParticipant.localDataTracks;
|
||||
final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack;
|
||||
if (localDataTrack == null || _messages.isNotEmpty) {
|
||||
print('ConferenceRoom.sendMessage => Track is not available yet, buffering message.');
|
||||
_messages.add(message);
|
||||
return;
|
||||
}
|
||||
await localDataTrack.send(message);
|
||||
}
|
||||
|
||||
Future<void> sendBufferMessage(ByteBuffer message) async {
|
||||
final tracks = _room.localParticipant.localDataTracks;
|
||||
final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack;
|
||||
if (localDataTrack == null) {
|
||||
return;
|
||||
}
|
||||
await localDataTrack.sendBuffer(message);
|
||||
}
|
||||
|
||||
Future<void> toggleVideoEnabled() async {
|
||||
final tracks = _room.localParticipant.localVideoTracks;
|
||||
final localVideoTrack = tracks.isEmpty ? null : tracks[0].localVideoTrack;
|
||||
if (localVideoTrack == null) {
|
||||
print('ConferenceRoom.toggleVideoEnabled() => Track is not available yet!');
|
||||
return;
|
||||
}
|
||||
await localVideoTrack.enable(!localVideoTrack.isEnabled);
|
||||
|
||||
var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
var participant = _participants[index];
|
||||
_participants.replaceRange(
|
||||
index,
|
||||
index + 1,
|
||||
[
|
||||
participant.copyWith(videoEnabled: localVideoTrack.isEnabled),
|
||||
],
|
||||
);
|
||||
print('ConferenceRoom.toggleVideoEnabled() => ${localVideoTrack.isEnabled}');
|
||||
_onVideoEnabledStreamController.add(localVideoTrack.isEnabled);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> toggleAudioEnabled() async {
|
||||
final tracks = _room.localParticipant.localAudioTracks;
|
||||
final localAudioTrack = tracks.isEmpty ? null : tracks[0].localAudioTrack;
|
||||
if (localAudioTrack == null) {
|
||||
print('ConferenceRoom.toggleAudioEnabled() => Track is not available yet!');
|
||||
return;
|
||||
}
|
||||
await localAudioTrack.enable(!localAudioTrack.isEnabled);
|
||||
|
||||
var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
var participant = _participants[index];
|
||||
_participants.replaceRange(
|
||||
index,
|
||||
index + 1,
|
||||
[
|
||||
participant.copyWith(audioEnabled: localAudioTrack.isEnabled),
|
||||
],
|
||||
);
|
||||
print('ConferenceRoom.toggleAudioEnabled() => ${localAudioTrack.isEnabled}');
|
||||
_onAudioEnabledStreamController.add(localAudioTrack.isEnabled);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> switchCamera() async {
|
||||
print('ConferenceRoom.switchCamera()');
|
||||
try {
|
||||
await _cameraCapturer.switchCamera();
|
||||
} on FormatException catch (e) {
|
||||
print(
|
||||
'ConferenceRoom.switchCamera() failed because of FormatException with message: ${e.message}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void addDummy({Widget child}) {
|
||||
print('ConferenceRoom.addDummy()');
|
||||
if (_participants.length >= 18) {
|
||||
throw PlatformException(
|
||||
code: 'ConferenceRoom.maximumReached',
|
||||
message: 'Maximum reached',
|
||||
details: 'Currently the lay-out can only render a maximum of 18 participants',
|
||||
);
|
||||
}
|
||||
_participants.insert(
|
||||
0,
|
||||
ParticipantWidget(
|
||||
id: (_participants.length + 1).toString(),
|
||||
child: child,
|
||||
isRemote: true,
|
||||
audioEnabled: true,
|
||||
videoEnabled: true,
|
||||
isDummy: true,
|
||||
),
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void removeDummy() {
|
||||
print('ConferenceRoom.removeDummy()');
|
||||
var dummy = _participants.firstWhere((participant) => participant.isDummy, orElse: () => null);
|
||||
if (dummy != null) {
|
||||
_participants.remove(dummy);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void _onConnected(Room room) {
|
||||
print('ConferenceRoom._onConnected => state: ${room.state}');
|
||||
|
||||
// When connected for the first time, add remote participant listeners
|
||||
_streamSubscriptions.add(_room.onParticipantConnected.listen(_onParticipantConnected));
|
||||
_streamSubscriptions.add(_room.onParticipantDisconnected.listen(_onParticipantDisconnected));
|
||||
_streamSubscriptions.add(_room.onDominantSpeakerChange.listen(_onDominantSpeakerChanged));
|
||||
// Only add ourselves when connected for the first time too.
|
||||
_participants.add(
|
||||
_buildParticipant(
|
||||
child: room.localParticipant.localVideoTracks[0].localVideoTrack.widget(),
|
||||
id: identity,
|
||||
audioEnabled: true,
|
||||
videoEnabled: true,
|
||||
),
|
||||
);
|
||||
for (final remoteParticipant in room.remoteParticipants) {
|
||||
var participant = _participants.firstWhere((participant) => participant.id == remoteParticipant.sid, orElse: () => null);
|
||||
if (participant == null) {
|
||||
print('Adding participant that was already present in the room ${remoteParticipant.sid}, before I connected');
|
||||
_addRemoteParticipantListeners(remoteParticipant);
|
||||
}
|
||||
}
|
||||
// We have to listen for the [onDataTrackPublished] event on the [LocalParticipant] in
|
||||
// order to be able to use the [send] method.
|
||||
_streamSubscriptions.add(room.localParticipant.onDataTrackPublished.listen(_onLocalDataTrackPublished));
|
||||
notifyListeners();
|
||||
_completer.complete(room);
|
||||
|
||||
_timer = Timer.periodic(const Duration(minutes: 1), (_) {
|
||||
// Let's see if we can send some data over the DataTrack API
|
||||
sendMessage('And another minute has passed since I connected...');
|
||||
// Also try the ByteBuffer way of sending data
|
||||
final list = 'This data has been sent over the ByteBuffer channel of the DataTrack API'.codeUnits;
|
||||
var bytes = Uint8List.fromList(list);
|
||||
sendBufferMessage(bytes.buffer);
|
||||
});
|
||||
}
|
||||
|
||||
void _onLocalDataTrackPublished(LocalDataTrackPublishedEvent event) {
|
||||
// Send buffered messages, if any...
|
||||
while (_messages.isNotEmpty) {
|
||||
var message = _messages.removeAt(0);
|
||||
print('Sending buffered message: $message');
|
||||
event.localDataTrackPublication.localDataTrack.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
void _onConnectFailure(RoomConnectFailureEvent event) {
|
||||
print('ConferenceRoom._onConnectFailure: ${event.exception}');
|
||||
_completer.completeError(event.exception);
|
||||
}
|
||||
|
||||
void _onDominantSpeakerChanged(DominantSpeakerChangedEvent event) {
|
||||
print('ConferenceRoom._onDominantSpeakerChanged: ${event.remoteParticipant.identity}');
|
||||
var oldDominantParticipant = _participants.firstWhere((p) => p.isDominant, orElse: () => null);
|
||||
if (oldDominantParticipant != null) {
|
||||
var oldDominantParticipantIndex = _participants.indexOf(oldDominantParticipant);
|
||||
_participants.replaceRange(oldDominantParticipantIndex, oldDominantParticipantIndex + 1, [oldDominantParticipant.copyWith(isDominant: false)]);
|
||||
}
|
||||
|
||||
var newDominantParticipant = _participants.firstWhere((p) => p.id == event.remoteParticipant.sid);
|
||||
var newDominantParticipantIndex = _participants.indexOf(newDominantParticipant);
|
||||
_participants.replaceRange(newDominantParticipantIndex, newDominantParticipantIndex + 1, [newDominantParticipant.copyWith(isDominant: true)]);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _onParticipantConnected(RoomParticipantConnectedEvent event) {
|
||||
print('ConferenceRoom._onParticipantConnected, ${event.remoteParticipant.sid}');
|
||||
_addRemoteParticipantListeners(event.remoteParticipant);
|
||||
}
|
||||
|
||||
void _onParticipantDisconnected(RoomParticipantDisconnectedEvent event) {
|
||||
print('ConferenceRoom._onParticipantDisconnected: ${event.remoteParticipant.sid}');
|
||||
_participants.removeWhere((ParticipantWidget p) => p.id == event.remoteParticipant.sid);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
ParticipantWidget _buildParticipant({
|
||||
@required Widget child,
|
||||
@required String id,
|
||||
@required bool audioEnabled,
|
||||
@required bool videoEnabled,
|
||||
RemoteParticipant remoteParticipant,
|
||||
}) {
|
||||
return ParticipantWidget(
|
||||
id: remoteParticipant?.sid,
|
||||
isRemote: remoteParticipant != null,
|
||||
child: child,
|
||||
audioEnabled: audioEnabled,
|
||||
videoEnabled: videoEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
void _addRemoteParticipantListeners(RemoteParticipant remoteParticipant) {
|
||||
print('ConferenceRoom._addRemoteParticipantListeners() => Adding listeners to remoteParticipant ${remoteParticipant.sid}');
|
||||
_streamSubscriptions.add(remoteParticipant.onAudioTrackDisabled.listen(_onAudioTrackDisabled));
|
||||
_streamSubscriptions.add(remoteParticipant.onAudioTrackEnabled.listen(_onAudioTrackEnabled));
|
||||
_streamSubscriptions.add(remoteParticipant.onAudioTrackPublished.listen(_onAudioTrackPublished));
|
||||
_streamSubscriptions.add(remoteParticipant.onAudioTrackSubscribed.listen(_onAudioTrackSubscribed));
|
||||
_streamSubscriptions.add(remoteParticipant.onAudioTrackSubscriptionFailed.listen(_onAudioTrackSubscriptionFailed));
|
||||
_streamSubscriptions.add(remoteParticipant.onAudioTrackUnpublished.listen(_onAudioTrackUnpublished));
|
||||
_streamSubscriptions.add(remoteParticipant.onAudioTrackUnsubscribed.listen(_onAudioTrackUnsubscribed));
|
||||
|
||||
_streamSubscriptions.add(remoteParticipant.onDataTrackPublished.listen(_onDataTrackPublished));
|
||||
_streamSubscriptions.add(remoteParticipant.onDataTrackSubscribed.listen(_onDataTrackSubscribed));
|
||||
_streamSubscriptions.add(remoteParticipant.onDataTrackSubscriptionFailed.listen(_onDataTrackSubscriptionFailed));
|
||||
_streamSubscriptions.add(remoteParticipant.onDataTrackUnpublished.listen(_onDataTrackUnpublished));
|
||||
_streamSubscriptions.add(remoteParticipant.onDataTrackUnsubscribed.listen(_onDataTrackUnsubscribed));
|
||||
|
||||
_streamSubscriptions.add(remoteParticipant.onVideoTrackDisabled.listen(_onVideoTrackDisabled));
|
||||
_streamSubscriptions.add(remoteParticipant.onVideoTrackEnabled.listen(_onVideoTrackEnabled));
|
||||
_streamSubscriptions.add(remoteParticipant.onVideoTrackPublished.listen(_onVideoTrackPublished));
|
||||
_streamSubscriptions.add(remoteParticipant.onVideoTrackSubscribed.listen(_onVideoTrackSubscribed));
|
||||
_streamSubscriptions.add(remoteParticipant.onVideoTrackSubscriptionFailed.listen(_onVideoTrackSubscriptionFailed));
|
||||
_streamSubscriptions.add(remoteParticipant.onVideoTrackUnpublished.listen(_onVideoTrackUnpublished));
|
||||
_streamSubscriptions.add(remoteParticipant.onVideoTrackUnsubscribed.listen(_onVideoTrackUnsubscribed));
|
||||
}
|
||||
|
||||
void _onAudioTrackDisabled(RemoteAudioTrackEvent event) {
|
||||
print('ConferenceRoom._onAudioTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}');
|
||||
_setRemoteAudioEnabled(event);
|
||||
}
|
||||
|
||||
void _onAudioTrackEnabled(RemoteAudioTrackEvent event) {
|
||||
print('ConferenceRoom._onAudioTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}');
|
||||
_setRemoteAudioEnabled(event);
|
||||
}
|
||||
|
||||
void _onAudioTrackPublished(RemoteAudioTrackEvent event) {
|
||||
print('ConferenceRoom._onAudioTrackPublished(), ${event.remoteParticipant.sid}}');
|
||||
}
|
||||
|
||||
void _onAudioTrackSubscribed(RemoteAudioTrackSubscriptionEvent event) {
|
||||
print('ConferenceRoom._onAudioTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
|
||||
_addOrUpdateParticipant(event);
|
||||
}
|
||||
|
||||
void _onAudioTrackSubscriptionFailed(RemoteAudioTrackSubscriptionFailedEvent event) {
|
||||
print('ConferenceRoom._onAudioTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
|
||||
_onExceptionStreamController.add(
|
||||
PlatformException(
|
||||
code: 'ConferenceRoom.audioTrackSubscriptionFailed',
|
||||
message: 'AudioTrack Subscription Failed',
|
||||
details: event.exception.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onAudioTrackUnpublished(RemoteAudioTrackEvent event) {
|
||||
print('ConferenceRoom._onAudioTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
|
||||
}
|
||||
|
||||
void _onAudioTrackUnsubscribed(RemoteAudioTrackSubscriptionEvent event) {
|
||||
print('ConferenceRoom._onAudioTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrack.sid}');
|
||||
}
|
||||
|
||||
void _onDataTrackPublished(RemoteDataTrackEvent event) {
|
||||
print('ConferenceRoom._onDataTrackPublished(), ${event.remoteParticipant.sid}}');
|
||||
}
|
||||
|
||||
void _onDataTrackSubscribed(RemoteDataTrackSubscriptionEvent event) {
|
||||
print('ConferenceRoom._onDataTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
|
||||
final dataTrack = event.remoteDataTrackPublication.remoteDataTrack;
|
||||
_dataTracks.add(dataTrack);
|
||||
_streamSubscriptions.add(dataTrack.onMessage.listen(_onMessage));
|
||||
_streamSubscriptions.add(dataTrack.onBufferMessage.listen(_onBufferMessage));
|
||||
}
|
||||
|
||||
void _onDataTrackSubscriptionFailed(RemoteDataTrackSubscriptionFailedEvent event) {
|
||||
print('ConferenceRoom._onDataTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
|
||||
_onExceptionStreamController.add(
|
||||
PlatformException(
|
||||
code: 'ConferenceRoom.dataTrackSubscriptionFailed',
|
||||
message: 'DataTrack Subscription Failed',
|
||||
details: event.exception.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onDataTrackUnpublished(RemoteDataTrackEvent event) {
|
||||
print('ConferenceRoom._onDataTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
|
||||
}
|
||||
|
||||
void _onDataTrackUnsubscribed(RemoteDataTrackSubscriptionEvent event) {
|
||||
print('ConferenceRoom._onDataTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrack.sid}');
|
||||
}
|
||||
|
||||
void _onVideoTrackDisabled(RemoteVideoTrackEvent event) {
|
||||
print('ConferenceRoom._onVideoTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}');
|
||||
_setRemoteVideoEnabled(event);
|
||||
}
|
||||
|
||||
void _onVideoTrackEnabled(RemoteVideoTrackEvent event) {
|
||||
print('ConferenceRoom._onVideoTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}');
|
||||
_setRemoteVideoEnabled(event);
|
||||
}
|
||||
|
||||
void _onVideoTrackPublished(RemoteVideoTrackEvent event) {
|
||||
print('ConferenceRoom._onVideoTrackPublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
|
||||
}
|
||||
|
||||
void _onVideoTrackSubscribed(RemoteVideoTrackSubscriptionEvent event) {
|
||||
print('ConferenceRoom._onVideoTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}');
|
||||
_addOrUpdateParticipant(event);
|
||||
}
|
||||
|
||||
void _onVideoTrackSubscriptionFailed(RemoteVideoTrackSubscriptionFailedEvent event) {
|
||||
print('ConferenceRoom._onVideoTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
|
||||
_onExceptionStreamController.add(
|
||||
PlatformException(
|
||||
code: 'ConferenceRoom.videoTrackSubscriptionFailed',
|
||||
message: 'VideoTrack Subscription Failed',
|
||||
details: event.exception.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onVideoTrackUnpublished(RemoteVideoTrackEvent event) {
|
||||
print('ConferenceRoom._onVideoTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
|
||||
}
|
||||
|
||||
void _onVideoTrackUnsubscribed(RemoteVideoTrackSubscriptionEvent event) {
|
||||
print('ConferenceRoom._onVideoTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}');
|
||||
}
|
||||
|
||||
void _onMessage(RemoteDataTrackStringMessageEvent event) {
|
||||
print('onMessage => ${event.remoteDataTrack.sid}, ${event.message}');
|
||||
}
|
||||
|
||||
void _onBufferMessage(RemoteDataTrackBufferMessageEvent event) {
|
||||
print('onBufferMessage => ${event.remoteDataTrack.sid}, ${String.fromCharCodes(event.message.asUint8List())}');
|
||||
}
|
||||
|
||||
void _setRemoteAudioEnabled(RemoteAudioTrackEvent event) {
|
||||
if (event.remoteAudioTrackPublication == null) {
|
||||
return;
|
||||
}
|
||||
var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
var participant = _participants[index];
|
||||
_participants.replaceRange(
|
||||
index,
|
||||
index + 1,
|
||||
[
|
||||
participant.copyWith(audioEnabled: event.remoteAudioTrackPublication.isTrackEnabled),
|
||||
],
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _setRemoteVideoEnabled(RemoteVideoTrackEvent event) {
|
||||
if (event.remoteVideoTrackPublication == null) {
|
||||
return;
|
||||
}
|
||||
var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
var participant = _participants[index];
|
||||
_participants.replaceRange(
|
||||
index,
|
||||
index + 1,
|
||||
[
|
||||
participant.copyWith(videoEnabled: event.remoteVideoTrackPublication.isTrackEnabled),
|
||||
],
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _addOrUpdateParticipant(RemoteParticipantEvent event) {
|
||||
print('ConferenceRoom._addOrUpdateParticipant(), ${event.remoteParticipant.sid}');
|
||||
final participant = _participants.firstWhere(
|
||||
(ParticipantWidget participant) => participant.id == event.remoteParticipant.sid,
|
||||
orElse: () => null,
|
||||
);
|
||||
if (participant != null) {
|
||||
print('Participant found: ${participant.id}, updating A/V enabled values');
|
||||
_setRemoteVideoEnabled(event);
|
||||
_setRemoteAudioEnabled(event);
|
||||
} else {
|
||||
final bufferedParticipant = _participantBuffer.firstWhere(
|
||||
(ParticipantBuffer participant) => participant.id == event.remoteParticipant.sid,
|
||||
orElse: () => null,
|
||||
);
|
||||
if (bufferedParticipant != null) {
|
||||
_participantBuffer.remove(bufferedParticipant);
|
||||
} else if (event is RemoteAudioTrackEvent) {
|
||||
print('Audio subscription came first, waiting for the video subscription...');
|
||||
_participantBuffer.add(
|
||||
ParticipantBuffer(
|
||||
id: event.remoteParticipant.sid,
|
||||
audioEnabled: event.remoteAudioTrackPublication?.remoteAudioTrack?.isEnabled ?? true,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (event is RemoteVideoTrackSubscriptionEvent) {
|
||||
print('New participant, adding: ${event.remoteParticipant.sid}');
|
||||
_participants.insert(
|
||||
0,
|
||||
_buildParticipant(
|
||||
child: event.remoteVideoTrack.widget(),
|
||||
id: event.remoteParticipant.sid,
|
||||
remoteParticipant: event.remoteParticipant,
|
||||
audioEnabled: bufferedParticipant?.audioEnabled ?? true,
|
||||
videoEnabled: event.remoteVideoTrackPublication?.remoteVideoTrack?.isEnabled ?? true,
|
||||
),
|
||||
);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,173 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:diplomaticquarterapp/pages/conference/clipped_video.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DraggablePublisher extends StatefulWidget {
|
||||
final Size availableScreenSize;
|
||||
final Widget child;
|
||||
final double scaleFactor;
|
||||
final Stream<bool> onButtonBarVisible;
|
||||
final Stream<double> onButtonBarHeight;
|
||||
|
||||
const DraggablePublisher({
|
||||
Key key,
|
||||
@required this.availableScreenSize,
|
||||
this.child,
|
||||
@required this.onButtonBarVisible,
|
||||
@required this.onButtonBarHeight,
|
||||
|
||||
/// The portion of the screen the DraggableWidget should use.
|
||||
this.scaleFactor = .25,
|
||||
}) : assert(scaleFactor != null && scaleFactor > 0 && scaleFactor <= .4),
|
||||
assert(availableScreenSize != null),
|
||||
assert(onButtonBarVisible != null),
|
||||
assert(onButtonBarHeight != null),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
_DraggablePublisherState createState() => _DraggablePublisherState();
|
||||
}
|
||||
|
||||
class _DraggablePublisherState extends State<DraggablePublisher> {
|
||||
bool _isButtonBarVisible = true;
|
||||
double _buttonBarHeight = 0;
|
||||
double _width;
|
||||
double _height;
|
||||
double _top;
|
||||
double _left;
|
||||
double _viewPaddingTop;
|
||||
double _viewPaddingBottom;
|
||||
final double _padding = 8.0;
|
||||
final Duration _duration300ms = const Duration(milliseconds: 300);
|
||||
final Duration _duration0ms = const Duration(milliseconds: 0);
|
||||
Duration _duration;
|
||||
StreamSubscription _streamSubscription;
|
||||
StreamSubscription _streamHeightSubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_duration = _duration300ms;
|
||||
_width = widget.availableScreenSize.width * widget.scaleFactor;
|
||||
_height = _width * (widget.availableScreenSize.height / widget.availableScreenSize.width);
|
||||
_top = widget.availableScreenSize.height - (_buttonBarHeight + _padding) - _height;
|
||||
_left = widget.availableScreenSize.width - _padding - _width;
|
||||
|
||||
_streamSubscription = widget.onButtonBarVisible.listen(_buttonBarVisible);
|
||||
_streamHeightSubscription = widget.onButtonBarHeight.listen(_getButtonBarHeight);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
_viewPaddingTop = mediaQuery.viewPadding.top;
|
||||
_viewPaddingBottom = mediaQuery.viewPadding.bottom;
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_streamSubscription.cancel();
|
||||
_streamHeightSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _getButtonBarHeight(double height) {
|
||||
setState(() {
|
||||
_buttonBarHeight = height;
|
||||
_positionWidget();
|
||||
});
|
||||
}
|
||||
|
||||
void _buttonBarVisible(bool visible) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_isButtonBarVisible = visible;
|
||||
if (_duration == _duration300ms) {
|
||||
// only position the widget when we are not currently dragging it around
|
||||
_positionWidget();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedPositioned(
|
||||
top: _top,
|
||||
left: _left,
|
||||
width: _width,
|
||||
height: _height,
|
||||
duration: _duration,
|
||||
child: Listener(
|
||||
onPointerDown: (_) => _duration = _duration0ms,
|
||||
onPointerMove: (PointerMoveEvent event) {
|
||||
setState(() {
|
||||
_left = (_left + event.delta.dx).roundToDouble();
|
||||
_top = (_top + event.delta.dy).roundToDouble();
|
||||
});
|
||||
},
|
||||
onPointerUp: (_) => _positionWidget(),
|
||||
onPointerCancel: (_) => _positionWidget(),
|
||||
child: ClippedVideo(
|
||||
height: _height,
|
||||
width: _width,
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
double _getCurrentStatusBarHeight() {
|
||||
if (_isButtonBarVisible) {
|
||||
return _viewPaddingTop;
|
||||
}
|
||||
final _defaultViewPaddingTop = Platform.isIOS ? 20.0 : Platform.isAndroid ? 24.0 : 0.0;
|
||||
if (_viewPaddingTop > _defaultViewPaddingTop) {
|
||||
// There must be a hardware notch in the display.
|
||||
return _viewPaddingTop;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double _getCurrentButtonBarHeight() {
|
||||
if (_isButtonBarVisible) {
|
||||
return _buttonBarHeight + _viewPaddingBottom;
|
||||
}
|
||||
return _viewPaddingBottom;
|
||||
}
|
||||
|
||||
void _positionWidget() {
|
||||
// Determine the center of the object being dragged so we can decide
|
||||
// in which corner the object should be placed.
|
||||
var dx = (_width / 2) + _left;
|
||||
dx = dx < 0 ? 0 : dx >= widget.availableScreenSize.width ? widget.availableScreenSize.width - 1 : dx;
|
||||
var dy = (_height / 2) + _top;
|
||||
dy = dy < 0 ? 0 : dy >= widget.availableScreenSize.height ? widget.availableScreenSize.height - 1 : dy;
|
||||
final draggableCenter = Offset(dx, dy);
|
||||
|
||||
setState(() {
|
||||
_duration = _duration300ms;
|
||||
if (Rect.fromLTRB(0, 0, widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
|
||||
// Top-left
|
||||
_top = _getCurrentStatusBarHeight() + _padding;
|
||||
_left = _padding;
|
||||
} else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, 0, widget.availableScreenSize.width, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
|
||||
// Top-right
|
||||
_top = _getCurrentStatusBarHeight() + _padding;
|
||||
_left = widget.availableScreenSize.width - _padding - _width;
|
||||
} else if (Rect.fromLTRB(0, widget.availableScreenSize.height / 2, widget.availableScreenSize.width / 2, widget.availableScreenSize.height).contains(draggableCenter)) {
|
||||
// Bottom-left
|
||||
_top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
|
||||
_left = _padding;
|
||||
} else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2, widget.availableScreenSize.width, widget.availableScreenSize.height).contains(draggableCenter)) {
|
||||
// Bottom-right
|
||||
_top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
|
||||
_left = widget.availableScreenSize.width - _padding - _width;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,197 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ParticipantBuffer {
|
||||
final bool audioEnabled;
|
||||
final String id;
|
||||
|
||||
ParticipantBuffer({
|
||||
@required this.audioEnabled,
|
||||
@required this.id,
|
||||
}) : assert(audioEnabled != null),
|
||||
assert(id != null);
|
||||
}
|
||||
|
||||
class ParticipantWidget extends StatelessWidget {
|
||||
final Widget child;
|
||||
final String id;
|
||||
final bool audioEnabled;
|
||||
final bool videoEnabled;
|
||||
final bool isRemote;
|
||||
final bool isDummy;
|
||||
final bool isDominant;
|
||||
|
||||
const ParticipantWidget({
|
||||
Key key,
|
||||
@required this.child,
|
||||
@required this.audioEnabled,
|
||||
@required this.videoEnabled,
|
||||
@required this.id,
|
||||
@required this.isRemote,
|
||||
this.isDominant = false,
|
||||
this.isDummy = false,
|
||||
}) : assert(child != null),
|
||||
assert(audioEnabled != null),
|
||||
assert(videoEnabled != null),
|
||||
assert(isRemote != null),
|
||||
assert(isDominant != null),
|
||||
assert(isDummy != null),
|
||||
super(key: key);
|
||||
|
||||
ParticipantWidget copyWith({
|
||||
Widget child,
|
||||
bool audioEnabled,
|
||||
bool videoEnabled,
|
||||
bool isDominant,
|
||||
}) {
|
||||
return ParticipantWidget(
|
||||
id: id,
|
||||
child: child ?? this.child,
|
||||
audioEnabled: audioEnabled ?? this.audioEnabled,
|
||||
videoEnabled: videoEnabled ?? this.videoEnabled,
|
||||
isDominant: isDominant ?? this.isDominant,
|
||||
isRemote: isRemote,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final children = <Widget>[];
|
||||
final icons = <Widget>[];
|
||||
if (!videoEnabled) {
|
||||
icons.add(_buildVideoEnabledIcon());
|
||||
children.add(
|
||||
ClipRect(
|
||||
// Need to clip this BackdropFilter, otherwise it will blur the entire screen
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: Colors.black.withOpacity(.1)),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
children.add(child);
|
||||
}
|
||||
children.add(Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: AnimatedOpacity(
|
||||
duration: Duration(milliseconds: 500),
|
||||
opacity: isDominant ? 1 : 0,
|
||||
child: Icon(
|
||||
Icons.volume_up,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
));
|
||||
if (!audioEnabled) {
|
||||
icons.add(_buildAudioEnabledIcon());
|
||||
}
|
||||
if (icons.isNotEmpty) {
|
||||
if (isRemote) {
|
||||
final rows = <Widget>[];
|
||||
rows.add(_buildRow(icons));
|
||||
if (!audioEnabled && !videoEnabled) {
|
||||
rows.add(_buildRow(_fitText('The camera and microphone are off', Colors.white24)));
|
||||
} else if (!audioEnabled) {
|
||||
rows.add(_buildRow(_fitText('The microphone is off', Colors.black26)));
|
||||
} else if (!videoEnabled) {
|
||||
rows.add(_buildRow(_fitText('The camera is off', Colors.white24)));
|
||||
}
|
||||
children.add(
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: rows,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
children.add(Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: icons,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return Stack(
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _fitText(String text, Color color) {
|
||||
return [
|
||||
Flexible(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0, right: 8.0),
|
||||
child: Text(text, maxLines: 1, style: _buildTextStyle(color)),
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
TextStyle _buildTextStyle(Color color) {
|
||||
return TextStyle(
|
||||
color: color,
|
||||
shadows: <Shadow>[
|
||||
Shadow(
|
||||
blurRadius: 1.0,
|
||||
color: Color.fromARGB(255, 0, 0, 0),
|
||||
),
|
||||
Shadow(
|
||||
blurRadius: 1.0,
|
||||
color: Color.fromARGB(24, 255, 255, 255),
|
||||
),
|
||||
],
|
||||
fontSize: 15,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRow(List<Widget> children) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAudioEnabledIcon() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: CircleAvatar(
|
||||
maxRadius: 15,
|
||||
child: FittedBox(
|
||||
child: Icon(
|
||||
Icons.mic_off,
|
||||
color: Colors.black,
|
||||
key: Key('microphone-off-icon'),
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.white24,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildVideoEnabledIcon() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: CircleAvatar(
|
||||
maxRadius: 15,
|
||||
child: FittedBox(
|
||||
child: Icon(
|
||||
Icons.videocam_off,
|
||||
color: Colors.black,
|
||||
key: Key('videocam-off-icon'),
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.white24,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ButtonToProgress extends StatefulWidget {
|
||||
final double height;
|
||||
final double progressHeight;
|
||||
final String loadingText;
|
||||
final Duration duration;
|
||||
final TextStyle loadingTextStyle;
|
||||
final VoidCallback onPressed;
|
||||
final Stream<bool> onLoading;
|
||||
final Widget child;
|
||||
|
||||
const ButtonToProgress({
|
||||
Key key,
|
||||
this.height = 40.0,
|
||||
this.progressHeight = 5.0,
|
||||
this.loadingText,
|
||||
this.duration = const Duration(milliseconds: 300),
|
||||
this.loadingTextStyle,
|
||||
this.onPressed,
|
||||
this.onLoading,
|
||||
@required this.child,
|
||||
}) : assert(child != null),
|
||||
assert(height != null && height > 0),
|
||||
assert(progressHeight != null && progressHeight > 0 && progressHeight <= height),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
_ButtonToProgressState createState() => _ButtonToProgressState();
|
||||
}
|
||||
|
||||
class _ButtonToProgressState extends State<ButtonToProgress> {
|
||||
double _height;
|
||||
double _opacity = 0;
|
||||
bool _isLoading = false;
|
||||
|
||||
StreamSubscription<bool> _subscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_height = widget.height;
|
||||
if (widget.onLoading != null) {
|
||||
_subscription = widget.onLoading.listen((bool isLoading) {
|
||||
setState(() {
|
||||
_isLoading = isLoading;
|
||||
_height = isLoading ? widget.progressHeight : widget.height;
|
||||
_opacity = isLoading ? 1 : 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_subscription != null) {
|
||||
_subscription.cancel();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: widget.height,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (widget.loadingText == null)
|
||||
Container()
|
||||
else
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: widget.progressHeight),
|
||||
child: AnimatedOpacity(
|
||||
child: Center(
|
||||
child: FittedBox(
|
||||
child: Text(
|
||||
widget.loadingText,
|
||||
style: widget.loadingTextStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
opacity: _opacity,
|
||||
duration: Duration(milliseconds: widget.duration.inMilliseconds + 200),
|
||||
curve: Curves.easeInCubic,
|
||||
),
|
||||
),
|
||||
AnimatedPadding(
|
||||
duration: widget.duration,
|
||||
padding: EdgeInsets.only(
|
||||
top: math.max(widget.height - _height, 0),
|
||||
),
|
||||
child: AnimatedContainer(
|
||||
duration: widget.duration,
|
||||
height: _height,
|
||||
width: double.infinity,
|
||||
child: _isLoading ? const LinearProgressIndicator() : widget.child,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:native_device_orientation/native_device_orientation.dart';
|
||||
|
||||
class CircleButton extends StatefulWidget {
|
||||
final VoidCallback onLongPress;
|
||||
final VoidCallback onPressed;
|
||||
final GestureTapDownCallback onTapDown;
|
||||
final VoidCallback onTapCancel;
|
||||
final Widget child;
|
||||
final Color color;
|
||||
final double radius;
|
||||
|
||||
const CircleButton({
|
||||
Key key,
|
||||
this.onLongPress,
|
||||
this.onPressed,
|
||||
this.child,
|
||||
this.color,
|
||||
this.radius = 25.0,
|
||||
this.onTapCancel,
|
||||
this.onTapDown,
|
||||
}) : assert(radius != null),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
_CircleButtonState createState() => _CircleButtonState();
|
||||
}
|
||||
|
||||
class _CircleButtonState extends State<CircleButton> {
|
||||
double _rotationAngle = 0.0;
|
||||
|
||||
final Stream<NativeDeviceOrientation> _orientationStream = NativeDeviceOrientationCommunicator().onOrientationChanged(useSensor: true);
|
||||
StreamSubscription<NativeDeviceOrientation> _orientationSubscription;
|
||||
|
||||
void _handleOrientationChange(NativeDeviceOrientation orientation) {
|
||||
var targetAngle = 0.0;
|
||||
switch (orientation) {
|
||||
case NativeDeviceOrientation.unknown:
|
||||
case NativeDeviceOrientation.portraitUp:
|
||||
targetAngle = 0.0;
|
||||
break;
|
||||
case NativeDeviceOrientation.portraitDown:
|
||||
targetAngle = 180.0;
|
||||
break;
|
||||
case NativeDeviceOrientation.landscapeLeft:
|
||||
targetAngle = 90.0;
|
||||
break;
|
||||
case NativeDeviceOrientation.landscapeRight:
|
||||
targetAngle = 270.0;
|
||||
break;
|
||||
}
|
||||
setState(() {
|
||||
_rotationAngle = targetAngle;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_orientationSubscription = _orientationStream.listen(
|
||||
_handleOrientationChange,
|
||||
onError: (dynamic err) => print(err),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_orientationSubscription.cancel();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = 2 * widget.radius;
|
||||
|
||||
return Container(
|
||||
width: size,
|
||||
height: size,
|
||||
decoration: BoxDecoration(
|
||||
color: (widget.color ?? Colors.blue).withAlpha(200),
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(widget.radius),
|
||||
),
|
||||
),
|
||||
child: GestureDetector(
|
||||
onLongPress: widget.onLongPress,
|
||||
onTapDown: widget.onTapDown,
|
||||
onTapCancel: widget.onTapCancel,
|
||||
child: RawMaterialButton(
|
||||
onPressed: widget.onPressed,
|
||||
child: RotationTransition(
|
||||
child: widget.child,
|
||||
turns: AlwaysStoppedAnimation<double>(_rotationAngle / 360),
|
||||
),
|
||||
elevation: 0,
|
||||
shape: const CircleBorder(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,144 @@
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum NoiseBoxDensity {
|
||||
high,
|
||||
medium,
|
||||
low,
|
||||
xHigh,
|
||||
xLow,
|
||||
}
|
||||
|
||||
class NoiseBox extends StatefulWidget {
|
||||
final NoiseBoxDensity density;
|
||||
final Color backgroundColor;
|
||||
final Widget child;
|
||||
|
||||
const NoiseBox({
|
||||
Key key,
|
||||
this.backgroundColor,
|
||||
this.child,
|
||||
this.density = NoiseBoxDensity.medium,
|
||||
}) : assert(density != null),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
_NoiseBoxState createState() => _NoiseBoxState();
|
||||
}
|
||||
|
||||
class _NoiseBoxState extends State<NoiseBox> with SingleTickerProviderStateMixin {
|
||||
AnimationController _animationController;
|
||||
int _density;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_animationController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(seconds: 60),
|
||||
);
|
||||
_animationController.repeat();
|
||||
switch (widget.density) {
|
||||
case NoiseBoxDensity.high:
|
||||
_density = 5;
|
||||
break;
|
||||
case NoiseBoxDensity.medium:
|
||||
_density = 7;
|
||||
break;
|
||||
case NoiseBoxDensity.low:
|
||||
_density = 10;
|
||||
break;
|
||||
case NoiseBoxDensity.xHigh:
|
||||
_density = 3;
|
||||
break;
|
||||
case NoiseBoxDensity.xLow:
|
||||
_density = 12;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) => Container(
|
||||
color: widget.backgroundColor,
|
||||
width: constraints.biggest.width,
|
||||
height: constraints.biggest.height,
|
||||
child: AnimatedBuilder(
|
||||
animation: _animationController,
|
||||
builder: (BuildContext context, Widget w) {
|
||||
final children = <Widget>[
|
||||
CustomPaint(
|
||||
painter: NoisePainter(
|
||||
width: constraints.biggest.width,
|
||||
height: constraints.biggest.height,
|
||||
density: _density,
|
||||
),
|
||||
),
|
||||
];
|
||||
if (widget.child != null) {
|
||||
children.add(widget.child);
|
||||
}
|
||||
return Stack(
|
||||
children: children,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NoisePainter extends CustomPainter {
|
||||
final double width;
|
||||
final double height;
|
||||
final int density;
|
||||
|
||||
NoisePainter({
|
||||
@required this.width,
|
||||
@required this.height,
|
||||
@required this.density,
|
||||
}) : assert(width != null),
|
||||
assert(height != null),
|
||||
assert(density != null && density >= 3 && density < math.min(width, height));
|
||||
|
||||
List<Color> colors = <Color>[
|
||||
Colors.black,
|
||||
Colors.grey,
|
||||
Colors.blueGrey,
|
||||
Colors.red,
|
||||
Colors.green,
|
||||
Colors.blue,
|
||||
Colors.white,
|
||||
];
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final random = math.Random();
|
||||
for (var w = 0; w < width; w += density) {
|
||||
for (var h = 0; h < height; h += density) {
|
||||
final offset = Offset(
|
||||
random.nextDouble() * width,
|
||||
random.nextDouble() * height,
|
||||
);
|
||||
final paint = Paint();
|
||||
paint.color = colors[random.nextInt(colors.length)];
|
||||
paint.strokeWidth = random.nextDouble() * 2;
|
||||
|
||||
canvas.drawPoints(PointMode.points, <Offset>[offset], paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import './platform_widget.dart';
|
||||
|
||||
class PlatformAlertDialog extends PlatformWidget {
|
||||
PlatformAlertDialog({@required this.title, @required this.content, @required this.defaultActionText, this.cancelActionText})
|
||||
: assert(title != null),
|
||||
assert(content != null),
|
||||
assert(defaultActionText != null);
|
||||
|
||||
final String title;
|
||||
final String content;
|
||||
final String defaultActionText;
|
||||
final String cancelActionText;
|
||||
|
||||
Future<bool> show(BuildContext context) async {
|
||||
return Platform.isIOS
|
||||
? await showCupertinoDialog<bool>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => this,
|
||||
)
|
||||
: await showDialog<bool>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) => this,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildCupertinoWidget(BuildContext context) {
|
||||
return CupertinoAlertDialog(
|
||||
title: Text(title),
|
||||
content: Text(content),
|
||||
actions: _buildActions(context),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildMaterialWidget(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(title),
|
||||
content: Text(content),
|
||||
actions: _buildActions(context),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildActions(BuildContext context) {
|
||||
final actions = <Widget>[];
|
||||
if (cancelActionText != null) {
|
||||
actions.add(
|
||||
PlatformAlertDialogAction(
|
||||
child: Text(cancelActionText),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
actions.add(
|
||||
PlatformAlertDialogAction(
|
||||
child: Text(defaultActionText),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
),
|
||||
);
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
|
||||
class PlatformAlertDialogAction extends PlatformWidget {
|
||||
PlatformAlertDialogAction({
|
||||
this.child,
|
||||
this.onPressed,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
@override
|
||||
Widget buildCupertinoWidget(BuildContext context) {
|
||||
return CupertinoDialogAction(
|
||||
child: child,
|
||||
onPressed: onPressed,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildMaterialWidget(BuildContext context) {
|
||||
return FlatButton(
|
||||
child: child,
|
||||
onPressed: onPressed,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import './platform_alert_dialog.dart';
|
||||
|
||||
class PlatformExceptionAlertDialog extends PlatformAlertDialog {
|
||||
PlatformExceptionAlertDialog({
|
||||
String title = 'An error occurred',
|
||||
@required Exception exception,
|
||||
}) : super(
|
||||
title: title,
|
||||
content: exception is PlatformException ? _message(exception) : exception.toString(),
|
||||
defaultActionText: 'OK',
|
||||
);
|
||||
|
||||
static String _message(PlatformException exception) {
|
||||
return _errors[exception.code] ?? (exception.details != null ? (exception.details['message'] ?? exception.message) : exception.message);
|
||||
}
|
||||
|
||||
static final Map<String, String> _errors = <String, String>{
|
||||
'ERROR_CODE': 'Error description...',
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class PlatformWidget extends StatelessWidget {
|
||||
Widget buildCupertinoWidget(BuildContext context);
|
||||
|
||||
Widget buildMaterialWidget(BuildContext context);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (Platform.isIOS) {
|
||||
return buildCupertinoWidget(context);
|
||||
}
|
||||
return buildMaterialWidget(context);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
typedef ResponsiveBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
Size size,
|
||||
);
|
||||
|
||||
class ResponsiveSafeArea extends StatelessWidget {
|
||||
const ResponsiveSafeArea({
|
||||
@required ResponsiveBuilder builder,
|
||||
Key key,
|
||||
}) : responsiveBuilder = builder,
|
||||
assert(builder != null),
|
||||
super(key: key);
|
||||
|
||||
final ResponsiveBuilder responsiveBuilder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
return responsiveBuilder(
|
||||
context,
|
||||
constraints.biggest,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,255 @@
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/IncomingCallData.dart';
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/room_model.dart';
|
||||
import 'package:diplomaticquarterapp/pages/conference/conference_page.dart';
|
||||
import 'package:diplomaticquarterapp/pages/conference/widgets/platform_exception_alert_dialog.dart';
|
||||
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
|
||||
class IncomingCall extends StatefulWidget {
|
||||
IncomingCallData incomingCallData;
|
||||
|
||||
IncomingCall({@required this.incomingCallData});
|
||||
|
||||
@override
|
||||
_IncomingCallState createState() => _IncomingCallState();
|
||||
}
|
||||
|
||||
class _IncomingCallState extends State<IncomingCall>
|
||||
with SingleTickerProviderStateMixin {
|
||||
AnimationController _animationController;
|
||||
|
||||
final player = AudioPlayer();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_animationController =
|
||||
AnimationController(vsync: this, duration: Duration(milliseconds: 500));
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _runAnimation());
|
||||
|
||||
print(widget.incomingCallData.doctorname);
|
||||
print(widget.incomingCallData.clinicname);
|
||||
print(widget.incomingCallData.speciality);
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
player.stop();
|
||||
disposeAudioResources();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppScaffold(
|
||||
isShowAppBar: false,
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: Colors.grey[700]),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 30.0),
|
||||
alignment: Alignment.center,
|
||||
child: Text("Incoming Video Call",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 26.0,
|
||||
color: Colors.white,
|
||||
letterSpacing: 1.0)),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.fromLTRB(50.0, 30.0, 50.0, 20.0),
|
||||
child: Image.asset(
|
||||
'assets/images/new-design/hmg_full_logo_hd_white.png'),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.fromLTRB(30.0, 10.0, 30.0, 0.0),
|
||||
child: Divider(
|
||||
color: Colors.white,
|
||||
thickness: 1.0,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 20.0),
|
||||
alignment: Alignment.center,
|
||||
child: Text("Dr Eyad Ismail Abu Jayab",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 22.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 0.8,
|
||||
color: Colors.white)),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 10.0),
|
||||
alignment: Alignment.center,
|
||||
child: Text("ENT Clinic",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 22.0,
|
||||
letterSpacing: 0.8,
|
||||
color: Colors.white)),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 10.0),
|
||||
alignment: Alignment.center,
|
||||
child: Text("Speciality",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 22.0,
|
||||
letterSpacing: 0.8,
|
||||
color: Colors.white)),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[900].withOpacity(0.8),
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
padding: EdgeInsets.all(20.0),
|
||||
margin: EdgeInsets.only(top: 20.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Text("Appointment Information",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 1.0,
|
||||
color: Colors.white)),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 20.0),
|
||||
child: Text("Sun, 15th Dec, 2019, 09:00",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
letterSpacing: 1.0,
|
||||
color: Colors.white)),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 20.0),
|
||||
child: Text("ENT Clinic",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
letterSpacing: 1.0,
|
||||
color: Colors.white)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 100.0),
|
||||
alignment: Alignment.center,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
RotationTransition(
|
||||
turns: Tween(begin: 0.0, end: -.1)
|
||||
.chain(CurveTween(curve: Curves.elasticIn))
|
||||
.animate(_animationController),
|
||||
child: Container(
|
||||
child: RawMaterialButton(
|
||||
onPressed: () {
|
||||
_submit();
|
||||
},
|
||||
elevation: 2.0,
|
||||
fillColor: Colors.green,
|
||||
child: Icon(
|
||||
Icons.call,
|
||||
color: Colors.white,
|
||||
size: 35.0,
|
||||
),
|
||||
padding: EdgeInsets.all(15.0),
|
||||
shape: CircleBorder(),
|
||||
),
|
||||
)),
|
||||
Container(
|
||||
child: RawMaterialButton(
|
||||
onPressed: () {
|
||||
backToHome();
|
||||
},
|
||||
elevation: 2.0,
|
||||
fillColor: Colors.red,
|
||||
child: Icon(
|
||||
Icons.call_end,
|
||||
color: Colors.white,
|
||||
size: 35.0,
|
||||
),
|
||||
padding: EdgeInsets.all(15.0),
|
||||
shape: CircleBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _runAnimation() async {
|
||||
setAudioFile();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
await _animationController.forward();
|
||||
await _animationController.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _submit() async {
|
||||
backToHome();
|
||||
try {
|
||||
final roomModel = RoomModel(
|
||||
name: widget.incomingCallData.name,
|
||||
token: widget.incomingCallData.sessionId,
|
||||
identity: widget.incomingCallData.identity);
|
||||
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute<ConferencePage>(
|
||||
fullscreenDialog: true,
|
||||
builder: (BuildContext context) =>
|
||||
ConferencePage(roomModel: roomModel),
|
||||
),
|
||||
);
|
||||
} catch (err) {
|
||||
print(err);
|
||||
await PlatformExceptionAlertDialog(
|
||||
exception: err,
|
||||
).show(context);
|
||||
}
|
||||
}
|
||||
|
||||
void backToHome() {
|
||||
player.stop();
|
||||
// disposeAudioResources();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
disposeAudioResources() async {
|
||||
await player.dispose();
|
||||
}
|
||||
|
||||
void setAudioFile() async {
|
||||
player.stop();
|
||||
await player.setVolume(1.0); // full volume
|
||||
try {
|
||||
await player.setAsset('assets/sounds/ring_60Sec.mp3').then((value) {
|
||||
player.setLoopMode(LoopMode.one); // loop ring sound
|
||||
player.play();
|
||||
}).catchError((err) {
|
||||
print("Error: $err");
|
||||
});
|
||||
} catch (e) {
|
||||
print("Error: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
import 'package:diplomaticquarterapp/models/FamilyFiles/PatientERVirtualHistoryResponse.dart';
|
||||
import 'package:diplomaticquarterapp/pages/livecare/widgets/LiveCarePendingRequest.dart';
|
||||
import 'package:diplomaticquarterapp/pages/livecare/widgets/clinic_list.dart';
|
||||
import 'package:diplomaticquarterapp/pages/livecare/widgets/livecare_logs.dart';
|
||||
import 'package:diplomaticquarterapp/services/livecare_services/livecare_provider.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:smart_progress_bar/smart_progress_bar.dart';
|
||||
|
||||
class LiveCareHome extends StatefulWidget {
|
||||
static bool showFooterButton = true;
|
||||
|
||||
@override
|
||||
_LiveCareHomeState createState() => _LiveCareHomeState();
|
||||
}
|
||||
|
||||
class _LiveCareHomeState extends State<LiveCareHome>
|
||||
with SingleTickerProviderStateMixin {
|
||||
TabController _tabController;
|
||||
|
||||
bool isDataLoaded = false;
|
||||
bool hasLiveCareRequest = false;
|
||||
|
||||
List<ErRequestHistoryList> erRequestHistoryList;
|
||||
|
||||
ErRequestHistoryList pendingERRequestHistoryList;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_tabController = new TabController(length: 2, vsync: this);
|
||||
erRequestHistoryList = List();
|
||||
|
||||
pendingERRequestHistoryList = new ErRequestHistoryList();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => getLiveCareHistory());
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppScaffold(
|
||||
appBarTitle: "LiveCare",
|
||||
isShowAppBar: true,
|
||||
body: Container(
|
||||
child: Column(children: [
|
||||
/// this is will not colored with theme data
|
||||
TabBar(
|
||||
tabs: [
|
||||
Tab(text: TranslationBase.of(context).consultation),
|
||||
Tab(text: TranslationBase.of(context).logs),
|
||||
],
|
||||
controller: _tabController,
|
||||
),
|
||||
Divider(
|
||||
color: Colors.grey[600],
|
||||
thickness: 0.5,
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
children: [
|
||||
isDataLoaded && !hasLiveCareRequest
|
||||
? ClinicList(
|
||||
getLiveCareHistory: getLiveCareHistory,
|
||||
)
|
||||
: isDataLoaded
|
||||
? LiveCarePendingRequest(
|
||||
getLiveCareHistory: getLiveCareHistory,
|
||||
pendingERRequestHistoryList:
|
||||
pendingERRequestHistoryList)
|
||||
: Container(),
|
||||
isDataLoaded
|
||||
? LiveCareLogs(
|
||||
erRequestHistoryList: erRequestHistoryList,
|
||||
)
|
||||
: Container(),
|
||||
],
|
||||
controller: _tabController,
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void getLiveCareHistory() {
|
||||
setState(() {
|
||||
isDataLoaded = false;
|
||||
hasLiveCareRequest = false;
|
||||
});
|
||||
LiveCareService service = new LiveCareService();
|
||||
PatientERVirtualHistoryResponse patientERVirtualHistoryResponse =
|
||||
new PatientERVirtualHistoryResponse();
|
||||
service
|
||||
.getLivecareHistory(context)
|
||||
.then((res) {
|
||||
setState(() {
|
||||
print(res['ErRequestHistoryList'].length);
|
||||
if (res['ErRequestHistoryList'].length != 0) {
|
||||
patientERVirtualHistoryResponse =
|
||||
PatientERVirtualHistoryResponse.fromJson(res);
|
||||
erRequestHistoryList =
|
||||
patientERVirtualHistoryResponse.erRequestHistoryList;
|
||||
|
||||
if (patientERVirtualHistoryResponse
|
||||
.erRequestHistoryList[0].callStatus <
|
||||
4) {
|
||||
pendingERRequestHistoryList =
|
||||
patientERVirtualHistoryResponse.erRequestHistoryList[0];
|
||||
hasLiveCareRequest = true;
|
||||
} else {
|
||||
hasLiveCareRequest = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.catchError((err) {
|
||||
print(err);
|
||||
})
|
||||
.showProgressBar(
|
||||
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6))
|
||||
.then((value) {
|
||||
setState(() {
|
||||
isDataLoaded = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/ClinicsServiceTimingsResponse.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ClinicTimingsDialog extends StatefulWidget {
|
||||
final clinicName;
|
||||
final List<PatientERGetClinicsServiceTimingsList>
|
||||
patientERGetClinicsServiceTimingsList;
|
||||
|
||||
ClinicTimingsDialog(
|
||||
{@required this.clinicName,
|
||||
@required this.patientERGetClinicsServiceTimingsList});
|
||||
|
||||
@override
|
||||
_ClinicTimingsDialogState createState() => _ClinicTimingsDialogState();
|
||||
}
|
||||
|
||||
class _ClinicTimingsDialogState extends State<ClinicTimingsDialog> {
|
||||
@override
|
||||
void initState() {
|
||||
print(widget.patientERGetClinicsServiceTimingsList);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var size = MediaQuery.of(context).size;
|
||||
final double itemHeight = ((size.height - kToolbarHeight - 24) * 0.42) / 2;
|
||||
final double itemWidth = size.width / 2;
|
||||
return Container(
|
||||
child: Dialog(
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.68,
|
||||
margin: EdgeInsets.all(20.0),
|
||||
width: 450.0,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(bottom: 10.0),
|
||||
child: Text("Clinic Schedule",
|
||||
textAlign: TextAlign.center,
|
||||
style:
|
||||
TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
Divider(
|
||||
thickness: 1.0,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(bottom: 20.0, top: 10.0),
|
||||
child: Text(widget.clinicName,
|
||||
style:
|
||||
TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
CustomScrollView(
|
||||
primary: false,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
slivers: <Widget>[
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
|
||||
sliver: SliverGrid.count(
|
||||
crossAxisCount: 2,
|
||||
childAspectRatio: (itemWidth / itemHeight),
|
||||
children: widget.patientERGetClinicsServiceTimingsList
|
||||
.map((e) => Container(
|
||||
height: 10.0,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Text(e.dayOfWeekStr),
|
||||
Text(e.shiftTimings[0].startTime +
|
||||
" - " +
|
||||
e.shiftTimings[0].endTime),
|
||||
],
|
||||
),
|
||||
))
|
||||
.toList()),
|
||||
),
|
||||
],
|
||||
),
|
||||
Divider(
|
||||
thickness: 1.0,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
height: 30.0,
|
||||
child: Flex(
|
||||
direction: Axis.horizontal,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Container(
|
||||
child: Text("OK",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,212 @@
|
||||
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
|
||||
import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart';
|
||||
import 'package:diplomaticquarterapp/models/FamilyFiles/PatientERVirtualHistoryResponse.dart';
|
||||
import 'package:diplomaticquarterapp/pages/feedback/feedback_home_page.dart';
|
||||
import 'package:diplomaticquarterapp/services/livecare_services/livecare_provider.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||
import 'package:diplomaticquarterapp/widgets/dialogs/confirm_dialog.dart';
|
||||
import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:smart_progress_bar/smart_progress_bar.dart';
|
||||
|
||||
class LiveCareHistoryCard extends StatefulWidget {
|
||||
ErRequestHistoryList erRequestHistoryList;
|
||||
|
||||
LiveCareHistoryCard({this.erRequestHistoryList});
|
||||
|
||||
@override
|
||||
_LiveCareHistoryCardState createState() => _LiveCareHistoryCardState();
|
||||
}
|
||||
|
||||
class _LiveCareHistoryCardState extends State<LiveCareHistoryCard> {
|
||||
AuthenticatedUser authUser = new AuthenticatedUser();
|
||||
AppSharedPreferences sharedPref = AppSharedPreferences();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
getAuthenticatedUser();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.all(10.0),
|
||||
child: Card(
|
||||
margin: EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 8.0),
|
||||
color: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height * 0.22,
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text("Requested date:",
|
||||
style:
|
||||
TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(widget.erRequestHistoryList.sArrivalTime,
|
||||
style: TextStyle(fontSize: 14.0)),
|
||||
Text(
|
||||
"Call Duration\n" +
|
||||
getCallTime(
|
||||
widget.erRequestHistoryList.callDuration),
|
||||
textAlign: TextAlign.center,
|
||||
style:
|
||||
TextStyle(fontSize: 14.0, color: Colors.grey[600])),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.all(7.0),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||
color: Colors.green,
|
||||
),
|
||||
margin: EdgeInsets.only(top: 5.0, bottom: 5.0),
|
||||
child: Text(widget.erRequestHistoryList.stringCallStatus,
|
||||
style: TextStyle(fontSize: 14.0, color: Colors.white)),
|
||||
),
|
||||
Divider(
|
||||
color: Colors.grey[500],
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.all(5.0),
|
||||
child: Flex(
|
||||
direction: Axis.horizontal,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
print("Invoice");
|
||||
openInvoice();
|
||||
},
|
||||
child: Container(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.content_paste, color: Colors.blue),
|
||||
Container(
|
||||
margin: EdgeInsets.only(left: 10.0),
|
||||
child: Text("Invoice",
|
||||
textAlign: TextAlign.center),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
print("Complaints");
|
||||
openComplaint();
|
||||
},
|
||||
child: Container(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.add, size: 24.0, color: Colors.red),
|
||||
Container(
|
||||
margin: EdgeInsets.only(left: 10.0),
|
||||
child: Text("Complaints",
|
||||
textAlign: TextAlign.center),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
print("Rate Dr & Appointment");
|
||||
},
|
||||
child: Container(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(Icons.star,
|
||||
size: 24.0, color: Colors.yellow[700]),
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.2,
|
||||
margin: EdgeInsets.only(left: 10.0),
|
||||
child: Text("Rate Dr & Appointment",
|
||||
overflow: TextOverflow.clip,
|
||||
textAlign: TextAlign.center),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
openInvoice() {
|
||||
ConfirmDialog dialog = new ConfirmDialog(
|
||||
context: context,
|
||||
confirmMessage: "Send a copy of this invoice to the email: " +
|
||||
authUser.emailAddress,
|
||||
okText: TranslationBase.of(context).confirm,
|
||||
cancelText: TranslationBase.of(context).cancel_nocaps,
|
||||
okFunction: () => {sendInvoiceEmail(context)},
|
||||
cancelFunction: () => {});
|
||||
dialog.showAlertDialog(context);
|
||||
}
|
||||
|
||||
openComplaint() {
|
||||
Navigator.push(context, FadePage(page: FeedbackHomePage()));
|
||||
}
|
||||
|
||||
sendInvoiceEmail(context) {
|
||||
ConfirmDialog.closeAlertDialog(context);
|
||||
LiveCareService service = new LiveCareService();
|
||||
service
|
||||
.sendLiveCareInvoiceEmail(
|
||||
widget.erRequestHistoryList.appointmentNo.toString(),
|
||||
widget.erRequestHistoryList.projectID,
|
||||
authUser.emailAddress,
|
||||
context)
|
||||
.then((res) {
|
||||
AppToast.showSuccessToast(message: "LiveCare invoice sent successfully");
|
||||
}).catchError((err) {
|
||||
AppToast.showErrorToast(message: err);
|
||||
print(err);
|
||||
}).showProgressBar(
|
||||
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6));
|
||||
}
|
||||
|
||||
getAuthenticatedUser() async {
|
||||
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||
var data = AuthenticatedUser.fromJson(
|
||||
await this.sharedPref.getObject(USER_PROFILE));
|
||||
setState(() {
|
||||
authUser = data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getCallTime(int number) {
|
||||
number = number.round();
|
||||
var hours = (number / 60 / 60).floor();
|
||||
var minutes = (number / 60).floor() - (hours * 60).floor();
|
||||
var seconds = number % 60;
|
||||
return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LiveCareInfoDialog extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Dialog(
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.61,
|
||||
margin: EdgeInsets.all(20.0),
|
||||
width: 450.0,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(bottom: 10.0),
|
||||
child: Icon(Icons.info_outline,
|
||||
size: 80.0, color: Colors.red[900]),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(bottom: 20.0),
|
||||
child: Text("Important Instructions",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 24.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.red[900])),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(bottom: 20.0),
|
||||
child: Text(
|
||||
"Please make sure that you're logged in on Al Habib Mobile Application",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black)),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(bottom: 20.0),
|
||||
child: Icon(Icons.not_interested,
|
||||
size: 80.0, color: Colors.red[900]),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(bottom: 40.0),
|
||||
child: Text(
|
||||
"Otherwise, you will not receive the doctor's call.",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 22.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.red[900])),
|
||||
),
|
||||
Divider(
|
||||
thickness: 1.0,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
height: 40.0,
|
||||
child: Flex(
|
||||
direction: Axis.horizontal,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
child: Container(
|
||||
child: Text("Cancel",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0, color: Colors.red[700])),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
child: Container(
|
||||
child: Text("Ok",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,296 @@
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/ERAppointmentFeesResponse.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LiveCarePaymentDialog extends StatefulWidget {
|
||||
GetERAppointmentFeesList getERAppointmentFeesList;
|
||||
int waitingTime;
|
||||
String clinicName;
|
||||
|
||||
LiveCarePaymentDialog(
|
||||
{@required this.getERAppointmentFeesList, @required this.waitingTime, @required this.clinicName});
|
||||
|
||||
@override
|
||||
_LiveCarePaymentDialogState createState() => _LiveCarePaymentDialogState();
|
||||
}
|
||||
|
||||
class _LiveCarePaymentDialogState extends State<LiveCarePaymentDialog> {
|
||||
int _selected = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var size = MediaQuery.of(context).size;
|
||||
final double itemHeight = ((size.height - kToolbarHeight - 24) * 0.42) / 2;
|
||||
final double itemWidth = size.width / 2;
|
||||
|
||||
return Container(
|
||||
child: Dialog(
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.691,
|
||||
margin: EdgeInsets.all(20.0),
|
||||
width: 450.0,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(bottom: 10.0),
|
||||
child: Text("Online Consultation",
|
||||
textAlign: TextAlign.center,
|
||||
style:
|
||||
TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
Divider(
|
||||
thickness: 1.0,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
Flex(
|
||||
direction: Axis.horizontal,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text("Waiting time to start LiveCare consultation",
|
||||
textAlign: TextAlign.end,
|
||||
style: TextStyle(fontSize: 13.0)),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: Icon(Icons.access_time,
|
||||
size: 36.0, color: Colors.red[800]),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(widget.waitingTime.toString() + " Minutes",
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.red[900])),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(bottom: 10.0, top: 10.0),
|
||||
child: Text(widget.clinicName,
|
||||
textAlign: TextAlign.center,
|
||||
style:
|
||||
TextStyle(fontSize: 22.0, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black87,
|
||||
borderRadius: new BorderRadius.only(
|
||||
topLeft: const Radius.circular(5.0),
|
||||
topRight: const Radius.circular(5.0),
|
||||
),
|
||||
border: Border.all(color: Colors.black87)),
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(top: 5.0),
|
||||
padding: EdgeInsets.all(5.0),
|
||||
child: Text("Consultation fee",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white)),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: new BorderRadius.only(
|
||||
bottomLeft: const Radius.circular(5.0),
|
||||
bottomRight: const Radius.circular(5.0),
|
||||
),
|
||||
border: Border.all(color: Colors.black54)),
|
||||
child: Table(
|
||||
children: [
|
||||
TableRow(children: [
|
||||
TableCell(
|
||||
child: _getNormalText(
|
||||
TranslationBase.of(context).patientShareToDo)),
|
||||
TableCell(
|
||||
child: _getNormalText(
|
||||
widget.getERAppointmentFeesList.amount +
|
||||
" " +
|
||||
widget.getERAppointmentFeesList.currency)),
|
||||
]),
|
||||
TableRow(children: [
|
||||
TableCell(
|
||||
child: _getNormalText(
|
||||
TranslationBase.of(context).patientTaxToDo)),
|
||||
TableCell(
|
||||
child: _getNormalText(
|
||||
widget.getERAppointmentFeesList.tax +
|
||||
" " +
|
||||
widget.getERAppointmentFeesList.currency)),
|
||||
]),
|
||||
TableRow(children: [
|
||||
TableCell(
|
||||
child: _getMarginText(TranslationBase.of(context)
|
||||
.patientShareTotalToDo)),
|
||||
TableCell(
|
||||
child: _getMarginText(
|
||||
widget.getERAppointmentFeesList.total +
|
||||
" " +
|
||||
widget.getERAppointmentFeesList.currency)),
|
||||
]),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 10.0, bottom: 10.0, left: 10.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: new BorderRadius.all(
|
||||
const Radius.circular(5.0),
|
||||
),
|
||||
color: Colors.green[200].withOpacity(0.5)),
|
||||
margin: EdgeInsets.only(top: 20.0),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Image.asset("assets/images/new-design/alert-triangle.png"),
|
||||
Container(
|
||||
margin: EdgeInsets.only(left: 10.0),
|
||||
width: MediaQuery.of(context).size.width * 0.55,
|
||||
child: Text(
|
||||
"If you're Insurance patient, you have only have to pay the co-payment",
|
||||
style: TextStyle(fontSize: 13.0)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 10.0),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: new Radio(
|
||||
value: 1,
|
||||
groupValue: _selected,
|
||||
onChanged: onRadioChanged,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: new Text(
|
||||
'I Accept the Terms And Conditions',
|
||||
style: new TextStyle(fontSize: 14.0),
|
||||
),
|
||||
),
|
||||
// Container(
|
||||
//// alignment: Alignment.centerRight,
|
||||
// child: new Text(
|
||||
// 'Click Here',
|
||||
// textAlign: TextAlign.end,
|
||||
// style: new TextStyle(fontSize: 16.0),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
thickness: 1.0,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(top: 10.0),
|
||||
child: new Text(
|
||||
'You can pay by the following Options:',
|
||||
textAlign: TextAlign.center,
|
||||
style: new TextStyle(fontSize: 14.0),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 5.0),
|
||||
child: Image.asset(
|
||||
"assets/images/new-design/payment_options_invoice_confirmation.png",
|
||||
width: 300),
|
||||
),
|
||||
Divider(
|
||||
thickness: 1.0,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
height: 40.0,
|
||||
child: Flex(
|
||||
direction: Axis.horizontal,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
child: Container(
|
||||
child: Text("Cancel",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0, color: Colors.red[700])),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if(_selected == 0) {
|
||||
AppToast.showErrorToast(message: "Please accept terms & conditions to continue");
|
||||
} else {
|
||||
Navigator.pop(context, true);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
child: Text("Ok",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onRadioChanged(int value) {
|
||||
setState(() {
|
||||
_selected = value;
|
||||
});
|
||||
}
|
||||
|
||||
_getNormalText(text) {
|
||||
return Container(
|
||||
margin: EdgeInsets.only(top: 10.0, right: 10.0),
|
||||
child: Text(text,
|
||||
textAlign: TextAlign.end,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Open-Sans',
|
||||
letterSpacing: 0.5,
|
||||
color: Colors.black)),
|
||||
);
|
||||
}
|
||||
|
||||
_getMarginText(text) {
|
||||
return Container(
|
||||
margin: EdgeInsets.only(top: 10.0, right: 10.0, bottom: 10.0),
|
||||
child: Text(text,
|
||||
textAlign: TextAlign.end,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Open-Sans',
|
||||
letterSpacing: 0.5,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,167 @@
|
||||
import 'package:circular_countdown_timer/circular_countdown_timer.dart';
|
||||
import 'package:diplomaticquarterapp/models/FamilyFiles/PatientERVirtualHistoryResponse.dart';
|
||||
import 'package:diplomaticquarterapp/services/livecare_services/livecare_provider.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/date_uitl.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:smart_progress_bar/smart_progress_bar.dart';
|
||||
|
||||
class LiveCarePendingRequest extends StatefulWidget {
|
||||
ErRequestHistoryList pendingERRequestHistoryList;
|
||||
final Function getLiveCareHistory;
|
||||
|
||||
LiveCarePendingRequest(
|
||||
{@required this.getLiveCareHistory, this.pendingERRequestHistoryList});
|
||||
|
||||
@override
|
||||
_LiveCarePendingRequestState createState() => _LiveCarePendingRequestState();
|
||||
}
|
||||
|
||||
class _LiveCarePendingRequestState extends State<LiveCarePendingRequest> {
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey[300]),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Colors.white,
|
||||
shape: BoxShape.rectangle,
|
||||
),
|
||||
margin: EdgeInsets.all(15.0),
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Text("In Progress:",
|
||||
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(top: 10.0),
|
||||
child: Text("Estimated Waiting Time: ",
|
||||
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
Container(
|
||||
transform: Matrix4.translationValues(0.0, -50.0, 0.0),
|
||||
alignment: Alignment.center,
|
||||
child: CircularCountDownTimer(
|
||||
duration:
|
||||
widget.pendingERRequestHistoryList.watingtimeInteger * 60,
|
||||
width: MediaQuery.of(context).size.width / 3,
|
||||
height: MediaQuery.of(context).size.height / 3,
|
||||
color: Colors.white,
|
||||
fillColor: Colors.green[700],
|
||||
strokeWidth: 15.0,
|
||||
textStyle: TextStyle(
|
||||
fontSize: 22.0,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold),
|
||||
isReverse: true,
|
||||
isTimerTextShown: true,
|
||||
onComplete: () {
|
||||
print('Countdown Ended');
|
||||
},
|
||||
),
|
||||
),
|
||||
Container(
|
||||
transform: Matrix4.translationValues(0.0, -60.0, 0.0),
|
||||
child: Divider(
|
||||
color: Colors.grey[500],
|
||||
thickness: 0.7,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
transform: Matrix4.translationValues(0.0, -50.0, 0.0),
|
||||
child: Text("Requested date:",
|
||||
style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
Container(
|
||||
transform: Matrix4.translationValues(0.0, -30.0, 0.0),
|
||||
child: Text(
|
||||
DateUtil.getDateFormatted(
|
||||
widget.pendingERRequestHistoryList.arrivalTime),
|
||||
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
Container(
|
||||
transform: Matrix4.translationValues(0.0, -20.0, 0.0),
|
||||
padding: EdgeInsets.all(7.0),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||
color: Colors.red[800],
|
||||
),
|
||||
margin: EdgeInsets.only(top: 5.0, bottom: 5.0),
|
||||
child: Text(widget.pendingERRequestHistoryList.stringCallStatus,
|
||||
style: TextStyle(fontSize: 14.0, color: Colors.white)),
|
||||
),
|
||||
Container(
|
||||
transform: Matrix4.translationValues(0.0, 0.0, 0.0),
|
||||
child: Divider(
|
||||
color: Colors.grey[500],
|
||||
thickness: 0.7,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
transform: Matrix4.translationValues(0.0, 10.0, 0.0),
|
||||
child: Text(
|
||||
"Your turn is after " +
|
||||
widget.pendingERRequestHistoryList.patCount.toString() +
|
||||
" Patients",
|
||||
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
Container(
|
||||
transform: Matrix4.translationValues(0.0, 130.0, 0.0),
|
||||
alignment: Alignment.bottomCenter,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: ButtonTheme(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
minWidth: MediaQuery.of(context).size.width,
|
||||
height: 45.0,
|
||||
child: RaisedButton(
|
||||
color: Colors.red[800],
|
||||
textColor: Colors.white,
|
||||
disabledTextColor: Colors.white,
|
||||
disabledColor: new Color(0xFFbcc2c4),
|
||||
onPressed: () {
|
||||
cancelLiveCareRequest();
|
||||
},
|
||||
child: Text(TranslationBase.of(context).cancel,
|
||||
style: TextStyle(fontSize: 18.0)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
cancelLiveCareRequest() {
|
||||
LiveCareService service = new LiveCareService();
|
||||
service
|
||||
.cancelLiveCareRequest(widget.pendingERRequestHistoryList.vCID, context)
|
||||
.then((res) {
|
||||
AppToast.showSuccessToast(
|
||||
message: "LiveCare request cancelled successfully");
|
||||
})
|
||||
.catchError((err) {
|
||||
print(err);
|
||||
})
|
||||
.showProgressBar(
|
||||
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6))
|
||||
.then((value) {
|
||||
widget.getLiveCareHistory();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/ClinicsServiceTimingsResponse.dart';
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/LiveCareClinicsListResponse.dart';
|
||||
import 'package:diplomaticquarterapp/services/livecare_services/livecare_provider.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:smart_progress_bar/smart_progress_bar.dart';
|
||||
|
||||
import 'ClinicTimingsDialog.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class ClinicCard extends StatefulWidget {
|
||||
bool isSelected;
|
||||
final PatientERGetClinicsList patientERGetClinicsList;
|
||||
var languageID;
|
||||
|
||||
ClinicCard(
|
||||
{this.isSelected,
|
||||
this.languageID,
|
||||
@required this.patientERGetClinicsList});
|
||||
|
||||
@override
|
||||
_State createState() => _State();
|
||||
}
|
||||
|
||||
class _State extends State<ClinicCard> {
|
||||
ClinicsServiceTimingsResponse clinicsServiceTimingsResponse;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
clinicsServiceTimingsResponse = new ClinicsServiceTimingsResponse();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Card(
|
||||
margin: EdgeInsets.fromLTRB(13.0, 10.0, 8.0, 8.0),
|
||||
color: widget.isSelected ? Colors.blue : Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.8,
|
||||
padding: EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Text(
|
||||
widget.languageID == 'ar'
|
||||
? widget.patientERGetClinicsList.serviceNameN
|
||||
: widget.patientERGetClinicsList.serviceName,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color:
|
||||
widget.isSelected ? Colors.white : Colors.black)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
getClinicTimings(widget.patientERGetClinicsList);
|
||||
},
|
||||
child: Card(
|
||||
margin: EdgeInsets.fromLTRB(8.0, 10.0, 8.0, 8.0),
|
||||
color: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Icon(Icons.access_time, size: 26.0, color: Colors.red[800]),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
getClinicTimings(PatientERGetClinicsList patientERGetClinicsList) {
|
||||
LiveCareService service = new LiveCareService();
|
||||
service
|
||||
.getLivecareClinicTiming(patientERGetClinicsList.serviceID, context)
|
||||
.then((res) {
|
||||
if (res['MessageStatus'] == 1) {
|
||||
setState(() {
|
||||
clinicsServiceTimingsResponse =
|
||||
ClinicsServiceTimingsResponse.fromJson(res);
|
||||
print(clinicsServiceTimingsResponse
|
||||
.patientERGetClinicsServiceTimingsList.length);
|
||||
|
||||
showGeneralDialog(
|
||||
barrierColor: Colors.black.withOpacity(0.5),
|
||||
transitionBuilder: (context, a1, a2, widget) {
|
||||
final curvedValue =
|
||||
Curves.easeInOutBack.transform(a1.value) - 1.0;
|
||||
return Transform(
|
||||
transform:
|
||||
Matrix4.translationValues(0.0, curvedValue * 200, 0.0),
|
||||
child: Opacity(
|
||||
opacity: a1.value,
|
||||
child: ClinicTimingsDialog(
|
||||
clinicName: patientERGetClinicsList.serviceName,
|
||||
patientERGetClinicsServiceTimingsList:
|
||||
clinicsServiceTimingsResponse
|
||||
.patientERGetClinicsServiceTimingsList,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
transitionDuration: Duration(milliseconds: 500),
|
||||
barrierDismissible: true,
|
||||
barrierLabel: '',
|
||||
context: context,
|
||||
pageBuilder: (context, animation1, animation2) {});
|
||||
});
|
||||
} else {
|
||||
AppToast.showErrorToast(message: res['ErrorEndUserMessage']);
|
||||
}
|
||||
}).catchError((err) {
|
||||
print(err);
|
||||
}).showProgressBar(
|
||||
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,388 @@
|
||||
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
|
||||
import 'package:diplomaticquarterapp/models/Appointments/AppoimentAllHistoryResultList.dart';
|
||||
import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart';
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/ERAppointmentFeesResponse.dart';
|
||||
import 'package:diplomaticquarterapp/models/LiveCare/LiveCareClinicsListResponse.dart';
|
||||
import 'package:diplomaticquarterapp/pages/ToDoList/payment_method_select.dart';
|
||||
import 'package:diplomaticquarterapp/pages/livecare/widgets/LiveCareInfoDialog.dart';
|
||||
import 'package:diplomaticquarterapp/pages/livecare/widgets/LiveCarePaymentDialog.dart';
|
||||
import 'package:diplomaticquarterapp/pages/livecare/widgets/clinic_card.dart';
|
||||
import 'package:diplomaticquarterapp/services/appointment_services/GetDoctorsList.dart';
|
||||
import 'package:diplomaticquarterapp/services/authentication/auth_provider.dart';
|
||||
import 'package:diplomaticquarterapp/services/livecare_services/livecare_provider.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/utils.dart';
|
||||
import 'package:diplomaticquarterapp/widgets/in_app_browser/InAppBrowser.dart';
|
||||
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:smart_progress_bar/smart_progress_bar.dart';
|
||||
|
||||
class ClinicList extends StatefulWidget {
|
||||
final Function getLiveCareHistory;
|
||||
|
||||
ClinicList({@required this.getLiveCareHistory});
|
||||
|
||||
@override
|
||||
_clinic_listState createState() => _clinic_listState();
|
||||
}
|
||||
|
||||
class _clinic_listState extends State<ClinicList> {
|
||||
int currentSelectedIndex = 0;
|
||||
LiveCareClinicsListResponse liveCareClinicsListResponse;
|
||||
|
||||
bool isDataLoaded = false;
|
||||
var languageID;
|
||||
|
||||
int selectedClinicID = 1;
|
||||
String selectedClinicName = "-";
|
||||
|
||||
AppSharedPreferences sharedPref = AppSharedPreferences();
|
||||
|
||||
AuthenticatedUser authUser;
|
||||
AuthProvider authProvider = new AuthProvider();
|
||||
|
||||
MyInAppBrowser browser;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
liveCareClinicsListResponse = new LiveCareClinicsListResponse();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// Future.delayed(new Duration(milliseconds: 1200), () {
|
||||
getLiveCareClinicsList();
|
||||
// });
|
||||
});
|
||||
getLanguageID();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppScaffold(
|
||||
isShowAppBar: false,
|
||||
body: SingleChildScrollView(
|
||||
child: isDataLoaded
|
||||
? Container(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.all(15.0),
|
||||
child: Text("Online Clinics: ",
|
||||
style: TextStyle(
|
||||
fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
ListView.builder(
|
||||
scrollDirection: Axis.vertical,
|
||||
shrinkWrap: true,
|
||||
physics: ScrollPhysics(),
|
||||
padding: EdgeInsets.all(0.0),
|
||||
itemCount: liveCareClinicsListResponse
|
||||
.patientERGetClinicsList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
updateSelectedIndex(liveCareClinicsListResponse
|
||||
.patientERGetClinicsList[index]);
|
||||
},
|
||||
child: ClinicCard(
|
||||
isSelected: selectedClinicID ==
|
||||
liveCareClinicsListResponse
|
||||
.patientERGetClinicsList[index]
|
||||
.serviceID
|
||||
? true
|
||||
: false,
|
||||
patientERGetClinicsList: liveCareClinicsListResponse
|
||||
.patientERGetClinicsList[index],
|
||||
languageID: languageID,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Container(
|
||||
height: 80.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
),
|
||||
bottomSheet: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: 50.0,
|
||||
margin: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
|
||||
child: ButtonTheme(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
minWidth: MediaQuery.of(context).size.width * 0.7,
|
||||
height: 45.0,
|
||||
child: RaisedButton(
|
||||
color: new Color(0xFF60686b),
|
||||
textColor: Colors.white,
|
||||
disabledTextColor: Colors.white,
|
||||
disabledColor: new Color(0xFFbcc2c4),
|
||||
onPressed: startLiveCare,
|
||||
child: Text("Start", style: TextStyle(fontSize: 18.0)),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void startLiveCare() {
|
||||
LiveCareService service = new LiveCareService();
|
||||
ERAppointmentFeesResponse erAppointmentFeesResponse =
|
||||
new ERAppointmentFeesResponse();
|
||||
service
|
||||
.getERAppointmentFees(selectedClinicID, context)
|
||||
.then((res) {
|
||||
erAppointmentFeesResponse = ERAppointmentFeesResponse.fromJson(res);
|
||||
})
|
||||
.catchError((err) {
|
||||
print(err);
|
||||
})
|
||||
.showProgressBar(
|
||||
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6))
|
||||
.then((value) {
|
||||
getERAppointmentTime(
|
||||
erAppointmentFeesResponse.getERAppointmentFeesList);
|
||||
});
|
||||
}
|
||||
|
||||
getERAppointmentTime(GetERAppointmentFeesList getERAppointmentFeesList) {
|
||||
LiveCareService service = new LiveCareService();
|
||||
service.getERAppointmentTime(selectedClinicID, context).then((res) {
|
||||
print(res['WatingtimeInteger']);
|
||||
showLiveCarePaymentDialog(
|
||||
getERAppointmentFeesList, res['WatingtimeInteger']);
|
||||
}).catchError((err) {
|
||||
print(err);
|
||||
}).showProgressBar(
|
||||
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6));
|
||||
}
|
||||
|
||||
showLiveCarePaymentDialog(
|
||||
GetERAppointmentFeesList getERAppointmentFeesList, int waitingTime) {
|
||||
showGeneralDialog(
|
||||
barrierColor: Colors.black.withOpacity(0.5),
|
||||
transitionBuilder: (context, a1, a2, widget) {
|
||||
final curvedValue =
|
||||
Curves.easeInOutBack.transform(a1.value) - 1.0;
|
||||
return Transform(
|
||||
transform:
|
||||
Matrix4.translationValues(0.0, curvedValue * 200, 0.0),
|
||||
child: Opacity(
|
||||
opacity: a1.value,
|
||||
child: LiveCarePaymentDialog(
|
||||
getERAppointmentFeesList: getERAppointmentFeesList,
|
||||
waitingTime: waitingTime,
|
||||
clinicName: selectedClinicName),
|
||||
),
|
||||
);
|
||||
},
|
||||
transitionDuration: Duration(milliseconds: 500),
|
||||
barrierDismissible: true,
|
||||
barrierLabel: '',
|
||||
context: context,
|
||||
pageBuilder: (context, animation1, animation2) {})
|
||||
.then((value) {
|
||||
if (value) {
|
||||
if (getERAppointmentFeesList.total == "0" ||
|
||||
getERAppointmentFeesList.total == "0.0") {
|
||||
showLiveCareInfoDialog(getERAppointmentFeesList);
|
||||
} else {
|
||||
navigateToPaymentMethod(getERAppointmentFeesList, context);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
showLiveCareInfoDialog(
|
||||
GetERAppointmentFeesList getERAppointmentFeesList) async {
|
||||
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||
var data = AuthenticatedUser.fromJson(
|
||||
await this.sharedPref.getObject(USER_PROFILE));
|
||||
setState(() {
|
||||
authUser = data;
|
||||
});
|
||||
}
|
||||
|
||||
showGeneralDialog(
|
||||
barrierColor: Colors.black.withOpacity(0.5),
|
||||
transitionBuilder: (context, a1, a2, widget) {
|
||||
final curvedValue =
|
||||
Curves.easeInOutBack.transform(a1.value) - 1.0;
|
||||
return Transform(
|
||||
transform:
|
||||
Matrix4.translationValues(0.0, curvedValue * 200, 0.0),
|
||||
child: Opacity(
|
||||
opacity: a1.value,
|
||||
child: LiveCareInfoDialog(),
|
||||
),
|
||||
);
|
||||
},
|
||||
transitionDuration: Duration(milliseconds: 500),
|
||||
barrierDismissible: true,
|
||||
barrierLabel: '',
|
||||
context: context,
|
||||
pageBuilder: (context, animation1, animation2) {})
|
||||
.then((value) {
|
||||
if (value) {
|
||||
if (getERAppointmentFeesList.total == "0" ||
|
||||
getERAppointmentFeesList.total == "0.0") {
|
||||
addNewCallForPatientER(authUser.patientID.toString() +
|
||||
"" +
|
||||
DateTime.now().millisecondsSinceEpoch.toString());
|
||||
} else {
|
||||
navigateToPaymentMethod(getERAppointmentFeesList, context);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future navigateToPaymentMethod(
|
||||
GetERAppointmentFeesList getERAppointmentFeesList, context) async {
|
||||
AppoitmentAllHistoryResultList appo = new AppoitmentAllHistoryResultList();
|
||||
|
||||
appo.clinicID = selectedClinicID;
|
||||
appo.appointmentNo = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||
var data = AuthenticatedUser.fromJson(
|
||||
await this.sharedPref.getObject(USER_PROFILE));
|
||||
setState(() {
|
||||
authUser = data;
|
||||
});
|
||||
}
|
||||
|
||||
Navigator.push(
|
||||
context, MaterialPageRoute(builder: (context) => PaymentMethod()))
|
||||
.then((value) {
|
||||
print(value);
|
||||
if (value != null) {
|
||||
openPayment(value, authUser,
|
||||
double.parse(getERAppointmentFeesList.total), appo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openPayment(String paymentMethod, AuthenticatedUser authenticatedUser,
|
||||
double amount, AppoitmentAllHistoryResultList appo) {
|
||||
browser = new MyInAppBrowser(
|
||||
onExitCallback: onBrowserExit,
|
||||
appo: appo,
|
||||
onLoadStartCallback: onBrowserLoadStart);
|
||||
|
||||
browser.openPaymentBrowser(
|
||||
amount,
|
||||
"LiveCare Payment",
|
||||
Utils.getAppointmentTransID(12, appo.clinicID, appo.appointmentNo),
|
||||
"12",
|
||||
authenticatedUser.emailAddress,
|
||||
paymentMethod,
|
||||
authenticatedUser,
|
||||
browser);
|
||||
}
|
||||
|
||||
onBrowserLoadStart(String url) {
|
||||
print("onBrowserLoadStart");
|
||||
print(url);
|
||||
|
||||
MyInAppBrowser.successURLS.forEach((element) {
|
||||
if (url.contains(element)) {
|
||||
if (browser.isOpened()) browser.close();
|
||||
MyInAppBrowser.isPaymentDone = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
MyInAppBrowser.errorURLS.forEach((element) {
|
||||
if (url.contains(element)) {
|
||||
if (browser.isOpened()) browser.close();
|
||||
MyInAppBrowser.isPaymentDone = false;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onBrowserExit(AppoitmentAllHistoryResultList appo, bool isPaymentMade) {
|
||||
print("onBrowserExit Called!!!!");
|
||||
if (isPaymentMade) checkPaymentStatus(appo);
|
||||
}
|
||||
|
||||
checkPaymentStatus(AppoitmentAllHistoryResultList appo) {
|
||||
DoctorsListService service = new DoctorsListService();
|
||||
service
|
||||
.checkPaymentStatus(
|
||||
Utils.getAppointmentTransID(
|
||||
appo.projectID, appo.clinicID, appo.appointmentNo),
|
||||
context)
|
||||
.then((res) {
|
||||
print("Printing Payment Status Reponse!!!!");
|
||||
print(res);
|
||||
String paymentInfo = res['Response_Message'];
|
||||
if (paymentInfo == 'Success') {
|
||||
addNewCallForPatientER(Utils.getAppointmentTransID(
|
||||
appo.projectID, appo.clinicID, appo.appointmentNo));
|
||||
} else {
|
||||
AppToast.showErrorToast(message: res['Response_Message']);
|
||||
}
|
||||
}).catchError((err) {
|
||||
print(err);
|
||||
}).showProgressBar(
|
||||
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6));
|
||||
}
|
||||
|
||||
addNewCallForPatientER(String clientRequestID) {
|
||||
LiveCareService service = new LiveCareService();
|
||||
service
|
||||
.addNewCallForPatientER(selectedClinicID, clientRequestID, context)
|
||||
.then((res) {
|
||||
AppToast.showSuccessToast(
|
||||
message: "New Call has been added successfully");
|
||||
}).catchError((err) {
|
||||
print(err);
|
||||
}).showProgressBar(
|
||||
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6)).then((value) {
|
||||
widget.getLiveCareHistory();
|
||||
});
|
||||
}
|
||||
|
||||
getLanguageID() async {
|
||||
languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||
}
|
||||
|
||||
getLiveCareClinicsList() {
|
||||
isDataLoaded = false;
|
||||
LiveCareService service = new LiveCareService();
|
||||
service.getLivecareClinics(context).then((res) {
|
||||
print(res['PatientER_GetClinicsList'].length);
|
||||
if (res['MessageStatus'] == 1) {
|
||||
setState(() {
|
||||
liveCareClinicsListResponse =
|
||||
LiveCareClinicsListResponse.fromJson(res);
|
||||
print(liveCareClinicsListResponse.patientERGetClinicsList.length);
|
||||
selectedClinicID =
|
||||
liveCareClinicsListResponse.patientERGetClinicsList[0].serviceID;
|
||||
selectedClinicName = liveCareClinicsListResponse
|
||||
.patientERGetClinicsList[0].serviceName;
|
||||
isDataLoaded = true;
|
||||
});
|
||||
} else {
|
||||
isDataLoaded = true;
|
||||
AppToast.showErrorToast(message: res['ErrorEndUserMessage']);
|
||||
}
|
||||
}).catchError((err) {
|
||||
print(err);
|
||||
}).showProgressBar(
|
||||
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6));
|
||||
}
|
||||
|
||||
updateSelectedIndex(PatientERGetClinicsList patientERGetClinicsList) {
|
||||
setState(() {
|
||||
selectedClinicID = patientERGetClinicsList.serviceID;
|
||||
selectedClinicName = patientERGetClinicsList.serviceName;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
import 'package:diplomaticquarterapp/models/FamilyFiles/PatientERVirtualHistoryResponse.dart';
|
||||
import 'package:diplomaticquarterapp/pages/livecare/widgets/LiveCareHistoryCard.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LiveCareLogs extends StatefulWidget {
|
||||
List<ErRequestHistoryList> erRequestHistoryList;
|
||||
|
||||
LiveCareLogs({@required this.erRequestHistoryList});
|
||||
|
||||
@override
|
||||
_LiveCareLogsState createState() => _LiveCareLogsState();
|
||||
}
|
||||
|
||||
class _LiveCareLogsState extends State<LiveCareLogs> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Container(
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.vertical,
|
||||
shrinkWrap: true,
|
||||
physics: ScrollPhysics(),
|
||||
padding: EdgeInsets.all(0.0),
|
||||
itemCount: widget.erRequestHistoryList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return widget.erRequestHistoryList[index].callStatus < 4
|
||||
? Container()
|
||||
: LiveCareHistoryCard(
|
||||
erRequestHistoryList: widget.erRequestHistoryList[index]);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,304 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:diplomaticquarterapp/config/config.dart';
|
||||
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
|
||||
import 'package:diplomaticquarterapp/core/service/base_service.dart';
|
||||
import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart';
|
||||
import 'package:diplomaticquarterapp/models/Request.dart';
|
||||
import 'package:diplomaticquarterapp/services/authentication/auth_provider.dart';
|
||||
import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LiveCareService extends BaseService {
|
||||
AppSharedPreferences sharedPref = AppSharedPreferences();
|
||||
AppGlobal appGlobal = new AppGlobal();
|
||||
|
||||
AuthenticatedUser authUser = new AuthenticatedUser();
|
||||
AuthProvider authProvider = new AuthProvider();
|
||||
|
||||
Future<Map> getLivecareClinics(BuildContext context) async {
|
||||
Map<String, dynamic> request;
|
||||
|
||||
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||
var data = AuthenticatedUser.fromJson(
|
||||
await this.sharedPref.getObject(USER_PROFILE));
|
||||
authUser = data;
|
||||
}
|
||||
|
||||
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||
Request req = appGlobal.getPublicRequest();
|
||||
request = {
|
||||
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||
"IPAdress": "10.20.10.20",
|
||||
"VersionID": req.VersionID,
|
||||
"Channel": req.Channel,
|
||||
"generalid": 'Cs2020@2016\$2958',
|
||||
"PatientOutSA": 0,
|
||||
"TokenID": "",
|
||||
"DeviceTypeID": req.DeviceTypeID,
|
||||
"SessionID": "YckwoXhUmWBsnHKEKig",
|
||||
"Age": authUser.age != null ? authUser.age : 0,
|
||||
"PatientID": authUser.patientID != null ? authUser.patientID : 0,
|
||||
"Gender": authUser.gender != null ? authUser.gender : 0
|
||||
};
|
||||
|
||||
dynamic localRes;
|
||||
|
||||
await baseAppClient.post(GET_LIVECARE_CLINICS,
|
||||
onSuccess: (response, statusCode) async {
|
||||
localRes = response;
|
||||
}, onFailure: (String error, int statusCode) {
|
||||
throw error;
|
||||
}, body: request);
|
||||
return Future.value(localRes);
|
||||
}
|
||||
|
||||
Future<Map> getLivecareHistory(BuildContext context) async {
|
||||
Map<String, dynamic> request;
|
||||
|
||||
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||
var data = AuthenticatedUser.fromJson(
|
||||
await this.sharedPref.getObject(USER_PROFILE));
|
||||
authUser = data;
|
||||
}
|
||||
|
||||
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||
// Request req = appGlobal.getPublicRequest();
|
||||
request = {
|
||||
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||
"TokenID": "",
|
||||
"SessionID": "YckwoXhUmWBsnHKEKig"
|
||||
};
|
||||
|
||||
dynamic localRes;
|
||||
|
||||
await baseAppClient.post(GET_LIVECARE_HISTORY,
|
||||
onSuccess: (response, statusCode) async {
|
||||
localRes = response;
|
||||
}, onFailure: (String error, int statusCode) {
|
||||
throw error;
|
||||
}, body: request);
|
||||
return Future.value(localRes);
|
||||
}
|
||||
|
||||
Future<Map> getLivecareClinicTiming(
|
||||
int serviceID, BuildContext context) async {
|
||||
Map<String, dynamic> request;
|
||||
|
||||
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||
var data = AuthenticatedUser.fromJson(
|
||||
await this.sharedPref.getObject(USER_PROFILE));
|
||||
authUser = data;
|
||||
}
|
||||
|
||||
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||
Request req = appGlobal.getPublicRequest();
|
||||
request = {
|
||||
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||
"IPAdress": "10.20.10.20",
|
||||
"VersionID": req.VersionID,
|
||||
"Channel": req.Channel,
|
||||
"generalid": 'Cs2020@2016\$2958',
|
||||
"PatientOutSA": 0,
|
||||
"ServiceID": serviceID,
|
||||
"DeviceTypeID": req.DeviceTypeID,
|
||||
"SessionID": "YckwoXhUmWBsnHKEKig",
|
||||
"Age": authUser.age != null ? authUser.age : 0,
|
||||
"PatientID": authUser.patientID != null ? authUser.patientID : 0,
|
||||
"Gender": authUser.gender != null ? authUser.gender : 0
|
||||
};
|
||||
|
||||
dynamic localRes;
|
||||
|
||||
await baseAppClient.post(GET_LIVECARE_CLINIC_TIMING,
|
||||
onSuccess: (response, statusCode) async {
|
||||
localRes = response;
|
||||
}, onFailure: (String error, int statusCode) {
|
||||
throw error;
|
||||
}, body: request);
|
||||
return Future.value(localRes);
|
||||
}
|
||||
|
||||
Future<Map> getERAppointmentFees(int serviceID, BuildContext context) async {
|
||||
Map<String, dynamic> request;
|
||||
|
||||
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||
var data = AuthenticatedUser.fromJson(
|
||||
await this.sharedPref.getObject(USER_PROFILE));
|
||||
authUser = data;
|
||||
}
|
||||
|
||||
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||
Request req = appGlobal.getPublicRequest();
|
||||
request = {
|
||||
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||
"IPAdress": "10.20.10.20",
|
||||
"VersionID": req.VersionID,
|
||||
"Channel": req.Channel,
|
||||
"generalid": 'Cs2020@2016\$2958',
|
||||
"PatientOutSA": 0,
|
||||
"ServiceID": serviceID,
|
||||
"ProjectID": 15,
|
||||
"DeviceTypeID": req.DeviceTypeID,
|
||||
"PatientType": authUser.patientType != null ? authUser.patientType : 0,
|
||||
"PatientTypeID": authUser.patientType != null ? authUser.patientType : 0,
|
||||
"SessionID": "YckwoXhUmWBsnHKEKig",
|
||||
"Age": authUser.age != null ? authUser.age : 0,
|
||||
"PatientID": authUser.patientID != null ? authUser.patientID : 0,
|
||||
"Gender": authUser.gender != null ? authUser.gender : 0
|
||||
};
|
||||
|
||||
dynamic localRes;
|
||||
|
||||
await baseAppClient.post(GET_ER_APPOINTMENT_FEES,
|
||||
onSuccess: (response, statusCode) async {
|
||||
localRes = response;
|
||||
}, onFailure: (String error, int statusCode) {
|
||||
throw error;
|
||||
}, body: request);
|
||||
return Future.value(localRes);
|
||||
}
|
||||
|
||||
Future<Map> getERAppointmentTime(int serviceID, BuildContext context) async {
|
||||
Map<String, dynamic> request;
|
||||
|
||||
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||
var data = AuthenticatedUser.fromJson(
|
||||
await this.sharedPref.getObject(USER_PROFILE));
|
||||
authUser = data;
|
||||
}
|
||||
|
||||
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||
Request req = appGlobal.getPublicRequest();
|
||||
request = {
|
||||
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||
"IPAdress": "10.20.10.20",
|
||||
"VersionID": req.VersionID,
|
||||
"Channel": req.Channel,
|
||||
"generalid": 'Cs2020@2016\$2958',
|
||||
"PatientOutSA": 0,
|
||||
"ServiceID": serviceID,
|
||||
"ProjectID": 15,
|
||||
"DeviceTypeID": req.DeviceTypeID,
|
||||
"PatientType": authUser.patientType != null ? authUser.patientType : 0,
|
||||
"PatientTypeID": authUser.patientType != null ? authUser.patientType : 0,
|
||||
"SessionID": "YckwoXhUmWBsnHKEKig",
|
||||
"Age": authUser.age != null ? authUser.age : 0,
|
||||
"PatientID": authUser.patientID != null ? authUser.patientID : 0,
|
||||
"Gender": authUser.gender != null ? authUser.gender : 0
|
||||
};
|
||||
|
||||
dynamic localRes;
|
||||
|
||||
await baseAppClient.post(GET_ER_APPOINTMENT_TIME,
|
||||
onSuccess: (response, statusCode) async {
|
||||
localRes = response;
|
||||
}, onFailure: (String error, int statusCode) {
|
||||
throw error;
|
||||
}, body: request);
|
||||
return Future.value(localRes);
|
||||
}
|
||||
|
||||
Future<Map> addNewCallForPatientER(
|
||||
int serviceID, String clientRequestID, BuildContext context) async {
|
||||
Map<String, dynamic> request;
|
||||
|
||||
String deviceToken;
|
||||
getDeviceToken().then((value) {
|
||||
print(value);
|
||||
deviceToken = value;
|
||||
});
|
||||
|
||||
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||
var data = AuthenticatedUser.fromJson(
|
||||
await this.sharedPref.getObject(USER_PROFILE));
|
||||
authUser = data;
|
||||
}
|
||||
|
||||
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||
Request req = appGlobal.getPublicRequest();
|
||||
request = {
|
||||
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||
"IPAdress": "10.20.10.20",
|
||||
"VersionID": req.VersionID,
|
||||
"Channel": req.Channel,
|
||||
"generalid": 'Cs2020@2016\$2958',
|
||||
"PatientOutSA": 0,
|
||||
"ErServiceID": serviceID,
|
||||
"ClientRequestID": clientRequestID,
|
||||
"DeviceToken": deviceToken,
|
||||
"VoipToken": "",
|
||||
"Latitude": "24.708488",
|
||||
"Longitude": "46.665925",
|
||||
"DeviceType": Platform.isIOS ? 'iOS' : 'Android',
|
||||
"PatientType": authUser.patientType != null ? authUser.patientType : 0,
|
||||
"PatientTypeID": authUser.patientType != null ? authUser.patientType : 0,
|
||||
"SessionID": "YckwoXhUmWBsnHKEKig",
|
||||
"Age": authUser.age != null ? authUser.age : 0,
|
||||
"PatientID": authUser.patientID != null ? authUser.patientID : 0,
|
||||
"Gender": authUser.gender != null ? authUser.gender : 0
|
||||
};
|
||||
|
||||
dynamic localRes;
|
||||
|
||||
await baseAppClient.post(ADD_NEW_CALL_FOR_PATIENT_ER,
|
||||
onSuccess: (response, statusCode) async {
|
||||
localRes = response;
|
||||
}, onFailure: (String error, int statusCode) {
|
||||
throw error;
|
||||
}, body: request);
|
||||
return Future.value(localRes);
|
||||
}
|
||||
|
||||
Future<String> getDeviceToken() async {
|
||||
String deviceToken = await sharedPref.getString(PUSH_TOKEN);
|
||||
return deviceToken;
|
||||
}
|
||||
|
||||
Future<Map> cancelLiveCareRequest(int vc_id, BuildContext context) async {
|
||||
Map<String, dynamic> request;
|
||||
|
||||
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||
var data = AuthenticatedUser.fromJson(
|
||||
await this.sharedPref.getObject(USER_PROFILE));
|
||||
authUser = data;
|
||||
}
|
||||
request = {"VCID": vc_id};
|
||||
|
||||
dynamic localRes;
|
||||
|
||||
await baseAppClient.post(CANCEL_LIVECARE_REQUEST,
|
||||
onSuccess: (response, statusCode) async {
|
||||
localRes = response;
|
||||
}, onFailure: (String error, int statusCode) {
|
||||
throw error;
|
||||
}, body: request);
|
||||
return Future.value(localRes);
|
||||
}
|
||||
|
||||
Future<Map> sendLiveCareInvoiceEmail(String appoNo, int projectID,
|
||||
String emailAddress, BuildContext context) async {
|
||||
Map<String, dynamic> request;
|
||||
|
||||
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||
var data = AuthenticatedUser.fromJson(
|
||||
await this.sharedPref.getObject(USER_PROFILE));
|
||||
authUser = data;
|
||||
}
|
||||
request = {
|
||||
"To": emailAddress,
|
||||
"ProjectID": projectID,
|
||||
"AppointmentNo": appoNo
|
||||
};
|
||||
|
||||
dynamic localRes;
|
||||
|
||||
await baseAppClient.post(SEND_LIVECARE_INVOICE_EMAIL,
|
||||
onSuccess: (response, statusCode) async {
|
||||
localRes = response;
|
||||
}, onFailure: (String error, int statusCode) {
|
||||
throw error;
|
||||
}, body: request);
|
||||
return Future.value(localRes);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NavigationService {
|
||||
final GlobalKey<NavigatorState> navigatorKey =
|
||||
new GlobalKey<NavigatorState>();
|
||||
|
||||
Future<dynamic> navigateTo(String routeName) {
|
||||
return navigatorKey.currentState.pushNamed(routeName);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AlertDialogBox {
|
||||
final BuildContext context;
|
||||
|
||||
final confirmMessage;
|
||||
final okText;
|
||||
final Function okFunction;
|
||||
|
||||
AlertDialogBox(
|
||||
{@required this.context,
|
||||
@required this.confirmMessage,
|
||||
@required this.okText,
|
||||
@required this.okFunction});
|
||||
|
||||
showAlertDialog(BuildContext context) {
|
||||
Widget continueButton =
|
||||
FlatButton(child: Text(this.okText), onPressed: this.okFunction);
|
||||
|
||||
// set up the AlertDialog
|
||||
AlertDialog alert = AlertDialog(
|
||||
title: Text(TranslationBase.of(context).confirm),
|
||||
content: Text(this.confirmMessage),
|
||||
actions: [
|
||||
continueButton,
|
||||
],
|
||||
);
|
||||
|
||||
// show the dialog
|
||||
showDialog(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return alert;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static closeAlertDialog(BuildContext context) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue