Merge branch 'merge-update-with-lab-changes' into update-to-3.32-sdk
# Conflicts: # android/app/local.properties # android/app/src/main/AndroidManifest.xml # android/gradle.properties # android/gradle/wrapper/gradle-wrapper.properties # ios/Podfile # ios/Runner.xcodeproj/project.pbxproj # ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme # ios/Runner/Info.plist # ios/Runner/Runner.entitlements # lib/config/localized_values.dart # lib/core/model/labs/LabOrderResult.dart # lib/core/model/labs/lab_result.dart # lib/core/service/client/base_app_client.dart # lib/core/service/medical/labs_service.dart # lib/core/viewModels/medical/labs_view_model.dart # lib/core/viewModels/pharmacyModule/PharmacyAddressesViewModel.dart # lib/core/viewModels/project_view_model.dart # lib/new_ui/exception_widget/ExceptionBottomSheet.dart # lib/pages/AlHabibMedicalService/ComprehensiveMedicalCheckup/NewCMC/cmc_location_page.dart # lib/pages/AlHabibMedicalService/ComprehensiveMedicalCheckup/NewCMC/new_cmc_step_tow_page.dart # lib/pages/AlHabibMedicalService/HomeHealthCare/NewHomeHealthCare/location_page.dart # lib/pages/AlHabibMedicalService/HomeHealthCare/NewHomeHealthCare/new_Home_health_care_step_one_page.dart # lib/pages/AlHabibMedicalService/HomeHealthCare/NewHomeHealthCare/new_Home_health_care_step_tow_page.dart # lib/pages/landing/landing_page.dart # lib/pages/login/forgot-password.dart # lib/pages/login/register_new.dart # lib/pages/login/saved_login.dart # lib/pages/login/welcome.dart # lib/pages/medical/labs/laboratory_result_page.dart # lib/uitl/app_toast.dart # lib/uitl/translations_delegate_base.dart # lib/uitl/utils.dart # lib/widgets/Loader/gif_loader_container.dart # lib/widgets/data_display/medical/LabResult/FlowChartPage.dart # lib/widgets/data_display/medical/LabResult/Lab_Result_details_wideget.dart # lib/widgets/data_display/medical/LabResult/laboratory_result_widget.dart # lib/widgets/in_app_browser/InAppBrowser.dart # lib/widgets/pickupLocation/PickupLocationFromMap.dart # pubspec.yamlpull/2/head
commit
f265c605ca
@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: f7a6a7906be96d2288f5d63a5a54c515a6e987fe
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
@ -0,0 +1,13 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Flutter",
|
||||
"request": "launch",
|
||||
"type": "dart"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICgjCCAWoCAQAwPTEjMCEGCSqGSIb3DQEJARYUSGFyb29uNjEzOEBnbWFpbC5j
|
||||
b20xCTAHBgNVBAMMADELMAkGA1UEBhMCQUUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQDHadAER62mSf+wOxPu9rF1EvifDxbAEv6znZIHvwimgShdPLLS
|
||||
rkFdN4VpSHMzrASqpHnlSL3wxO6eym9pATH3WfLvKIQxnn9OkO5fScjQi0RkFbe0
|
||||
JqTqN2LaFxrSFrhqQ/1p/izr690HqjrfquRI8t8evatyTku67/xcCj6uSIVLfJoS
|
||||
8YCDVn0Y+w9n2Vw/+kHiNY6P96qp/CfXZgMLFpUu0mqcmZl97htfmB9AH2tC45yh
|
||||
mKxDJiKEnk69zVCh/bkc9tFE1tUDkkKblJTCEyWfdDUUYM4ET3t3fBqWB78F4J3m
|
||||
XglByGZmHgY2+7nsSQBiq7k9tFN2c7FZ9BD3AgMBAAGgADANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEAJxqoyfEJ7V83ffCAqkYNNEcCQMIRi1Y4cTTm/5KZGA/UPeDeX48/bm4K
|
||||
XIf2zbv88OGASInBrvMDp0x0zs13Dx2yXxofGN+iQSBwN6jPV/upmm0DjgTmgUGh
|
||||
BJste70LaU228ADzWDw8UJm/GKjK8WvE0XOGpPKYq9cuVW3M+6QRoRHXy0eJc2PX
|
||||
VgopsuhHUyA5trxM7k4yc6GgYVx+fAwMaplovTwn5U8XK6bDEIvCjImfat/u4Wp8
|
||||
R31p0BQzQaeyWfpp9Ucn/fvQSeB+wtjhHuA1ukcyWY1kBYOyCMIHymFrJLaTpUj7
|
||||
aQFYZDV28HYv0yWhWIAD9Pu+aDHWpA==
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
@ -0,0 +1,74 @@
|
||||
Bag Attributes
|
||||
friendlyName: VoIP Services: com.HMG.HMG-Smartphone
|
||||
localKeyID: 70 4A 99 03 F8 D7 92 FA F7 2F DD 3B D8 BF 2C F0 BD CE 21 C8
|
||||
subject=/UID=com.HMG.HMG-Smartphone.voip/CN=VoIP Services: com.HMG.HMG-Smartphone/OU=3A359E86ZF/O=Dr Sulaiman Al Habib Medical Center/C=SA
|
||||
issuer=/CN=Apple Worldwide Developer Relations Certification Authority/OU=G4/O=Apple Inc./C=US
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGkTCCBXmgAwIBAgIQFB+/PYFlrZ6SCzSX9cqOBDANBgkqhkiG9w0BAQsFADB1
|
||||
MUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBD
|
||||
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTELMAkGA1UECwwCRzQxEzARBgNVBAoMCkFw
|
||||
cGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTI0MDYwOTEyMDc1MVoXDTI1MDcwOTEy
|
||||
MDc1MFowga0xKzApBgoJkiaJk/IsZAEBDBtjb20uSE1HLkhNRy1TbWFydHBob25l
|
||||
LnZvaXAxLjAsBgNVBAMMJVZvSVAgU2VydmljZXM6IGNvbS5ITUcuSE1HLVNtYXJ0
|
||||
cGhvbmUxEzARBgNVBAsMCjNBMzU5RTg2WkYxLDAqBgNVBAoMI0RyIFN1bGFpbWFu
|
||||
IEFsIEhhYmliIE1lZGljYWwgQ2VudGVyMQswCQYDVQQGEwJTQTCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAMdp0ARHraZJ/7A7E+72sXUS+J8PFsAS/rOd
|
||||
kge/CKaBKF08stKuQV03hWlIczOsBKqkeeVIvfDE7p7Kb2kBMfdZ8u8ohDGef06Q
|
||||
7l9JyNCLRGQVt7QmpOo3YtoXGtIWuGpD/Wn+LOvr3QeqOt+q5Ejy3x69q3JOS7rv
|
||||
/FwKPq5IhUt8mhLxgINWfRj7D2fZXD/6QeI1jo/3qqn8J9dmAwsWlS7SapyZmX3u
|
||||
G1+YH0Afa0LjnKGYrEMmIoSeTr3NUKH9uRz20UTW1QOSQpuUlMITJZ90NRRgzgRP
|
||||
e3d8GpYHvwXgneZeCUHIZmYeBjb7uexJAGKruT20U3ZzsVn0EPcCAwEAAaOCAuIw
|
||||
ggLeMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUW9n6HeeaGgujmXYiUIY+kchb
|
||||
d6gwcAYIKwYBBQUHAQEEZDBiMC0GCCsGAQUFBzAChiFodHRwOi8vY2VydHMuYXBw
|
||||
bGUuY29tL3d3ZHJnNC5kZXIwMQYIKwYBBQUHMAGGJWh0dHA6Ly9vY3NwLmFwcGxl
|
||||
LmNvbS9vY3NwMDMtd3dkcmc0MDUwggEeBgNVHSAEggEVMIIBETCCAQ0GCSqGSIb3
|
||||
Y2QFATCB/zCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRp
|
||||
ZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRo
|
||||
ZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1
|
||||
c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGlj
|
||||
ZSBzdGF0ZW1lbnRzLjA3BggrBgEFBQcCARYraHR0cHM6Ly93d3cuYXBwbGUuY29t
|
||||
L2NlcnRpZmljYXRlYXV0aG9yaXR5LzATBgNVHSUEDDAKBggrBgEFBQcDAjAyBgNV
|
||||
HR8EKzApMCegJaAjhiFodHRwOi8vY3JsLmFwcGxlLmNvbS93d2RyZzQtNi5jcmww
|
||||
HQYDVR0OBBYEFHBKmQP415L69y/dO9i/LPC9ziHIMA4GA1UdDwEB/wQEAwIHgDAQ
|
||||
BgoqhkiG92NkBgMBBAIFADAQBgoqhkiG92NkBgMCBAIFADBDBgoqhkiG92NkBgME
|
||||
BDUMM2NvbS5ITUcuSE1HLVNtYXJ0cGhvbmUsIGNvbS5ITUcuSE1HLVNtYXJ0cGhv
|
||||
bmUudm9pcDAQBgoqhkiG92NkBgMFBAIFADAmBgoqhkiG92NkBgMDBBgMFmNvbS5I
|
||||
TUcuSE1HLVNtYXJ0cGhvbmUwDQYJKoZIhvcNAQELBQADggEBAJAcIzSskk8/GbxQ
|
||||
7nGUPPEWFswL66xTt3GusHmeXkKDXG3ac+4jLk8yU7gunos77CNDm+9TkShdJ2Rg
|
||||
tTRopK4ockqwoPrCeEVrooHR4e42YvmkB6AfYKJRnQZQVJIpW0OES7vrtoWVGoNj
|
||||
ktHOV+wFIuAQPI9P4z0RFyRw85MHr+jFqxeLU/1500PwCxIiNHkIcUtzZgrnr22R
|
||||
7rgkxav+RrjLjXVQvC4VJispKCU7yR69Xv1rsC5zc7OTCuCkbQ9FvnfsiUz5ZDzp
|
||||
3D0gGojU9wmJ1yVk9HiZsuiVwmg/LbDHI3pSYbxz3n1UHB6Pu0jX3c3Uxkx674HD
|
||||
/0uvHzg=
|
||||
-----END CERTIFICATE-----
|
||||
Bag Attributes
|
||||
localKeyID: 70 4A 99 03 F8 D7 92 FA F7 2F DD 3B D8 BF 2C F0 BD CE 21 C8
|
||||
Key Attributes: <No Attributes>
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHadAER62mSf+w
|
||||
OxPu9rF1EvifDxbAEv6znZIHvwimgShdPLLSrkFdN4VpSHMzrASqpHnlSL3wxO6e
|
||||
ym9pATH3WfLvKIQxnn9OkO5fScjQi0RkFbe0JqTqN2LaFxrSFrhqQ/1p/izr690H
|
||||
qjrfquRI8t8evatyTku67/xcCj6uSIVLfJoS8YCDVn0Y+w9n2Vw/+kHiNY6P96qp
|
||||
/CfXZgMLFpUu0mqcmZl97htfmB9AH2tC45yhmKxDJiKEnk69zVCh/bkc9tFE1tUD
|
||||
kkKblJTCEyWfdDUUYM4ET3t3fBqWB78F4J3mXglByGZmHgY2+7nsSQBiq7k9tFN2
|
||||
c7FZ9BD3AgMBAAECggEBALTXH58SBcd86hS+rsgSDkJ2ruNIQsBzOWpYJjtCIi0l
|
||||
ih5r0lu/TWWk2SbuXhLpF95MhILLIf18Ck4UlU5rmA2SE/6RBrkc3KS6SII/no6J
|
||||
Db+Z9lu4g+wnyEyxkVAe7KTp/aGmsMROxMaTuQpnptkE8BSnuxhRKtpfksNBQzC2
|
||||
19OkEy4JAO7xWbm9w/ASiCtYf0Vj26DQo982Fe44cpG08iB2v1uUJINQkGzmg+85
|
||||
vvn5A1ECpSpgd/eOa68ojxeNSU7iFGZPbpjuincLPBFlC5zp9KD1Bt6p2KwIizBl
|
||||
GPYTp6a1DnWly6n0PuLN0fl/nRmI6ar48Ja1Qzk8NvECgYEA5iQo9lnjNOHjT6bk
|
||||
hj5QXMz3MDMCPdJo6jQyADtmsC/F4RjufujfXPhMv2v8JLAjQjNMgfhTSF68zYHf
|
||||
i1qW8e/CgWu7mGQEIhWtxiTkLou8zW3VjxwpgfeUsGI5L+PxoN53ZUVcetLz4Y58
|
||||
8ATEhlxh02uGmG2k3HbvFu9NBT8CgYEA3dHJfFX2xv53ZD1E2IE1rM3j7a32s7VB
|
||||
k/YX8ODrAJxwNmy/vm4gd5h6ofqVS1/IV4NHFbmQ5c1tzB44YGOEZH5Ta9hhHgHU
|
||||
xoa3q6t+kjR9KrQvxjTSe9mW5x/jAgU0Wp/XrAfMZxc2eukb4/sJAGKG0kyFeX5O
|
||||
5abVixWj7kkCgYAafd8ZPI+i7r2z1YpIybOGezGZIsYHR8k77HY1AKF5doZYuv35
|
||||
LeVIVmy/PDbyIjRs0mrkKymrEvy4y1JoQC1P/EFvlG8rw5eWNNikDmsV0JATNal3
|
||||
p84/X7iF9UiP8rVFx1YHGkzi9pWMSVKobmQQ82FvbNk5gqWPm0E6aqF7EQKBgQDB
|
||||
mE0PlT9SIdk1xTuG8g5UiUV6tS/xXCql5kyy0LqJx/8voO4gV6FCf/PX1wnhxPDn
|
||||
YA30agaKaiZ/1YhCp9hulMpd/3WTaOm5cu5uh7hqBiGP2b8wb/2cViWcBnpr0vsE
|
||||
dSkYXZ4KA3pq/3R2HyyWB2e/t0Vu6cwWJuuM/hAa8QKBgGNRStNVSMPvzx2X7mB6
|
||||
EqdlWqVbfSTHJiZKyNPWHlSuZgdQ14BRwq7KZ0ysZy+kOX4rh1rIfqzrKc62xEXB
|
||||
E+fmIpu+DUOM5ojEPCf9Om87r3fMJ54SjM7z1dtVc+Muy4TnFZ4UQcN9txWbnkAo
|
||||
++H5gC9DBedOEXkwoCYjoeuM
|
||||
-----END PRIVATE KEY-----
|
||||
Binary file not shown.
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,38 @@
|
||||
<?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>815750722565-da8p56le8bd6apsbm9eft0jjl1rtpgkt.apps.googleusercontent.com</string>
|
||||
<key>REVERSED_CLIENT_ID</key>
|
||||
<string>com.googleusercontent.apps.815750722565-da8p56le8bd6apsbm9eft0jjl1rtpgkt</string>
|
||||
<key>ANDROID_CLIENT_ID</key>
|
||||
<string>815750722565-m14h8mkosm7cnq6uh6rhqr54dn02d705.apps.googleusercontent.com</string>
|
||||
<key>API_KEY</key>
|
||||
<string>AIzaSyDiXnCO00li4V7Ioa2YZ_M4ECxRsu_P9tA</string>
|
||||
<key>GCM_SENDER_ID</key>
|
||||
<string>815750722565</string>
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>com.HMG.Smartphone</string>
|
||||
<key>PROJECT_ID</key>
|
||||
<string>api-project-815750722565</string>
|
||||
<key>STORAGE_BUCKET</key>
|
||||
<string>api-project-815750722565.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:815750722565:ios:328ec247a81a2ca23c186c</string>
|
||||
<key>DATABASE_URL</key>
|
||||
<string>https://api-project-815750722565.firebaseio.com</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -1,7 +1,10 @@
|
||||
org.gradle.jvmargs=-Xmx4096m
|
||||
kotlin.daemon.jvmargs=-Xmx4096m
|
||||
#android.enableR8=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.suppressUnsupportedCompileSdk=33
|
||||
MAPBOX_USER_NAME = "mapbox"
|
||||
MAPBOX_DOWNLOADS_TOKEN="sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg"
|
||||
#MAPBOX_DOWNLOADS_TOKEN="pk.eyJ1IjoicndhaWQiLCJhIjoiY2x5cGo4aHNjMGNsbTJyc2djempobGQxaSJ9.RCaC6WrUt4A4YnZNfxnONQ"
|
||||
kotlin.jvm.target.validation.mode=IGNORE
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#Wed Jul 02 15:26:33 AST 2025
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
<svg height="1792" id="download" width="1792" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M1344 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm256 0q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128-224v320q0 40-28 68t-68 28H160q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zm-325-569q17 41-14 70l-448 448q-18 19-45 19t-45-19L403 621q-31-29-14-70 17-39 59-39h256V64q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 531 B |
@ -0,0 +1,5 @@
|
||||
<svg id="Download" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
class="color000000 svgShape"
|
||||
d="M1344 1344q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm256 0q0-26-19-45t-45-19-45 19-19 45 19 45 45 19 45-19 19-45zm128-224v320q0 40-28 68t-68 28H160q-40 0-68-28t-28-68v-320q0-40 28-68t68-28h465l135 136q58 56 136 56t136-56l136-136h464q40 0 68 28t28 68zm-325-569q17 41-14 70l-448 448q-18 19-45 19t-45-19L403 621q-31-29-14-70 17-39 59-39h256V64q0-26 19-45t45-19h256q26 0 45 19t19 45v448h256q42 0 59 39z" fill="#ffffff"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 580 B |
@ -0,0 +1 @@
|
||||
84018bae6147d7170a2e3bd8d9e051aa
|
||||
@ -0,0 +1,59 @@
|
||||
import 'package:hmg_patient_app/core/viewModels/medical/labs_view_model.dart';
|
||||
import 'package:hmg_patient_app/pages/base/base_view.dart';
|
||||
import 'package:hmg_patient_app/uitl/translations_delegate_base.dart';
|
||||
import 'package:hmg_patient_app/widgets/data_display/medical/LabResult/lab_result_graph.dart';
|
||||
import 'package:hmg_patient_app/widgets/others/app_scaffold_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FullScreenGraph extends StatelessWidget {
|
||||
final List<DataPoint> completeeGraphValues ;
|
||||
final List<ThresholdRange> threshold ;
|
||||
|
||||
const FullScreenGraph({super.key, required this.completeeGraphValues, required this.threshold});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppScaffold(
|
||||
isShowAppBar: true,
|
||||
appBarTitle: TranslationBase.of(context).labResult,
|
||||
showNewAppBar: true,
|
||||
showNewAppBarTitle: true,
|
||||
backgroundColor: Color(0xffF8F8F8),
|
||||
body: RotatedBox(
|
||||
quarterTurns: 1,
|
||||
child: SizedBox(
|
||||
// width: MediaQuery.sizeOf(context).height,
|
||||
height: MediaQuery.sizeOf(context).width,
|
||||
child: Material(
|
||||
color: Colors.white,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 18),
|
||||
child: DynamicResultChart(
|
||||
dataPoints: completeeGraphValues,
|
||||
thresholds: threshold,
|
||||
// width:((completeeGraphValues.length <=2)? MediaQuery.sizeOf(context).height : (MediaQuery.sizeOf(context).height* (
|
||||
// completeeGraphValues.length <= 3
|
||||
// ? 1
|
||||
// : (completeeGraphValues.length/3))
|
||||
// ) )-77,
|
||||
width: MediaQuery.sizeOf(context).height-100,
|
||||
maxX: completeeGraphValues.length.toDouble(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
height: MediaQuery.sizeOf(context).width,
|
||||
isFullScreenGraph: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
// double getMax(List<DataPoint> dataPoints) {
|
||||
// double max = double.negativeInfinity;
|
||||
// for (var point in dataPoints) {
|
||||
// if (point.value > max) {
|
||||
// max = point.y;
|
||||
// }
|
||||
// }
|
||||
// return max;
|
||||
// }
|
||||
}
|
||||
@ -0,0 +1,276 @@
|
||||
import 'package:hmg_patient_app/uitl/translations_delegate_base.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class DynamicResultChart extends StatelessWidget {
|
||||
final List<DataPoint> dataPoints;
|
||||
final List<ThresholdRange> thresholds;
|
||||
final double? width;
|
||||
final double height;
|
||||
final double? maxX;
|
||||
final Axis scrollDirection;
|
||||
final bool isFullScreenGraph;
|
||||
|
||||
const DynamicResultChart(
|
||||
{super.key, required this.dataPoints, required this.thresholds, required this.width, required this.scrollDirection, required this.height, this.maxX, this.isFullScreenGraph = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var minY = 0.0;
|
||||
var maxY = 0.0;
|
||||
if (thresholds.isNotEmpty) {
|
||||
print("the thresholds are $thresholds");
|
||||
minY = thresholds.first.value;
|
||||
maxY = thresholds.last.value + 1;
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
final spots = dataPoints.asMap().entries.map((entry) {
|
||||
return FlSpot(entry.key.toDouble(), entry.value.value);
|
||||
}).toList();
|
||||
final widthPerPoint = 30.0; // Customize as needed
|
||||
final chartWidth = (dataPoints.length + 1) * widthPerPoint;
|
||||
|
||||
return Material(
|
||||
color: Colors.white,
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
height: height,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0, bottom: 8),
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
minY: minY,
|
||||
maxY: maxY,
|
||||
minX: dataPoints.first.labelValue - 1,
|
||||
maxX: maxX,
|
||||
lineTouchData: LineTouchData(touchTooltipData: LineTouchTooltipData(getTooltipItems: (touchedSpots) {
|
||||
if (touchedSpots.isEmpty) return [];
|
||||
|
||||
// Only show tooltip for the first touched spot, hide others
|
||||
return touchedSpots.map((spot) {
|
||||
if (spot == touchedSpots.first) {
|
||||
final dataPoint = dataPoints[spot.x.toInt()];
|
||||
|
||||
if (dataPoint.isStringResource) {
|
||||
return LineTooltipItem(
|
||||
'${double.parse(dataPoint.actualValue).toStringAsFixed(2)}',
|
||||
const TextStyle(color: Colors.white),
|
||||
);
|
||||
}
|
||||
|
||||
return LineTooltipItem(
|
||||
// '${dataPoint.label} ${spot.y.toStringAsFixed(2)}',
|
||||
'${dataPoint.label} ${double.parse(dataPoint.actualValue).toStringAsFixed(2)}',
|
||||
const TextStyle(color: Colors.white),
|
||||
);
|
||||
}
|
||||
return null; // hides the rest
|
||||
}).toList();
|
||||
})),
|
||||
titlesData: FlTitlesData(
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
reservedSize: 77,
|
||||
interval: .1, // Let fl_chart handle it
|
||||
getTitlesWidget: (value, _) {
|
||||
// print("the value is ======== ${value}");
|
||||
|
||||
// Compare with 2-decimal precision to avoid close duplicates
|
||||
final matchingThreshold = thresholds.firstWhere(
|
||||
(t) => t.value.toStringAsFixed(1) == value.toStringAsFixed(1),
|
||||
orElse: () => ThresholdRange(label: '', value: 0, color: Colors.transparent, lineColor: Colors.transparent),
|
||||
);
|
||||
|
||||
var actualValue = (matchingThreshold.actualValue != null)
|
||||
? "${TranslationBase.of(context).getTranslation(matchingThreshold.label)} (${matchingThreshold.actualValue})"
|
||||
: '${TranslationBase.of(context).getTranslation(matchingThreshold.label)}';
|
||||
if (matchingThreshold.label.isNotEmpty) {
|
||||
return Text(
|
||||
actualValue,
|
||||
style: const TextStyle(fontSize: 10),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
axisNameSize: 60,
|
||||
sideTitles: SideTitles(
|
||||
showTitles: !isFullScreenGraph,
|
||||
reservedSize: 50,
|
||||
getTitlesWidget: (value, _) {
|
||||
if (value.toInt() >= 0 && value.toInt() < dataPoints.length) {
|
||||
var label = dataPoints[value.toInt()].label;
|
||||
if (dataPoints[value.toInt()].isStringResource) {
|
||||
label = TranslationBase.of(context).getTranslation(dataPoints[value.toInt()].label);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsetsDirectional.only(
|
||||
top: 8.0,
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
interval: 1, // ensures 1:1 mapping with spots
|
||||
),
|
||||
),
|
||||
topTitles: AxisTitles(),
|
||||
rightTitles: AxisTitles(),
|
||||
),
|
||||
borderData: FlBorderData(show: false),
|
||||
lineBarsData: _buildColoredLineSegments(dataPoints, thresholds),
|
||||
gridData: FlGridData(show: false),
|
||||
extraLinesData: ExtraLinesData(
|
||||
horizontalLines: thresholds.map((t) {
|
||||
return HorizontalLine(
|
||||
y: t.value,
|
||||
color: t.lineColor,
|
||||
strokeWidth: 2,
|
||||
// label: HorizontalLineLabel(
|
||||
// show: true,
|
||||
// alignment: Alignment.centerLeft,
|
||||
// style: const TextStyle(fontSize: 10),
|
||||
// labelResolver: (_) => '${t.label} (${t.value.toStringAsFixed(2)})',
|
||||
// ),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
rangeAnnotations: RangeAnnotations(
|
||||
horizontalRangeAnnotations: _buildRangeShades(thresholds),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
List<LineChartBarData> _buildColoredLineSegments(List<DataPoint> dataPoints, List<ThresholdRange> thresholds) {
|
||||
List<LineChartBarData> segments = [];
|
||||
Color getColor(double value) {
|
||||
for (int i = thresholds.length - 1; i >= 0; i--) {
|
||||
if (value >= thresholds[i].value) {
|
||||
return thresholds[i].lineColor;
|
||||
}
|
||||
}
|
||||
return Colors.grey;
|
||||
}
|
||||
|
||||
for (int i = 0; i < dataPoints.length - 1; i++) {
|
||||
final dp1 = dataPoints[i];
|
||||
final dp2 = dataPoints[i + 1];
|
||||
|
||||
final spot1 = FlSpot(i.toDouble(), dp1.value);
|
||||
final spot2 = FlSpot((i + 1).toDouble(), dp2.value);
|
||||
|
||||
final color1 = getColor(dp1.value);
|
||||
final color2 = getColor(dp2.value);
|
||||
|
||||
segments.add(LineChartBarData(
|
||||
spots: [spot1, spot2],
|
||||
isCurved: false,
|
||||
barWidth: 2,
|
||||
gradient: LinearGradient(
|
||||
colors: [color1, color2],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
),
|
||||
dotData: FlDotData(show: false), // We'll handle dots separately
|
||||
));
|
||||
}
|
||||
|
||||
// Add dot markers separately so they are on top
|
||||
|
||||
final List<FlSpot> allSpots = dataPoints.asMap().entries.map((entry) {
|
||||
return FlSpot(entry.key.toDouble(), entry.value.value);
|
||||
}).toList();
|
||||
print("the spots are $allSpots");
|
||||
|
||||
var data = [
|
||||
...segments,
|
||||
LineChartBarData(
|
||||
spots: allSpots,
|
||||
isCurved: false,
|
||||
barWidth: 0, // invisible line
|
||||
dotData: FlDotData(
|
||||
show: true,
|
||||
getDotPainter: (spot, _, __, index) {
|
||||
final value = spot.y;
|
||||
final color = getColor(value);
|
||||
return FlDotCirclePainter(
|
||||
radius: 4,
|
||||
color: color,
|
||||
strokeWidth: 1,
|
||||
strokeColor: Colors.white,
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
];
|
||||
|
||||
// var max = LineChartHelper.calculateMaxAxisValues(data).maxX;
|
||||
// print("the maxX is the -------> $max");
|
||||
return data;
|
||||
}
|
||||
|
||||
List<HorizontalRangeAnnotation> _buildRangeShades(List<ThresholdRange> thresholds) {
|
||||
List<HorizontalRangeAnnotation> ranges = [];
|
||||
for (int i = 0; i < thresholds.length - 1; i++) {
|
||||
ranges.add(HorizontalRangeAnnotation(
|
||||
y1: thresholds[i].value,
|
||||
y2: thresholds[i + 1].value,
|
||||
color: thresholds[i].color,
|
||||
));
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
}
|
||||
|
||||
final List<DataPoint> sampleData = [
|
||||
DataPoint(value: 1.4, label: 'Jan 2024', date: DateTime(2024, 1, 1)),
|
||||
DataPoint(value: 3.6, label: 'Feb 2024', date: DateTime(2024, 2, 1)),
|
||||
DataPoint(value: 1.96, label: 'This result', date: DateTime(2024, 3, 1)),
|
||||
];
|
||||
final List<ThresholdRange> thresholdLevels = [
|
||||
ThresholdRange(label: 'Critical Low', value: 1.4, color: Color(0xfff6e9e9), lineColor: Color(0xFFe9a2a4)),
|
||||
ThresholdRange(label: 'Low', value: 3.6, color: Color(0xFFf2fbf5), lineColor: Color(0xFFeecd94)),
|
||||
ThresholdRange(label: 'Normal', value: 5.96, color: Color(0xFFf2fbf5), lineColor: Color(0xFF5dc36b)),
|
||||
ThresholdRange(label: 'High', value: 7.15, color: Color(0xfff6e9e9), lineColor: Color(0xFFeecd94)),
|
||||
ThresholdRange(label: 'Critical High', value: 10.15, color: Color(0xfff6e9e9), lineColor: Color(0xFFe9a2a4)),
|
||||
];
|
||||
|
||||
class DataPoint {
|
||||
final double value;
|
||||
double labelValue;
|
||||
String label;
|
||||
String actualValue;
|
||||
final DateTime date;
|
||||
bool isStringResource;
|
||||
|
||||
DataPoint({required this.value, required this.label, required this.date, this.isStringResource = false, this.labelValue = 0.0, this.actualValue = ""});
|
||||
}
|
||||
|
||||
class ThresholdRange {
|
||||
final String label;
|
||||
final double value;
|
||||
final Color color;
|
||||
final Color lineColor;
|
||||
final String? actualValue;
|
||||
|
||||
ThresholdRange({required this.label, required this.value, required this.color, required this.lineColor, this.actualValue});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ThresholdRange(label: $label, value: $value, color: ${color.value.toRadixString(16)}, lineColor: ${lineColor.value.toRadixString(16)})';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,156 @@
|
||||
import 'package:hmg_patient_app/core/model/labs/lab_result.dart';
|
||||
import 'package:hmg_patient_app/core/model/labs/patient_lab_orders.dart';
|
||||
import 'package:hmg_patient_app/theme/colors.dart';
|
||||
import 'package:hmg_patient_app/uitl/translations_delegate_base.dart';
|
||||
import 'package:hmg_patient_app/widgets/data_display/medical/LabResult/newUI/labWidgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:readmore/readmore.dart';
|
||||
|
||||
import '../FlowChartPage.dart';
|
||||
|
||||
class LabItem extends StatefulWidget {
|
||||
final LabResultList item;
|
||||
PatientLabOrders? patientLabOrders;
|
||||
|
||||
LabItem({super.key, required this.item, this.patientLabOrders});
|
||||
|
||||
@override
|
||||
State<LabItem> createState() => _LabItemState();
|
||||
}
|
||||
|
||||
class _LabItemState extends State<LabItem> {
|
||||
bool _isShowMoreGeneral = false;
|
||||
bool showBottomSheet = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (showBottomSheet) {
|
||||
print('the bottom sheet is showing');
|
||||
openFlowChart(context, '');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isShowMoreGeneral = !_isShowMoreGeneral;
|
||||
});
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
widget.item.filterName ?? '',
|
||||
style: TextStyle(fontSize: 21, fontWeight: FontWeight.bold, color: Color(0xff2B353E), letterSpacing: -0.64, height: 25 / 16),
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
_isShowMoreGeneral ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
|
||||
color: Color(0xff2B353E),
|
||||
size: 26,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
ReadMoreText(
|
||||
widget.item.description ?? '',
|
||||
trimMode: TrimMode.Line,
|
||||
trimLines: 3,
|
||||
style: const TextStyle(color: Color(0xff575757), fontSize: 11),
|
||||
colorClickableText: CustomColors.accentColor,
|
||||
trimCollapsedText: '${TranslationBase.of(context).readMore}',
|
||||
trimExpandedText: ' show less',
|
||||
),
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (_isShowMoreGeneral)
|
||||
...List.generate(widget.item.patientLabResultList?.length ?? 0, (index) {
|
||||
var data = widget.item.patientLabResultList?[index];
|
||||
return Column(
|
||||
children: [
|
||||
ItemResultCardWidgetWithParams(
|
||||
title: data?.description ?? '',
|
||||
subTitle: data?.resultValue ?? '',
|
||||
referenceRange: data?.referanceRange ?? '',
|
||||
percentage: data?.percentage ?? 0.0,
|
||||
note: data?.testShortDescription ?? '',
|
||||
source: "",
|
||||
buttonText: "",
|
||||
type: data?.calculatedResultFlag?.getType() ?? ResultTypes.unknown,
|
||||
shouldShowResultBarAndGraph: data?.shouldShowResultBarAndGraph() ?? true,
|
||||
onButtonPressed: () {
|
||||
openFlowChart(context, data?.description ?? '');
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
height: 13,
|
||||
),
|
||||
],
|
||||
);
|
||||
})
|
||||
// Expanded(
|
||||
// child: ListView.builder(
|
||||
// itemCount: widget.item.patientLabResultList?.length ?? 0,
|
||||
// itemBuilder: (context, index) {
|
||||
// var data = widget.item.patientLabResultList?[index];
|
||||
// Column(
|
||||
// children: [
|
||||
// EllipsisTextWithMore(
|
||||
// text: data?.packageShortDescription ?? '',
|
||||
// ),
|
||||
// SizedBox(
|
||||
// height: 8,
|
||||
// ),
|
||||
// ItemResultCardWidgetWithParams(
|
||||
// title: data?.description ?? '',
|
||||
// subTitle: data?.resultValue ?? '',
|
||||
// referenceRange: data?.referanceRange ?? '',
|
||||
// percentage: 20,
|
||||
// note: data?.testShortDescription ?? '',
|
||||
// source: "",
|
||||
// buttonText: "",
|
||||
// type: data?.calculatedResultFlag?.getType() ??
|
||||
// ResultTypes.unknown),
|
||||
// ],
|
||||
// );
|
||||
// }))
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
openFlowChart(BuildContext context, String procedure) {
|
||||
print('openFlowChart: the bottom sheet is showing');
|
||||
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Colors.white,
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
scrollControlDisabledMaxHeightRatio: .75,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20))),
|
||||
builder: (context) {
|
||||
print('builder: the bottom sheet is showing');
|
||||
|
||||
return FlowChartPage(
|
||||
filterName: procedure,
|
||||
patientLabOrder: widget.patientLabOrders,
|
||||
);
|
||||
}).then((value) {
|
||||
setState(() {
|
||||
showBottomSheet = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,523 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:hmg_patient_app/uitl/translations_delegate_base.dart';
|
||||
import 'package:hmg_patient_app/uitl/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
///@author
|
||||
///
|
||||
enum ResultTypes { lowCriticalLow, criticalLow, low, normal, high, criticalHigh, highCriticalHigh, IRR, unknown }
|
||||
|
||||
class ItemResultCardWidget extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const ItemResultCardWidget({
|
||||
Key? key,
|
||||
required this.child,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12, bottom: 12),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomResultProgressBar extends StatelessWidget {
|
||||
final num percentage;
|
||||
final String value;
|
||||
final ResultTypes type;
|
||||
|
||||
CustomResultProgressBar({Key? key, required this.percentage, required this.value, required this.type}) : super(key: key);
|
||||
|
||||
final GlobalKey lcl = GlobalKey();
|
||||
|
||||
final GlobalKey cl = GlobalKey();
|
||||
|
||||
final GlobalKey l = GlobalKey();
|
||||
|
||||
final GlobalKey n = GlobalKey();
|
||||
|
||||
final GlobalKey h = GlobalKey();
|
||||
|
||||
final GlobalKey ch = GlobalKey();
|
||||
|
||||
final GlobalKey hch = GlobalKey();
|
||||
|
||||
OverlayEntry? overlayEntry;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
final double totalWidth = constraints.maxWidth;
|
||||
final double spacing = 2;
|
||||
final double tooltipPosition = (totalWidth * (percentage / 100)).clamp(0, totalWidth - 50);
|
||||
return Stack(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
_buildResultBar(
|
||||
key: cl,
|
||||
flex: 3,
|
||||
color: Color(0xFFDE7676),
|
||||
title: TranslationBase.of(context).criticalLow,
|
||||
),
|
||||
SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
_buildResultBar(
|
||||
key: l,
|
||||
flex: 2,
|
||||
color: Color(0xFFEAB157),
|
||||
title: TranslationBase.of(context).low,
|
||||
),
|
||||
SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
_buildResultBar(
|
||||
key: n,
|
||||
flex: 5,
|
||||
color: Color(0xFF09AA28),
|
||||
title: TranslationBase.of(context).normal,
|
||||
),
|
||||
SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
_buildResultBar(
|
||||
key: h,
|
||||
flex: 2,
|
||||
color: Color(0xFFEAB157),
|
||||
title: TranslationBase.of(context).high,
|
||||
),
|
||||
SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
_buildResultBar(
|
||||
key: ch,
|
||||
flex: 3,
|
||||
color: Color(0xFFDE7676),
|
||||
title: TranslationBase.of(context).criticalHigh,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!percentage.isNegative)
|
||||
Positioned(
|
||||
left: tooltipPosition,
|
||||
top: 33,
|
||||
child: Column(
|
||||
children: [
|
||||
TextCloud(
|
||||
text: "${value}",
|
||||
color: getColorForResultType(type),
|
||||
width: 100,
|
||||
height: 20,
|
||||
padding: EdgeInsets.zero,
|
||||
axisDirection: AxisDirection.up,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Color getColorForResultType(ResultTypes type) {
|
||||
switch (type) {
|
||||
case ResultTypes.lowCriticalLow:
|
||||
case ResultTypes.highCriticalHigh:
|
||||
case ResultTypes.criticalLow:
|
||||
case ResultTypes.criticalHigh:
|
||||
return Color(0xFFDE7676);
|
||||
case ResultTypes.low:
|
||||
case ResultTypes.high:
|
||||
return Color(0xFFEAB157);
|
||||
case ResultTypes.normal:
|
||||
return Color(0xFF09AA28);
|
||||
default:
|
||||
return Color(0xFF09AA28);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildResultBar({required int flex, required Color color, required String title, Key? key}) {
|
||||
return Expanded(
|
||||
key: key,
|
||||
flex: flex,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 8,
|
||||
fontFamily: 'Poppins',
|
||||
letterSpacing: -0.3,
|
||||
color: color,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Container(
|
||||
height: 5,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TextCloud extends StatelessWidget {
|
||||
final String text; // write here box content
|
||||
final Color color; // Set box Color
|
||||
final EdgeInsets padding; // Set content padding
|
||||
final double width; // Box width
|
||||
final double height; // Box Height
|
||||
final AxisDirection axisDirection; // Set triangle location up,left,right,down
|
||||
final double locationOfArrow; // set between 0 and 1, If 0.5 is set triangle position will be centered
|
||||
const TextCloud({
|
||||
super.key,
|
||||
required this.text,
|
||||
this.color = Colors.white,
|
||||
this.padding = const EdgeInsets.all(10),
|
||||
this.width = 200,
|
||||
this.height = 100,
|
||||
this.axisDirection = AxisDirection.down,
|
||||
this.locationOfArrow = 0.5,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Size arrowSize = const Size(12, 12);
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Container(
|
||||
width: width,
|
||||
height: height,
|
||||
padding: padding,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(color: Colors.white, fontSize: 9, letterSpacing: -.3),
|
||||
)),
|
||||
),
|
||||
Builder(builder: (context) {
|
||||
double angle = 0;
|
||||
switch (axisDirection) {
|
||||
case AxisDirection.left:
|
||||
angle = pi * -0.5;
|
||||
break;
|
||||
case AxisDirection.up:
|
||||
angle = pi * -2;
|
||||
break;
|
||||
case AxisDirection.right:
|
||||
angle = pi * 0.5;
|
||||
break;
|
||||
case AxisDirection.down:
|
||||
angle = pi;
|
||||
break;
|
||||
default:
|
||||
angle = 0;
|
||||
}
|
||||
return Positioned(
|
||||
left: axisDirection == AxisDirection.left
|
||||
? -arrowSize.width + 5
|
||||
: (axisDirection == AxisDirection.up || axisDirection == AxisDirection.down ? width * locationOfArrow - arrowSize.width / 2 : null),
|
||||
right: axisDirection == AxisDirection.right ? -arrowSize.width + 5 : null,
|
||||
top: axisDirection == AxisDirection.up
|
||||
? -arrowSize.width + 5
|
||||
: (axisDirection == AxisDirection.right || axisDirection == AxisDirection.left ? height * locationOfArrow - arrowSize.width / 2 : null),
|
||||
bottom: axisDirection == AxisDirection.down ? -arrowSize.width + 5 : null,
|
||||
child: Transform.rotate(
|
||||
angle: angle,
|
||||
child: CustomPaint(
|
||||
size: arrowSize,
|
||||
painter: ArrowPaint(color: color),
|
||||
),
|
||||
),
|
||||
);
|
||||
})
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ArrowPaint extends CustomPainter {
|
||||
final Color color;
|
||||
|
||||
ArrowPaint({required this.color});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
Path path_0 = Path();
|
||||
path_0.moveTo(size.width * 0.5745375, size.height * 0.06573708);
|
||||
path_0.lineTo(size.width * 0.9813667, size.height * 0.8794000);
|
||||
path_0.cubicTo(size.width * 1.001950, size.height * 0.9205625, size.width * 0.9852625, size.height * 0.9706208, size.width * 0.9441000, size.height * 0.9912000);
|
||||
path_0.cubicTo(size.width * 0.9325250, size.height * 0.9969875, size.width * 0.9197667, size.height, size.width * 0.9068292, size.height);
|
||||
path_0.lineTo(size.width * 0.09316958, size.height);
|
||||
path_0.cubicTo(size.width * 0.04714583, size.height, size.width * 0.009836208, size.height * 0.9626917, size.width * 0.009836208, size.height * 0.9166667);
|
||||
path_0.cubicTo(size.width * 0.009836208, size.height * 0.9037292, size.width * 0.01284829, size.height * 0.8909708, size.width * 0.01863392, size.height * 0.8794000);
|
||||
path_0.lineTo(size.width * 0.4254625, size.height * 0.06573708);
|
||||
path_0.cubicTo(size.width * 0.4460458, size.height * 0.02457225, size.width * 0.4961042, size.height * 0.007886875, size.width * 0.5372667, size.height * 0.02846929);
|
||||
path_0.cubicTo(size.width * 0.5533958, size.height * 0.03653296, size.width * 0.5664708, size.height * 0.04961000, size.width * 0.5745375, size.height * 0.06573708);
|
||||
path_0.close();
|
||||
|
||||
Paint paint_0_fill = Paint()..style = PaintingStyle.fill;
|
||||
paint_0_fill.color = color;
|
||||
canvas.drawPath(path_0, paint_0_fill);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class ItemResultCardWidgetWithParams extends StatelessWidget {
|
||||
final String title;
|
||||
final String subTitle;
|
||||
final String note;
|
||||
final String source;
|
||||
final String referenceRange;
|
||||
final num percentage;
|
||||
final String buttonText;
|
||||
final ResultTypes type;
|
||||
final bool isArabic;
|
||||
final Function()? onButtonPressed;
|
||||
final bool shouldShowResultBarAndGraph;
|
||||
|
||||
const ItemResultCardWidgetWithParams({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.subTitle,
|
||||
required this.referenceRange,
|
||||
required this.percentage,
|
||||
required this.note,
|
||||
required this.source,
|
||||
required this.buttonText,
|
||||
required this.type,
|
||||
this.onButtonPressed,
|
||||
this.isArabic = false,
|
||||
this.shouldShowResultBarAndGraph = true,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final fontFamily = isArabic ? 'Cairo' : 'Poppins';
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 21),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(10)),
|
||||
border: BorderDirectional(
|
||||
start: BorderSide(
|
||||
color: getColorForResultType(type),
|
||||
width: MediaQuery.of(context).size.width * 0.015,
|
||||
),
|
||||
),
|
||||
color: Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.2),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: const Offset(0, 3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
// Title Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(fontSize: 16, fontFamily: fontFamily, fontWeight: FontWeight.w600, color: const Color(0xff2E303A), letterSpacing: -.64),
|
||||
),
|
||||
),
|
||||
// GestureDetector(
|
||||
// onTap: onButtonPressed,
|
||||
// child: Utils.tableColumnValueWithUnderLine(TranslationBase.of(context).viewFlowChart, isLast: true, isCapitable: false),
|
||||
// ),
|
||||
Visibility(
|
||||
visible: shouldShowResultBarAndGraph,
|
||||
child: GestureDetector(
|
||||
onTap: onButtonPressed,
|
||||
child: Utils.tableColumnValueWithUnderLine(TranslationBase.of(context).viewFlowChart, isLast: true, isCapitable: false),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ResultStatusWidget(type: type),
|
||||
SizedBox(width: 5),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
subTitle,
|
||||
style: TextStyle(fontSize: 14, fontFamily: fontFamily, fontWeight: FontWeight.w600, color: const Color(0xff2E303A), letterSpacing: -0.56),
|
||||
),
|
||||
Text(
|
||||
"${TranslationBase.of(context).referenceRange} \n $referenceRange",
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontFamily: fontFamily,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: const Color(0xff2E303A),
|
||||
letterSpacing: -0.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// Progress Bar
|
||||
Visibility(
|
||||
visible: shouldShowResultBarAndGraph,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 55,
|
||||
child: CustomResultProgressBar(
|
||||
percentage: percentage,
|
||||
value: subTitle,
|
||||
type: type,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Note Section
|
||||
if (note.isNotEmpty)
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Divider(color: Color(0xFFEFEFEF), thickness: 1),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"${TranslationBase.of(context).notes}:",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: const Color(0xFF2E303A),
|
||||
letterSpacing: -0.4,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
note,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: const Color(0xFF575757),
|
||||
letterSpacing: -0.4,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Color getColorForResultType(ResultTypes type) {
|
||||
switch (type) {
|
||||
case ResultTypes.criticalLow:
|
||||
case ResultTypes.criticalHigh:
|
||||
case ResultTypes.lowCriticalLow:
|
||||
case ResultTypes.highCriticalHigh:
|
||||
return Color(0xFFDE7676);
|
||||
case ResultTypes.low:
|
||||
case ResultTypes.high:
|
||||
return Color(0xFFEAB157);
|
||||
case ResultTypes.normal:
|
||||
return Color(0xFF09AA28);
|
||||
//todo handle irr here
|
||||
default:
|
||||
return Color(0xFF09AA28);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ResultStatusWidget extends StatelessWidget {
|
||||
final ResultTypes type;
|
||||
|
||||
const ResultStatusWidget({
|
||||
Key? key,
|
||||
required this.type,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color? color;
|
||||
IconData? icon;
|
||||
|
||||
switch (type) {
|
||||
case ResultTypes.criticalLow:
|
||||
case ResultTypes.lowCriticalLow:
|
||||
color = const Color(0xFFDE7676);
|
||||
icon = Icons.arrow_circle_down;
|
||||
break;
|
||||
case ResultTypes.low:
|
||||
color = const Color(0xFFEAB157);
|
||||
icon = Icons.arrow_circle_down;
|
||||
break;
|
||||
case ResultTypes.criticalHigh:
|
||||
case ResultTypes.highCriticalHigh:
|
||||
color = const Color(0xFFDE7676);
|
||||
icon = Icons.arrow_circle_up;
|
||||
break;
|
||||
case ResultTypes.high:
|
||||
color = const Color(0xFFEAB157);
|
||||
icon = Icons.arrow_circle_up;
|
||||
break;
|
||||
case ResultTypes.normal:
|
||||
color = const Color(0xFF09AA28);
|
||||
icon = Icons.check_circle;
|
||||
break;
|
||||
case ResultTypes.unknown:
|
||||
case ResultTypes.IRR:
|
||||
color = Color(0xFF09AA28);
|
||||
icon = Icons.check_circle;
|
||||
break;
|
||||
}
|
||||
|
||||
// Return the icon if defined, otherwise a placeholder widget
|
||||
return icon != null
|
||||
? Icon(
|
||||
icon,
|
||||
color: color,
|
||||
)
|
||||
: const SizedBox(); // Use SizedBox to keep the layout consistent
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.cxx
|
||||
@ -0,0 +1,69 @@
|
||||
group 'ai.panoptic.vital_sign_camera'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.5.20'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 34
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 24
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||
implementation 'androidx.camera:camera-core:1.0.2'
|
||||
implementation 'com.google.mlkit:face-detection:16.1.2'
|
||||
implementation 'com.android.volley:volley:1.2.1'
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation project(":vital-sign-engine")
|
||||
|
||||
implementation "androidx.camera:camera-core:1.1.0-beta02"
|
||||
implementation "androidx.camera:camera-camera2:1.1.0-beta02"
|
||||
implementation "androidx.camera:camera-lifecycle:1.1.0-beta02"
|
||||
implementation "androidx.camera:camera-video:1.1.0-beta02"
|
||||
|
||||
implementation "androidx.camera:camera-view:1.1.0-beta02"
|
||||
implementation "androidx.camera:camera-extensions:1.1.0-beta02"
|
||||
|
||||
implementation "androidx.exifinterface:exifinterface:1.3.3"
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.5.2"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@ -0,0 +1,234 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@ -0,0 +1,2 @@
|
||||
configurations.maybeCreate("default")
|
||||
artifacts.add("default", file('vital-sign-engine-debug.aar'))
|
||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="ai.panoptic.vital_sign_camera">
|
||||
</manifest>
|
||||
@ -0,0 +1,108 @@
|
||||
package ai.panoptic.vital_sign_camera
|
||||
|
||||
import ai.panoptic.wvs.camera.*
|
||||
import ai.panoptic.wvs.engine.Converter
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.View
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
class FlutterVitalSignCamera internal constructor(
|
||||
context: Context,
|
||||
messenger: BinaryMessenger,
|
||||
id: Int,
|
||||
props: Map<String, Any>,
|
||||
lifecycle: Lifecycle
|
||||
) : PlatformView, MethodChannel.MethodCallHandler, EventChannel.StreamHandler, VitalSignCameraEventHandler {
|
||||
|
||||
private val camera: VitalSignCamera
|
||||
private val methodChannel: MethodChannel
|
||||
private var eventSink: EventChannel.EventSink? = null
|
||||
private val mainThread = Handler(Looper.getMainLooper())
|
||||
|
||||
override fun getView(): View {
|
||||
return camera
|
||||
}
|
||||
|
||||
init {
|
||||
camera = VitalSignCamera(context, MLKitFaceDetector(), lifecycle, this)
|
||||
|
||||
methodChannel = MethodChannel(messenger, "ai.panoptic/channel/flutter_vital_sign_camera_$id")
|
||||
methodChannel.setMethodCallHandler(this)
|
||||
|
||||
val eventChannel = EventChannel(messenger, "ai.panoptic/channel/flutter_vital_sign_camera_processed_frame_event_$id")
|
||||
eventChannel.setStreamHandler(this)
|
||||
|
||||
camera.setProps(props)
|
||||
}
|
||||
|
||||
override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {
|
||||
when (methodCall.method) {
|
||||
"startScanning" -> {
|
||||
camera.startStartScanning()
|
||||
.thenAccept { result.success(null) }
|
||||
.exceptionally {
|
||||
result.error("-1", it.message, it.cause)
|
||||
return@exceptionally null
|
||||
}
|
||||
}
|
||||
"stopScanning" -> {
|
||||
camera.stopScanning()
|
||||
.thenAccept { result.success(null) }
|
||||
.exceptionally {
|
||||
result.error("-2", it.message, it.cause)
|
||||
return@exceptionally null
|
||||
}
|
||||
}
|
||||
"configure" -> {
|
||||
val arguments = methodCall.arguments as? Map<String, Any>
|
||||
arguments?.let {
|
||||
camera.setProps(it)
|
||||
}
|
||||
|
||||
}
|
||||
else -> {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
// camera.destroy()
|
||||
}
|
||||
|
||||
override fun onVideoFrameProcessed(result: ai.panoptic.wvs.engine.ProcessFrameResult): kotlin.Unit {
|
||||
// Already handled in onEvent.
|
||||
// No need to handle here.
|
||||
}
|
||||
|
||||
override fun onEvent(cameraView: VitalSignCamera, eventName: String, event: Map<String,Any>?) {
|
||||
when(eventName) {
|
||||
"videoFrameProcessed" -> {
|
||||
eventSink?.let {
|
||||
mainThread.post {
|
||||
it.success(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
|
||||
eventSink = events
|
||||
}
|
||||
|
||||
override fun onCancel(arguments: Any?) {
|
||||
eventSink = null
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,165 @@
|
||||
package ai.panoptic.vital_sign_camera;
|
||||
|
||||
import static com.google.mlkit.vision.face.FaceContour.FACE;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.media.Image;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.mlkit.vision.common.InputImage;
|
||||
import com.google.mlkit.vision.face.Face;
|
||||
import com.google.mlkit.vision.face.FaceContour;
|
||||
import com.google.mlkit.vision.face.FaceDetection;
|
||||
import com.google.mlkit.vision.face.FaceDetector;
|
||||
import com.google.mlkit.vision.face.FaceDetectorOptions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import ai.panoptic.wvs.engine.EyesOpenProbability;
|
||||
import ai.panoptic.wvs.engine.FaceDetectorFeature;
|
||||
import ai.panoptic.wvs.engine.HeadEulerAngle;
|
||||
import ai.panoptic.wvs.engine.NormalizedFacebox;
|
||||
import ai.panoptic.wvs.engine.VitalSignFaceDetector;
|
||||
|
||||
public class MLKitFaceDetector implements VitalSignFaceDetector {
|
||||
|
||||
ai.panoptic.wvs.engine.Face face = null;
|
||||
|
||||
FaceDetectorOptions options =
|
||||
new FaceDetectorOptions.Builder()
|
||||
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
|
||||
.setContourMode(FaceDetectorOptions.CONTOUR_MODE_NONE)
|
||||
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
|
||||
.setMinFaceSize(0.15f)
|
||||
.build();
|
||||
|
||||
FaceDetector faceDetector = FaceDetection.getClient(options);
|
||||
|
||||
public ai.panoptic.wvs.engine.Face detectFace(Image mediaImage, int rotationDegrees) {
|
||||
|
||||
if (mediaImage == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
float width = mediaImage.getWidth();
|
||||
float height = mediaImage.getHeight();
|
||||
if (rotationDegrees == 270 || rotationDegrees == 90) {
|
||||
width = mediaImage.getHeight();
|
||||
height = mediaImage.getWidth();
|
||||
}
|
||||
final float _width = width;
|
||||
final float _height = height;
|
||||
InputImage image = InputImage.fromMediaImage(mediaImage, rotationDegrees);
|
||||
|
||||
Task<List<Face>> task = faceDetector.process(image);
|
||||
|
||||
task.addOnCompleteListener( result -> {
|
||||
|
||||
if (result.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
if (!result.isSuccessful()) {
|
||||
return;
|
||||
}
|
||||
if (!result.isComplete()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
List<Face> faces = result.getResult();
|
||||
if (faces.isEmpty()) {
|
||||
this.face = null;
|
||||
return;
|
||||
}
|
||||
Face face = faces.get(0);
|
||||
this.face = createFace(face, _width, _height);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.face = null;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return face;
|
||||
}
|
||||
|
||||
static private ai.panoptic.wvs.engine.Face createFace(Face face, float _width, float _height) {
|
||||
|
||||
FaceContour contour = face.getContour(FACE);
|
||||
|
||||
NormalizedFacebox facebox = new NormalizedFacebox();
|
||||
float minX = _width;
|
||||
float minY = _height;
|
||||
float maxX = 0;
|
||||
float maxY = 0;
|
||||
|
||||
if (contour != null) {
|
||||
|
||||
for (PointF p : contour.getPoints()) {
|
||||
if (p.x < minX) {
|
||||
minX = p.x;
|
||||
}
|
||||
if (p.y < minY) {
|
||||
minY = p.y;
|
||||
}
|
||||
if (p.x > maxX) {
|
||||
maxX = p.x;
|
||||
}
|
||||
if (p.y > maxY) {
|
||||
maxY = p.y;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
minX = face.getBoundingBox().left;
|
||||
minY = face.getBoundingBox().top;
|
||||
maxX = face.getBoundingBox().right;
|
||||
maxY = face.getBoundingBox().bottom;
|
||||
}
|
||||
|
||||
facebox.xCenter = (minX + (maxX - minX) / 2.0f) / _width;
|
||||
facebox.yCenter = (minY + (maxY - minY) / 2.0f) / _height;
|
||||
facebox.width = (maxX - minX) / _width;
|
||||
facebox.height = (maxY - minY) / _height;
|
||||
|
||||
ai.panoptic.wvs.engine.Face result = new ai.panoptic.wvs.engine.Face(facebox);
|
||||
|
||||
result.setEulerAngle(
|
||||
new HeadEulerAngle(
|
||||
face.getHeadEulerAngleX(),
|
||||
face.getHeadEulerAngleY(),
|
||||
face.getHeadEulerAngleY()
|
||||
)
|
||||
);
|
||||
|
||||
if (face.getLeftEyeOpenProbability() != null && face.getRightEyeOpenProbability() != null) {
|
||||
result.setEyesOpenProbability(
|
||||
new EyesOpenProbability(
|
||||
face.getLeftEyeOpenProbability(),
|
||||
face.getRightEyeOpenProbability()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<FaceDetectorFeature> supportedFeatures() {
|
||||
ArraySet<FaceDetectorFeature> features = new ArraySet<FaceDetectorFeature>();
|
||||
features.add(FaceDetectorFeature.eyeOpenProbability);
|
||||
features.add(FaceDetectorFeature.eulerAngle);
|
||||
return features;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NormalizedFacebox detect(Image mediaImage, int rotationDegrees) {
|
||||
detectFace(mediaImage, rotationDegrees);
|
||||
if (face == null) {
|
||||
return null;
|
||||
}
|
||||
return face.getFacebox();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,145 @@
|
||||
package ai.panoptic.vital_sign_camera
|
||||
|
||||
import ai.panoptic.wvs.camera.availableCameraDevices
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.NonNull
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.*
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||
import io.flutter.embedding.engine.plugins.lifecycle.FlutterLifecycleAdapter
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
import io.flutter.plugin.common.MethodChannel.Result
|
||||
import io.flutter.plugin.common.PluginRegistry
|
||||
import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/** VitalSignCameraPlugin */
|
||||
class VitalSignCameraPlugin: FlutterPlugin, MethodCallHandler, ActivityAware,
|
||||
RequestPermissionsResultListener {
|
||||
|
||||
companion object {
|
||||
const val TAG = "VitalSignCameraPlugin"
|
||||
|
||||
fun parsePermissionStatus(status: Int): String {
|
||||
return when (status) {
|
||||
PackageManager.PERMISSION_DENIED -> "denied"
|
||||
PackageManager.PERMISSION_GRANTED -> "authorized"
|
||||
else -> "not-determined"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// The MethodChannel that will the communication between Flutter and native Android
|
||||
///
|
||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||
/// when the Flutter Engine is detached from the Activity
|
||||
private lateinit var channel : MethodChannel
|
||||
private lateinit var pluginBinding: FlutterPlugin.FlutterPluginBinding
|
||||
private lateinit var context: Context
|
||||
private lateinit var activity: Activity
|
||||
private lateinit var activityBinding:ActivityPluginBinding
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.Main)
|
||||
private var requestCode = 1
|
||||
private var permissionRequests = HashMap<Int, Result>()
|
||||
|
||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
|
||||
Log.d("VitalSignCameraPlugin", "onAttachedToEngine")
|
||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "vital_sign_camera_plugin")
|
||||
channel.setMethodCallHandler(this)
|
||||
pluginBinding = flutterPluginBinding
|
||||
context = flutterPluginBinding.applicationContext
|
||||
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel.setMethodCallHandler(null)
|
||||
}
|
||||
|
||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
Log.d(TAG, "onAttachedToActivity")
|
||||
|
||||
val lifecycle: Lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(binding)
|
||||
|
||||
pluginBinding.platformViewRegistry.registerViewFactory(
|
||||
"ai.panoptic/flutter_vital_sign_camera",
|
||||
VitalSignCameraFactory(pluginBinding.binaryMessenger, lifecycle)
|
||||
)
|
||||
|
||||
activity = binding.activity
|
||||
activityBinding = binding
|
||||
activityBinding.addRequestPermissionsResultListener(this)
|
||||
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {}
|
||||
|
||||
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {}
|
||||
|
||||
override fun onDetachedFromActivity() {
|
||||
activityBinding.removeRequestPermissionsResultListener(this)
|
||||
}
|
||||
|
||||
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
|
||||
|
||||
when (call.method) {
|
||||
"availableCameraDevices" -> {
|
||||
coroutineScope.launch {
|
||||
val devices = availableCameraDevices(context)
|
||||
val returnValue = devices.map { it.map }
|
||||
Log.d(TAG, "$returnValue")
|
||||
result.success(returnValue)
|
||||
}
|
||||
}
|
||||
"getCameraPermissionStatus" -> {
|
||||
Log.d(TAG, "getCameraPermissionStatus")
|
||||
val status =
|
||||
parsePermissionStatus(
|
||||
ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA))
|
||||
result.success(status)
|
||||
}
|
||||
"requestCameraPermission" -> {
|
||||
Log.d(TAG, "requestCameraPermission")
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
// API 21 and below always grants permission on app install
|
||||
result.success("authorized")
|
||||
}
|
||||
activity.requestPermissions(arrayOf(Manifest.permission.CAMERA), requestCode)
|
||||
permissionRequests[requestCode] = result
|
||||
requestCode += 1
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
): Boolean {
|
||||
|
||||
permissionRequests[requestCode]?.let {
|
||||
val permissionStatus = if (grantResults.isNotEmpty()) grantResults[0] else PackageManager.PERMISSION_DENIED
|
||||
it.success(parsePermissionStatus(permissionStatus))
|
||||
permissionRequests.remove(requestCode)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,218 @@
|
||||
//
|
||||
// MLKitFaceDetector.swift
|
||||
// VitalSignEngineCore
|
||||
//
|
||||
// Created by Eddie Lau on 24/8/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MLKitFaceDetection
|
||||
import MLKitVision
|
||||
import VitalSignEngineCore
|
||||
|
||||
public class MLKitFaceDetector : VitalSignFaceDetector {
|
||||
public typealias Face = MLKitFace
|
||||
|
||||
public let supportedFeatures: Set<VitalSignEngineCore.FaceDetectorFeature>
|
||||
= [.eyesOpenProbability, .eularAngle]
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
public let maxDetectionHeight:CGFloat = 240
|
||||
let fps = Fps(name: "MLKitFaceDetector")
|
||||
|
||||
lazy var faceDetector:FaceDetector = {
|
||||
let option = FaceDetectorOptions()
|
||||
option.contourMode = .all
|
||||
option.classificationMode = .all
|
||||
option.landmarkMode = .none
|
||||
option.performanceMode = .fast
|
||||
return FaceDetector.faceDetector(options: option)
|
||||
}()
|
||||
|
||||
var face:Face? {
|
||||
didSet {
|
||||
fps.tick()
|
||||
}
|
||||
}
|
||||
|
||||
public var frameRate: Double? { fps.lastReportedAverage }
|
||||
|
||||
public var maxConcurrency = 2
|
||||
var jobCount = 0
|
||||
|
||||
public func detect(from frame: UIImage) -> Face? {
|
||||
|
||||
guard jobCount < maxConcurrency else {
|
||||
return face
|
||||
}
|
||||
|
||||
jobCount += 1
|
||||
DispatchQueue.global().async {
|
||||
|
||||
let scaledFrame = self.scaled(videoFrame: frame)
|
||||
let frameSize = scaledFrame.size
|
||||
|
||||
let image = VisionImage(image: scaledFrame)
|
||||
image.orientation = image.orientation
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
self.faceDetector.process(image) { faces, error in
|
||||
|
||||
guard let face = faces?.first else {
|
||||
self.face = nil
|
||||
self.jobCount -= 1
|
||||
return
|
||||
}
|
||||
self.face = MLKitFace(face: face, frameSize: frameSize)
|
||||
self.jobCount -= 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return face
|
||||
}
|
||||
|
||||
func scaled(videoFrame frame:UIImage) -> UIImage {
|
||||
var scale = CGFloat(1.0)
|
||||
if frame.size.height > maxDetectionHeight {
|
||||
scale = maxDetectionHeight / frame.size.height
|
||||
}
|
||||
let scaledScale = CGSize(width:frame.size.width * scale, height:frame.size.height * scale)
|
||||
return frame.resizedImage(size: scaledScale)!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct MLKitFaceLandmarks: FaceLandmarks {
|
||||
public typealias ContourType = FaceContour
|
||||
let mlkitFace:MLKitFaceDetection.Face
|
||||
public let videoFrameSize: CGSize
|
||||
init(mlkitFace: MLKitFaceDetection.Face, videoFrameSize: CGSize) {
|
||||
self.mlkitFace = mlkitFace
|
||||
self.videoFrameSize = videoFrameSize
|
||||
}
|
||||
public var faceOval: FaceContour? { mlkitFace.contour(ofType: .face) }
|
||||
public func contour(of type: VitalSignEngineCore.FaceLandmarkType) -> FaceContour? {
|
||||
guard let contourType = type.asContourType else { return nil }
|
||||
return mlkitFace.contour(ofType: contourType)
|
||||
}
|
||||
}
|
||||
|
||||
public class MLKitFace : FaceProtocol {
|
||||
public typealias ContourType = FaceContour
|
||||
|
||||
let mlkitFace:MLKitFaceDetection.Face
|
||||
|
||||
public let faceBox: NormalizedFaceBox
|
||||
public let faceLandmarks: MLKitFaceLandmarks
|
||||
public let eulerAngle: (x: CGFloat, y: CGFloat, z: CGFloat)?
|
||||
public let eyesOpenProbability: (leftEye:CGFloat, rightEye:CGFloat)?
|
||||
|
||||
init?(face:MLKitFaceDetection.Face, frameSize:CGSize) {
|
||||
|
||||
self.mlkitFace = face
|
||||
self.faceLandmarks = MLKitFaceLandmarks(mlkitFace: face, videoFrameSize: frameSize)
|
||||
|
||||
guard let faceContour = face.contour(ofType: FaceContourType.face) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let eyesOpenPropbability: (leftEye:CGFloat, rightEye:CGFloat)?
|
||||
if face.hasLeftEyeOpenProbability && face.hasRightEyeOpenProbability {
|
||||
eyesOpenPropbability = (face.leftEyeOpenProbability, face.rightEyeOpenProbability)
|
||||
} else {
|
||||
eyesOpenPropbability = nil
|
||||
}
|
||||
self.eyesOpenProbability = eyesOpenPropbability
|
||||
|
||||
let eulerAngle: (x:CGFloat, y:CGFloat, z:CGFloat)?
|
||||
if face.hasHeadEulerAngleX && face.hasHeadEulerAngleY && face.hasHeadEulerAngleZ {
|
||||
eulerAngle = (face.headEulerAngleX, face.headEulerAngleY, face.headEulerAngleZ)
|
||||
} else {
|
||||
eulerAngle = nil
|
||||
}
|
||||
self.eulerAngle = eulerAngle
|
||||
|
||||
self.faceBox = faceContour.toFacebox(frameSize: frameSize)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension MLKitFaceDetection.FaceContour {
|
||||
|
||||
func toFacebox(frameSize:CGSize) -> NormalizedFaceBox {
|
||||
var minX = CGFloat.greatestFiniteMagnitude
|
||||
var minY = CGFloat.greatestFiniteMagnitude
|
||||
var maxX = -CGFloat.greatestFiniteMagnitude
|
||||
var maxY = -CGFloat.greatestFiniteMagnitude
|
||||
points.forEach { point in
|
||||
minX = min(minX, point.x)
|
||||
minY = min(minY, point.y)
|
||||
maxX = max(maxX, point.x)
|
||||
maxY = max(maxY, point.y)
|
||||
}
|
||||
|
||||
return NormalizedFaceBox(
|
||||
xCenter: ((maxX - minX)/2 + minX) / frameSize.width,
|
||||
yCenter: ((maxY - minY)/2 + minY) / frameSize.height,
|
||||
width: (maxX - minX) / frameSize.width,
|
||||
height: (maxY - minY) / frameSize.height
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate extension UIImage {
|
||||
func resizedImage(size: CGSize) -> UIImage? {
|
||||
UIGraphicsBeginImageContextWithOptions(size, false, scale)
|
||||
defer { UIGraphicsEndImageContext() }
|
||||
draw(in: CGRect(origin: .zero, size: size))
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
return image?.data.flatMap(UIImage.init) // This line is copied from MLKit, Not sure why is needed
|
||||
}
|
||||
|
||||
private var data: Data? {
|
||||
#if swift(>=4.2)
|
||||
return pngData() ?? jpegData(compressionQuality: Constant.jpegCompressionQuality)
|
||||
#else
|
||||
return pngData() ?? jpegData(compressionQuality: Constant.jpegCompressionQuality)
|
||||
#endif // swift(>=4.2)
|
||||
}
|
||||
|
||||
private enum Constant {
|
||||
static let jpegCompressionQuality: CGFloat = 0.8
|
||||
}
|
||||
}
|
||||
|
||||
extension VisionPoint : FaceContourPoint {
|
||||
}
|
||||
|
||||
extension FaceContour : Contour {
|
||||
}
|
||||
|
||||
extension VitalSignEngineCore.FaceLandmarkType {
|
||||
var asContourType:MLKitFaceDetection.FaceContourType? {
|
||||
switch self {
|
||||
case .faceOval: return .face
|
||||
case .leftEyebrowTop: return .leftEyebrowTop
|
||||
case .leftEyebrowBottom: return .leftEyebrowBottom
|
||||
case .rightEyebrowTop: return .rightEyebrowTop
|
||||
case .rightEyebrowBottom: return .rightEyebrowBottom
|
||||
case .leftEye: return .leftEye
|
||||
case .rightEye: return .rightEye
|
||||
case .upperLipTop: return .upperLipTop
|
||||
case .upperLipBottom: return .upperLipBottom
|
||||
case .lowerLipTop: return .lowerLipTop
|
||||
case .lowerLipBottom: return .lowerLipBottom
|
||||
case .noseBridge: return .noseBridge
|
||||
case .noseBottom: return .noseBottom
|
||||
case .leftCheek: return .leftCheek
|
||||
case .rightCheek: return .rightCheek
|
||||
@unknown default: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
import VitalSignEngineCore
|
||||
|
||||
public class SwiftVitalSignCameraPlugin: NSObject, FlutterPlugin {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
|
||||
let channel = FlutterMethodChannel(name: "vital_sign_camera_plugin", binaryMessenger: registrar.messenger())
|
||||
let instance = SwiftVitalSignCameraPlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
|
||||
registrar.register(VitalSignCameraFactory(messenger: registrar.messenger()), withId: "ai.panoptic/flutter_vital_sign_camera")
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
if call.method == "availableCameraDevices" {
|
||||
let devices = VitalSignCamera.availableCameraDevices()
|
||||
result(devices.map({ $0.flutterCompatibleDict }))
|
||||
}
|
||||
if call.method == "requestCameraPermission" {
|
||||
VitalSignCamera.requestCameraPermission().done { status in
|
||||
result(status.rawValue)
|
||||
}.catch { error in
|
||||
result(FlutterError(code: "\(error.code)", message: error.message, details: nil))
|
||||
}
|
||||
}
|
||||
if call.method == "getCameraPermissionStatus" {
|
||||
let status = VitalSignCamera.getCameraPermissionStatus()
|
||||
result(status.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate extension CameraDevice {
|
||||
var flutterCompatibleDict:[String:Any] {
|
||||
[
|
||||
"id": id,
|
||||
"devices": devices.map { $0.rawValue },
|
||||
"position": position.rawValue,
|
||||
"name": name,
|
||||
"hasFlash": NSNumber(value: hasFlash),
|
||||
"hasTorch": NSNumber(value: hasTorch),
|
||||
"minZoom": NSNumber(value: Double(minZoom)),
|
||||
"neutralZoom": NSNumber(value: Double(neutralZoom)),
|
||||
"maxZoom": NSNumber(value: Double(maxZoom)),
|
||||
"isMultiCam": NSNumber(value: isMultiCam),
|
||||
"supportsParallelVideoProcessing": NSNumber(value: supportsParallelVideoProcessing),
|
||||
"supportsDepthCapture": NSNumber(value: supportsDepthCapture),
|
||||
"supportsRawCapture": NSNumber(value: supportsRawCapture),
|
||||
"supportsLowLightBoost": NSNumber(value: supportsLowLightBoost),
|
||||
"supportsFocus": NSNumber(value: supportsFocus),
|
||||
"formats": formats.map { format -> [String: Any] in
|
||||
format.flutterCompatibleDict
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension CameraCaptureFormat {
|
||||
var flutterCompatibleDict:[String:Any] {
|
||||
var dict:[String:Any] = [
|
||||
"videoStabilizationModes": videoStabilizationModes.map { $0.rawValue },
|
||||
"autoFocusSystem": autoFocusSystem.rawValue,
|
||||
"photoHeight": NSNumber(value: Double(photoHeight)),
|
||||
"photoWidth": NSNumber(value: Double(photoWidth)),
|
||||
"videoHeight": NSNumber(value: Double(videoHeight)),
|
||||
"videoWidth": NSNumber(value: Double(videoWidth)),
|
||||
"maxISO": NSNumber(value: Int(maxISO)),
|
||||
"minISO": NSNumber(value: Int(minISO)),
|
||||
"fieldOfView": NSNumber(value: Double(fieldOfView)),
|
||||
"maxZoom": NSNumber(value: Double(maxZoom)),
|
||||
"colorSpaces": colorSpaces.map { $0.rawValue },
|
||||
"supportsVideoHDR": NSNumber(value: supportsVideoHDR),
|
||||
"supportsPhotoHDR": NSNumber(value: supportsPhotoHDR),
|
||||
"frameRateRanges": frameRateRanges.map {
|
||||
[
|
||||
"minFrameRate": NSNumber(value: Int($0.minFrameRate)),
|
||||
"maxFrameRate": NSNumber(value: Int($0.maxFrameRate)),
|
||||
]
|
||||
},
|
||||
"pixelFormat": pixelFormat.rawValue,
|
||||
]
|
||||
|
||||
if let isHighestPhotoQualitySupported = isHighestPhotoQualitySupported {
|
||||
dict["isHighestPhotoQualitySupported"] = isHighestPhotoQualitySupported
|
||||
}
|
||||
|
||||
return dict
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
//
|
||||
// VitalSignCameraFactory.swift
|
||||
// vital_sign_camera
|
||||
//
|
||||
// Created by Eddie Lau on 24/1/2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import Foundation
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
class VitalSignCameraFactory: NSObject, FlutterPlatformViewFactory {
|
||||
private var messenger: FlutterBinaryMessenger
|
||||
|
||||
init(messenger: FlutterBinaryMessenger) {
|
||||
self.messenger = messenger
|
||||
super.init()
|
||||
}
|
||||
|
||||
func create(
|
||||
withFrame frame: CGRect,
|
||||
viewIdentifier viewId: Int64,
|
||||
arguments args: Any?
|
||||
) -> FlutterPlatformView {
|
||||
return FlutterVitalSignCamera(
|
||||
frame: frame,
|
||||
viewIdentifier: viewId,
|
||||
arguments: args,
|
||||
binaryMessenger: messenger)
|
||||
}
|
||||
|
||||
func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
|
||||
FlutterStandardMessageCodec.sharedInstance()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface VitalSignCameraPlugin : NSObject<FlutterPlugin>
|
||||
@end
|
||||
@ -0,0 +1,15 @@
|
||||
#import "VitalSignCameraPlugin.h"
|
||||
#if __has_include(<vital_sign_camera/vital_sign_camera-Swift.h>)
|
||||
#import <vital_sign_camera/vital_sign_camera-Swift.h>
|
||||
#else
|
||||
// Support project import fallback if the generated compatibility header
|
||||
// is not copied when this plugin is created as a library.
|
||||
// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
|
||||
#import "vital_sign_camera-Swift.h"
|
||||
#endif
|
||||
|
||||
@implementation VitalSignCameraPlugin
|
||||
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
||||
[SwiftVitalSignCameraPlugin registerWithRegistrar:registrar];
|
||||
}
|
||||
@end
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,411 @@
|
||||
#if 0
|
||||
#elif defined(__arm64__) && __arm64__
|
||||
// Generated by Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
|
||||
#ifndef VITALSIGNENGINECORE_SWIFT_H
|
||||
#define VITALSIGNENGINECORE_SWIFT_H
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgcc-compat"
|
||||
|
||||
#if !defined(__has_include)
|
||||
# define __has_include(x) 0
|
||||
#endif
|
||||
#if !defined(__has_attribute)
|
||||
# define __has_attribute(x) 0
|
||||
#endif
|
||||
#if !defined(__has_feature)
|
||||
# define __has_feature(x) 0
|
||||
#endif
|
||||
#if !defined(__has_warning)
|
||||
# define __has_warning(x) 0
|
||||
#endif
|
||||
|
||||
#if __has_include(<swift/objc-prologue.h>)
|
||||
# include <swift/objc-prologue.h>
|
||||
#endif
|
||||
|
||||
#pragma clang diagnostic ignored "-Wauto-import"
|
||||
#if defined(__OBJC__)
|
||||
#include <Foundation/Foundation.h>
|
||||
#endif
|
||||
#if defined(__cplusplus)
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstdbool>
|
||||
#include <cstring>
|
||||
#include <stdlib.h>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
#if defined(__cplusplus)
|
||||
#if defined(__arm64e__) && __has_include(<ptrauth.h>)
|
||||
# include <ptrauth.h>
|
||||
#else
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
|
||||
# ifndef __ptrauth_swift_value_witness_function_pointer
|
||||
# define __ptrauth_swift_value_witness_function_pointer(x)
|
||||
# endif
|
||||
# ifndef __ptrauth_swift_class_method_pointer
|
||||
# define __ptrauth_swift_class_method_pointer(x)
|
||||
# endif
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(SWIFT_TYPEDEFS)
|
||||
# define SWIFT_TYPEDEFS 1
|
||||
# if __has_include(<uchar.h>)
|
||||
# include <uchar.h>
|
||||
# elif !defined(__cplusplus)
|
||||
typedef uint_least16_t char16_t;
|
||||
typedef uint_least32_t char32_t;
|
||||
# endif
|
||||
typedef float swift_float2 __attribute__((__ext_vector_type__(2)));
|
||||
typedef float swift_float3 __attribute__((__ext_vector_type__(3)));
|
||||
typedef float swift_float4 __attribute__((__ext_vector_type__(4)));
|
||||
typedef double swift_double2 __attribute__((__ext_vector_type__(2)));
|
||||
typedef double swift_double3 __attribute__((__ext_vector_type__(3)));
|
||||
typedef double swift_double4 __attribute__((__ext_vector_type__(4)));
|
||||
typedef int swift_int2 __attribute__((__ext_vector_type__(2)));
|
||||
typedef int swift_int3 __attribute__((__ext_vector_type__(3)));
|
||||
typedef int swift_int4 __attribute__((__ext_vector_type__(4)));
|
||||
typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2)));
|
||||
typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3)));
|
||||
typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4)));
|
||||
#endif
|
||||
|
||||
#if !defined(SWIFT_PASTE)
|
||||
# define SWIFT_PASTE_HELPER(x, y) x##y
|
||||
# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)
|
||||
#endif
|
||||
#if !defined(SWIFT_METATYPE)
|
||||
# define SWIFT_METATYPE(X) Class
|
||||
#endif
|
||||
#if !defined(SWIFT_CLASS_PROPERTY)
|
||||
# if __has_feature(objc_class_property)
|
||||
# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__
|
||||
# else
|
||||
# define SWIFT_CLASS_PROPERTY(...)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_RUNTIME_NAME)
|
||||
# if __has_attribute(objc_runtime_name)
|
||||
# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X)))
|
||||
# else
|
||||
# define SWIFT_RUNTIME_NAME(X)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_COMPILE_NAME)
|
||||
# if __has_attribute(swift_name)
|
||||
# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
|
||||
# else
|
||||
# define SWIFT_COMPILE_NAME(X)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_METHOD_FAMILY)
|
||||
# if __has_attribute(objc_method_family)
|
||||
# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X)))
|
||||
# else
|
||||
# define SWIFT_METHOD_FAMILY(X)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_NOESCAPE)
|
||||
# if __has_attribute(noescape)
|
||||
# define SWIFT_NOESCAPE __attribute__((noescape))
|
||||
# else
|
||||
# define SWIFT_NOESCAPE
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_RELEASES_ARGUMENT)
|
||||
# if __has_attribute(ns_consumed)
|
||||
# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))
|
||||
# else
|
||||
# define SWIFT_RELEASES_ARGUMENT
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_WARN_UNUSED_RESULT)
|
||||
# if __has_attribute(warn_unused_result)
|
||||
# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
# else
|
||||
# define SWIFT_WARN_UNUSED_RESULT
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_NORETURN)
|
||||
# if __has_attribute(noreturn)
|
||||
# define SWIFT_NORETURN __attribute__((noreturn))
|
||||
# else
|
||||
# define SWIFT_NORETURN
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_CLASS_EXTRA)
|
||||
# define SWIFT_CLASS_EXTRA
|
||||
#endif
|
||||
#if !defined(SWIFT_PROTOCOL_EXTRA)
|
||||
# define SWIFT_PROTOCOL_EXTRA
|
||||
#endif
|
||||
#if !defined(SWIFT_ENUM_EXTRA)
|
||||
# define SWIFT_ENUM_EXTRA
|
||||
#endif
|
||||
#if !defined(SWIFT_CLASS)
|
||||
# if __has_attribute(objc_subclassing_restricted)
|
||||
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA
|
||||
# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
|
||||
# else
|
||||
# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
|
||||
# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_RESILIENT_CLASS)
|
||||
# if __has_attribute(objc_class_stub)
|
||||
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub))
|
||||
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME)
|
||||
# else
|
||||
# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME)
|
||||
# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_PROTOCOL)
|
||||
# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
|
||||
# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
|
||||
#endif
|
||||
#if !defined(SWIFT_EXTENSION)
|
||||
# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)
|
||||
#endif
|
||||
#if !defined(OBJC_DESIGNATED_INITIALIZER)
|
||||
# if __has_attribute(objc_designated_initializer)
|
||||
# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
|
||||
# else
|
||||
# define OBJC_DESIGNATED_INITIALIZER
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_ENUM_ATTR)
|
||||
# if __has_attribute(enum_extensibility)
|
||||
# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility)))
|
||||
# else
|
||||
# define SWIFT_ENUM_ATTR(_extensibility)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_ENUM)
|
||||
# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
|
||||
# if __has_feature(generalized_swift_name)
|
||||
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type
|
||||
# else
|
||||
# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility)
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_UNAVAILABLE)
|
||||
# define SWIFT_UNAVAILABLE __attribute__((unavailable))
|
||||
#endif
|
||||
#if !defined(SWIFT_UNAVAILABLE_MSG)
|
||||
# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg)))
|
||||
#endif
|
||||
#if !defined(SWIFT_AVAILABILITY)
|
||||
# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__)))
|
||||
#endif
|
||||
#if !defined(SWIFT_WEAK_IMPORT)
|
||||
# define SWIFT_WEAK_IMPORT __attribute__((weak_import))
|
||||
#endif
|
||||
#if !defined(SWIFT_DEPRECATED)
|
||||
# define SWIFT_DEPRECATED __attribute__((deprecated))
|
||||
#endif
|
||||
#if !defined(SWIFT_DEPRECATED_MSG)
|
||||
# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__)))
|
||||
#endif
|
||||
#if !defined(SWIFT_DEPRECATED_OBJC)
|
||||
# if __has_feature(attribute_diagnose_if_objc)
|
||||
# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning")))
|
||||
# else
|
||||
# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)
|
||||
# endif
|
||||
#endif
|
||||
#if defined(__OBJC__)
|
||||
#if !defined(IBSegueAction)
|
||||
# define IBSegueAction
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(SWIFT_EXTERN)
|
||||
# if defined(__cplusplus)
|
||||
# define SWIFT_EXTERN extern "C"
|
||||
# else
|
||||
# define SWIFT_EXTERN extern
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SWIFT_CALL)
|
||||
# define SWIFT_CALL __attribute__((swiftcall))
|
||||
#endif
|
||||
#if !defined(SWIFT_INDIRECT_RESULT)
|
||||
# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result))
|
||||
#endif
|
||||
#if !defined(SWIFT_CONTEXT)
|
||||
# define SWIFT_CONTEXT __attribute__((swift_context))
|
||||
#endif
|
||||
#if !defined(SWIFT_ERROR_RESULT)
|
||||
# define SWIFT_ERROR_RESULT __attribute__((swift_error_result))
|
||||
#endif
|
||||
#if defined(__cplusplus)
|
||||
# define SWIFT_NOEXCEPT noexcept
|
||||
#else
|
||||
# define SWIFT_NOEXCEPT
|
||||
#endif
|
||||
#if !defined(SWIFT_C_INLINE_THUNK)
|
||||
# if __has_attribute(always_inline)
|
||||
# if __has_attribute(nodebug)
|
||||
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug))
|
||||
# else
|
||||
# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline))
|
||||
# endif
|
||||
# else
|
||||
# define SWIFT_C_INLINE_THUNK inline
|
||||
# endif
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
|
||||
# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL)
|
||||
# define SWIFT_IMPORT_STDLIB_SYMBOL
|
||||
#endif
|
||||
#endif
|
||||
#if defined(__OBJC__)
|
||||
#if __has_feature(objc_modules)
|
||||
#if __has_warning("-Watimport-in-framework-header")
|
||||
#pragma clang diagnostic ignored "-Watimport-in-framework-header"
|
||||
#endif
|
||||
@import AVFoundation;
|
||||
@import CoreFoundation;
|
||||
@import CoreMedia;
|
||||
@import Dispatch;
|
||||
@import ObjectiveC;
|
||||
@import UIKit;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch"
|
||||
#pragma clang diagnostic ignored "-Wduplicate-method-arg"
|
||||
#if __has_warning("-Wpragma-clang-attribute")
|
||||
# pragma clang diagnostic ignored "-Wpragma-clang-attribute"
|
||||
#endif
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas"
|
||||
#pragma clang diagnostic ignored "-Wnullability"
|
||||
#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
|
||||
|
||||
#if __has_attribute(external_source_symbol)
|
||||
# pragma push_macro("any")
|
||||
# undef any
|
||||
# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="VitalSignEngineCore",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol))
|
||||
# pragma pop_macro("any")
|
||||
#endif
|
||||
|
||||
#if defined(__OBJC__)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SWIFT_CLASS("_TtC19VitalSignEngineCore12CameraQueues")
|
||||
@interface CameraQueues : NSObject
|
||||
/// The serial execution queue for the camera preview layer (input stream) as well as output processing of photos.
|
||||
SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong) dispatch_queue_t _Nonnull cameraQueue;)
|
||||
+ (dispatch_queue_t _Nonnull)cameraQueue SWIFT_WARN_UNUSED_RESULT;
|
||||
/// The serial execution queue for output processing of videos for recording.
|
||||
SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong) dispatch_queue_t _Nonnull videoQueue;)
|
||||
+ (dispatch_queue_t _Nonnull)videoQueue SWIFT_WARN_UNUSED_RESULT;
|
||||
/// The serial execution queue for output processing of videos for frame processing.
|
||||
SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong) dispatch_queue_t _Nonnull frameProcessorQueue;)
|
||||
+ (dispatch_queue_t _Nonnull)frameProcessorQueue SWIFT_WARN_UNUSED_RESULT;
|
||||
/// The serial execution queue for output processing of audio buffers.
|
||||
SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong) dispatch_queue_t _Nonnull audioQueue;)
|
||||
+ (dispatch_queue_t _Nonnull)audioQueue SWIFT_WARN_UNUSED_RESULT;
|
||||
/// The serial execution queue for output processing of audio buffers.
|
||||
SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong) dispatch_queue_t _Nonnull recordingQueue;)
|
||||
+ (dispatch_queue_t _Nonnull)recordingQueue SWIFT_WARN_UNUSED_RESULT;
|
||||
/// The serial execution queue for output processing of audio buffers.
|
||||
SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong) dispatch_queue_t _Nonnull simulatorQueue;)
|
||||
+ (dispatch_queue_t _Nonnull)simulatorQueue SWIFT_WARN_UNUSED_RESULT;
|
||||
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
|
||||
@end
|
||||
|
||||
@class NSCoder;
|
||||
|
||||
SWIFT_CLASS("_TtC19VitalSignEngineCore10CameraView")
|
||||
@interface CameraView : UIView
|
||||
SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly) Class _Nonnull layerClass;)
|
||||
+ (Class _Nonnull)layerClass SWIFT_WARN_UNUSED_RESULT;
|
||||
- (nonnull instancetype)initWithFrame:(CGRect)frame OBJC_DESIGNATED_INITIALIZER;
|
||||
- (nullable instancetype)initWithCoder:(NSCoder * _Nonnull)_ OBJC_DESIGNATED_INITIALIZER SWIFT_UNAVAILABLE;
|
||||
- (void)willMoveToSuperview:(UIView * _Nullable)newSuperview;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@class UIPinchGestureRecognizer;
|
||||
|
||||
@interface CameraView (SWIFT_EXTENSION(VitalSignEngineCore))
|
||||
- (void)onPinch:(UIPinchGestureRecognizer * _Nonnull)gesture;
|
||||
- (void)zoomWithFactor:(CGFloat)factor animated:(BOOL)animated;
|
||||
@end
|
||||
|
||||
@class AVCaptureOutput;
|
||||
@class AVCaptureConnection;
|
||||
|
||||
@interface CameraView (SWIFT_EXTENSION(VitalSignEngineCore)) <AVCaptureAudioDataOutputSampleBufferDelegate, AVCaptureVideoDataOutputSampleBufferDelegate>
|
||||
- (void)captureOutput:(AVCaptureOutput * _Nonnull)captureOutput didOutputSampleBuffer:(CMSampleBufferRef _Nonnull)sampleBuffer fromConnection:(AVCaptureConnection * _Nonnull)_;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SWIFT_CLASS("_TtC19VitalSignEngineCore15VitalSignCamera")
|
||||
@interface VitalSignCamera : UIView
|
||||
- (nonnull instancetype)initWithFrame:(CGRect)frame OBJC_DESIGNATED_INITIALIZER;
|
||||
- (nullable instancetype)initWithCoder:(NSCoder * _Nonnull)coder SWIFT_UNAVAILABLE;
|
||||
- (void)layoutSubviews;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
#if __has_attribute(external_source_symbol)
|
||||
# pragma clang attribute pop
|
||||
#endif
|
||||
#if defined(__cplusplus)
|
||||
#endif
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error unsupported Swift architecture
|
||||
#endif
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -0,0 +1,4 @@
|
||||
framework module VitalSignEngineCore {
|
||||
header "VitalSignEngineCore-Swift.h"
|
||||
requires objc
|
||||
}
|
||||
Binary file not shown.
@ -0,0 +1,183 @@
|
||||
|
||||
/* Class = "UILabel"; text = "Continuously monitors exposure levels and automatically adjusts exposure when necessary."; ObjectID = "2ad-p5-WH5"; */
|
||||
"2ad-p5-WH5.text" = "Continuously monitors exposure levels and automatically adjusts exposure when necessary.";
|
||||
|
||||
/* Class = "UILabel"; text = "White Balance"; ObjectID = "4ie-UM-xw1"; */
|
||||
"4ie-UM-xw1.text" = "White Balance";
|
||||
|
||||
/* Class = "UILabel"; text = "Front"; ObjectID = "55X-XR-Xoe"; */
|
||||
"55X-XR-Xoe.text" = "Front";
|
||||
|
||||
/* Class = "UITableViewController"; title = "Preview Layer Gravity"; ObjectID = "5qf-l9-Bpt"; */
|
||||
"5qf-l9-Bpt.title" = "Preview Layer Gravity";
|
||||
|
||||
/* Class = "UILabel"; text = "Show the camera preview by preserving its aspect ratio and fits it within the layer's bounds."; ObjectID = "6I3-ak-vEx"; */
|
||||
"6I3-ak-vEx.text" = "Show the camera preview by preserving its aspect ratio and fits it within the layer's bounds.";
|
||||
|
||||
/* Class = "UILabel"; text = "Continuous Auto"; ObjectID = "6yD-61-CEf"; */
|
||||
"6yD-61-CEf.text" = "Continuous Auto";
|
||||
|
||||
/* Class = "UILabel"; text = "White Balance"; ObjectID = "77e-kN-xI5"; */
|
||||
"77e-kN-xI5.text" = "White Balance";
|
||||
|
||||
/* Class = "UILabel"; text = "Preview Gravity"; ObjectID = "7AW-UQ-WfA"; */
|
||||
"7AW-UQ-WfA.text" = "Preview Gravity";
|
||||
|
||||
/* Class = "UILabel"; text = "Lens Aperture"; ObjectID = "7ZB-LO-AYF"; */
|
||||
"7ZB-LO-AYF.text" = "Lens Aperture";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Preview Gravity"; ObjectID = "7aV-Vw-b2w"; */
|
||||
"7aV-Vw-b2w.title" = "Preview Gravity";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Camera"; ObjectID = "8D6-fu-CJu"; */
|
||||
"8D6-fu-CJu.title" = "Camera";
|
||||
|
||||
/* Class = "UILabel"; text = "Medium"; ObjectID = "9xa-bl-6eJ"; */
|
||||
"9xa-bl-6eJ.text" = "Medium";
|
||||
|
||||
/* Class = "UITableViewSection"; headerTitle = "Camera"; ObjectID = "AKs-Pq-lcR"; */
|
||||
"AKs-Pq-lcR.headerTitle" = "Camera";
|
||||
|
||||
/* Class = "UILabel"; text = "640x480"; ObjectID = "B6a-KT-T4m"; */
|
||||
"B6a-KT-T4m.text" = "640x480";
|
||||
|
||||
/* Class = "UILabel"; text = "Face Detector"; ObjectID = "Byq-a3-CTH"; */
|
||||
"Byq-a3-CTH.text" = "Face Detector";
|
||||
|
||||
/* Class = "UILabel"; text = "1920x1080"; ObjectID = "Dp9-9b-EEt"; */
|
||||
"Dp9-9b-EEt.text" = "1920x1080";
|
||||
|
||||
/* Class = "UILabel"; text = "A mode that locks the white balance state."; ObjectID = "FOH-2X-R88"; */
|
||||
"FOH-2X-R88.text" = "A mode that locks the white balance state.";
|
||||
|
||||
/* Class = "UILabel"; text = "Duration"; ObjectID = "FPh-cx-ffz"; */
|
||||
"FPh-cx-ffz.text" = "Duration";
|
||||
|
||||
/* Class = "UILabel"; text = "Exposure"; ObjectID = "Hmc-Mc-AME"; */
|
||||
"Hmc-Mc-AME.text" = "Exposure";
|
||||
|
||||
/* Class = "UILabel"; text = "Resolution"; ObjectID = "J12-De-6Ah"; */
|
||||
"J12-De-6Ah.text" = "Resolution";
|
||||
|
||||
/* Class = "UITextField"; text = "100"; ObjectID = "J2A-ot-v0j"; */
|
||||
"J2A-ot-v0j.text" = "100";
|
||||
|
||||
/* Class = "UILabel"; text = "30 fps"; ObjectID = "J5t-dB-TfS"; */
|
||||
"J5t-dB-TfS.text" = "30 fps";
|
||||
|
||||
/* Class = "UILabel"; text = "Frame Rate"; ObjectID = "JgS-aW-Prv"; */
|
||||
"JgS-aW-Prv.text" = "Frame Rate";
|
||||
|
||||
/* Class = "UILabel"; text = "Video Size"; ObjectID = "KJ4-Uw-4AI"; */
|
||||
"KJ4-Uw-4AI.text" = "Video Size";
|
||||
|
||||
/* Class = "UILabel"; text = "Locked"; ObjectID = "Nuf-3J-OLu"; */
|
||||
"Nuf-3J-OLu.text" = "Locked";
|
||||
|
||||
/* Class = "UILabel"; text = "Frame Rate"; ObjectID = "RKd-LF-IM5"; */
|
||||
"RKd-LF-IM5.text" = "Frame Rate";
|
||||
|
||||
/* Class = "UILabel"; text = "Resize Aspect"; ObjectID = "Rz5-6b-y0h"; */
|
||||
"Rz5-6b-y0h.text" = "Resize Aspect";
|
||||
|
||||
/* Class = "UILabel"; text = "f1.0"; ObjectID = "SxX-36-5NN"; */
|
||||
"SxX-36-5NN.text" = "f1.0";
|
||||
|
||||
/* Class = "UILabel"; text = "Exposure"; ObjectID = "Szo-Pj-jno"; */
|
||||
"Szo-Pj-jno.text" = "Exposure";
|
||||
|
||||
/* Class = "UILabel"; text = "ISO"; ObjectID = "TTb-bW-pMN"; */
|
||||
"TTb-bW-pMN.text" = "ISO";
|
||||
|
||||
/* Class = "UILabel"; text = "Camera"; ObjectID = "WaU-wB-ecv"; */
|
||||
"WaU-wB-ecv.text" = "Camera";
|
||||
|
||||
/* Class = "UITableViewController"; title = "White Balance"; ObjectID = "X40-UA-27w"; */
|
||||
"X40-UA-27w.title" = "White Balance";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Xje-kQ-L0P"; */
|
||||
"Xje-kQ-L0P.title" = "Settings";
|
||||
|
||||
/* Class = "UIBarButtonItem"; title = "Close"; ObjectID = "XvT-YW-xBL"; */
|
||||
"XvT-YW-xBL.title" = "Close";
|
||||
|
||||
/* Class = "UILabel"; text = "Locked"; ObjectID = "YGU-l8-ZMR"; */
|
||||
"YGU-l8-ZMR.text" = "Locked";
|
||||
|
||||
/* Class = "UILabel"; text = "Format"; ObjectID = "YPv-nc-7nK"; */
|
||||
"YPv-nc-7nK.text" = "Format";
|
||||
|
||||
/* Class = "UILabel"; text = "Locked"; ObjectID = "YrA-Y0-l1z"; */
|
||||
"YrA-Y0-l1z.text" = "Locked";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Format"; ObjectID = "Z09-mH-385"; */
|
||||
"Z09-mH-385.title" = "Format";
|
||||
|
||||
/* Class = "UILabel"; text = "Camera On/Off"; ObjectID = "ZiN-q3-8Cs"; */
|
||||
"ZiN-q3-8Cs.text" = "Camera On/Off";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "White Balance"; ObjectID = "b1A-7E-zBH"; */
|
||||
"b1A-7E-zBH.title" = "White Balance";
|
||||
|
||||
/* Class = "UILabel"; text = "100"; ObjectID = "b1L-VE-boz"; */
|
||||
"b1L-VE-boz.text" = "100";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Exposure"; ObjectID = "bwF-Fn-XeY"; */
|
||||
"bwF-Fn-XeY.title" = "Exposure";
|
||||
|
||||
/* Class = "UIButton"; configuration.title = "Test"; ObjectID = "cC0-N9-hxt"; */
|
||||
"cC0-N9-hxt.configuration.title" = "Test";
|
||||
|
||||
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "cC0-N9-hxt"; */
|
||||
"cC0-N9-hxt.normalTitle" = "Test";
|
||||
|
||||
/* Class = "UISegmentedControl"; fSu-H9-Cz9.segmentTitles[0] = "sec"; ObjectID = "fSu-H9-Cz9"; */
|
||||
"fSu-H9-Cz9.segmentTitles[0]" = "sec";
|
||||
|
||||
/* Class = "UISegmentedControl"; fSu-H9-Cz9.segmentTitles[1] = "ms"; ObjectID = "fSu-H9-Cz9"; */
|
||||
"fSu-H9-Cz9.segmentTitles[1]" = "ms";
|
||||
|
||||
/* Class = "UISegmentedControl"; fSu-H9-Cz9.segmentTitles[2] = "μs"; ObjectID = "fSu-H9-Cz9"; */
|
||||
"fSu-H9-Cz9.segmentTitles[2]" = "μs";
|
||||
|
||||
/* Class = "UILabel"; text = "Locked"; ObjectID = "fty-cZ-huK"; */
|
||||
"fty-cZ-huK.text" = "Locked";
|
||||
|
||||
/* Class = "UILabel"; text = "Auto (10ms)"; ObjectID = "hVI-M7-yVe"; */
|
||||
"hVI-M7-yVe.text" = "Auto (10ms)";
|
||||
|
||||
/* Class = "UILabel"; text = "Camera Name"; ObjectID = "if3-5B-xPF"; */
|
||||
"if3-5B-xPF.text" = "Camera Name";
|
||||
|
||||
/* Class = "UILabel"; text = "Unit"; ObjectID = "l6D-7r-DH5"; */
|
||||
"l6D-7r-DH5.text" = "Unit";
|
||||
|
||||
/* Class = "UINavigationItem"; title = "Camera Test"; ObjectID = "lZ7-W3-MRS"; */
|
||||
"lZ7-W3-MRS.title" = "Camera Test";
|
||||
|
||||
/* Class = "UILabel"; text = "8ms"; ObjectID = "lct-8a-Lqk"; */
|
||||
"lct-8a-Lqk.text" = "8ms";
|
||||
|
||||
/* Class = "UITableViewController"; title = "Camera"; ObjectID = "lvC-jH-Oee"; */
|
||||
"lvC-jH-Oee.title" = "Camera";
|
||||
|
||||
/* Class = "UILabel"; text = "Resize Aspect Fill"; ObjectID = "mod-K3-RQU"; */
|
||||
"mod-K3-RQU.text" = "Resize Aspect Fill";
|
||||
|
||||
/* Class = "UILabel"; text = "HD 1920x1080"; ObjectID = "nbm-4k-ihb"; */
|
||||
"nbm-4k-ihb.text" = "HD 1920x1080";
|
||||
|
||||
/* Class = "UILabel"; text = "30 fps"; ObjectID = "pKS-rd-Dx6"; */
|
||||
"pKS-rd-Dx6.text" = "30 fps";
|
||||
|
||||
/* Class = "UILabel"; text = "Exposure Duration"; ObjectID = "soE-ah-Y4f"; */
|
||||
"soE-ah-Y4f.text" = "Exposure Duration";
|
||||
|
||||
/* Class = "UILabel"; text = "30 fps"; ObjectID = "u82-lR-lTP"; */
|
||||
"u82-lR-lTP.text" = "30 fps";
|
||||
|
||||
/* Class = "UILabel"; text = "100"; ObjectID = "y9H-1I-dYX"; */
|
||||
"y9H-1I-dYX.text" = "100";
|
||||
|
||||
/* Class = "UILabel"; text = "ISO"; ObjectID = "yOn-QG-KiO"; */
|
||||
"yOn-QG-KiO.text" = "ISO";
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue