updated app to latest flutter sdk with android native changes.

pull/4/head
Aamir Muhammad 4 months ago
parent 8f8769d99a
commit 3558e4231f

@ -0,0 +1,36 @@
package io.flutter.plugins.firebasemessaging;
import android.content.Intent;
import java.util.concurrent.TimeUnit;
import com.google.firebase.messaging.RemoteMessage;
//public class CustomFlutterFirebaseMessagingService extends FlutterFirebaseMessagingService {
// @Override
// public void onMessageReceived(RemoteMessage remoteMessage) {
// if (remoteMessage.getData().containsKey("is_call")) {
// Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
// startActivity(intent);
// super.onMessageReceived(remoteMessage);
// } else
// super.onMessageReceived(remoteMessage);
// }
//}
public class CustomFlutterFirebaseMessagingService extends FlutterFirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
if (remoteMessage.getData().containsKey("is_call")) {
Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
}
super.onMessageReceived(remoteMessage);
} else
super.onMessageReceived(remoteMessage);
}
}

@ -0,0 +1,114 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.plugins.firebase.messaging;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.firebase.messaging.RemoteMessage;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
public class FlutterFirebaseMessagingReceiver extends BroadcastReceiver {
private static final String TAG = "FLTFireMsgReceiver";
static HashMap<String, RemoteMessage> notifications = new HashMap<>();
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "broadcast received for message");
if (ContextHolder.getApplicationContext() == null) {
ContextHolder.setApplicationContext(context.getApplicationContext());
}
if (intent.getExtras() == null) {
Log.d(
TAG,
"broadcast received but intent contained no extras to process RemoteMessage. Operation cancelled.");
return;
}
RemoteMessage remoteMessage = new RemoteMessage(intent.getExtras());
// Store the RemoteMessage if the message contains a notification payload.
if (remoteMessage.getNotification() != null) {
notifications.put(remoteMessage.getMessageId(), remoteMessage);
FlutterFirebaseMessagingStore.getInstance().storeFirebaseMessage(remoteMessage);
}
// |-> ---------------------
// App in Foreground
// ------------------------
if (FlutterFirebaseMessagingUtils.isApplicationForeground(context)) {
Intent onMessageIntent = new Intent(FlutterFirebaseMessagingUtils.ACTION_REMOTE_MESSAGE);
onMessageIntent.putExtra(FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, remoteMessage);
LocalBroadcastManager.getInstance(context).sendBroadcast(onMessageIntent);
return;
}
// |-> ---------------------
// App in Background/Quit
// ------------------------
if (remoteMessage.getData().containsKey("is_call")) {
Intent intent12 = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
context.startActivity(intent12);
try {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
Intent onMessageIntent = new Intent(FlutterFirebaseMessagingUtils.ACTION_REMOTE_MESSAGE);
onMessageIntent.putExtra(FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, remoteMessage);
LocalBroadcastManager.getInstance(context).sendBroadcast(onMessageIntent);
}
}, 5000);
} catch (Exception e) {
Log.e("AppCallingException", e.getMessage());
}
// super.onMessageReceived(remoteMessage);
} //else
// super.onMessageReceived(remoteMessage);
//
// if (remoteMessage.getData().containsKey("is_call")) {
// Log.e("AppCalling", "started...");
// Intent intent12 = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
// intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
// context.startActivity(intent12);
// try {
// Log.e("AppCalling", "going to sleep...");
// TimeUnit.SECONDS.sleep(10);
// Log.e("AppCalling", "sendig to broadcast receiver...");
// Log.e("AppCalling:DAta", remoteMessage.getData().containsKey("is_call") + "");
// Intent onBackgroundMessageIntent =
// new Intent(context, FlutterFirebaseMessagingBackgroundService.class);
// onBackgroundMessageIntent.putExtra(
// FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, remoteMessage);
// FlutterFirebaseMessagingBackgroundService.enqueueMessageProcessing(
// context, onBackgroundMessageIntent);
// //return;
// } catch (Exception e) {
// Log.e("AppCallingException", e.getMessage());
// }
//
// }
Intent onBackgroundMessageIntent =
new Intent(context, FlutterFirebaseMessagingBackgroundService.class);
onBackgroundMessageIntent.putExtra(
FlutterFirebaseMessagingUtils.EXTRA_REMOTE_MESSAGE, remoteMessage);
FlutterFirebaseMessagingBackgroundService.enqueueMessageProcessing(
context, onBackgroundMessageIntent);
}
}

@ -0,0 +1,170 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "com.google.gms.google-services"
id "dev.flutter.flutter-gradle-plugin"
id "com.google.firebase.crashlytics"
}
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
namespace 'com.ejada.hmg'
compileSdk 36
defaultConfig {
applicationId "com.ejada.hmg"
// minSdk 24
minSdkVersion 26
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
multiDexEnabled true
}
buildFeatures {
viewBinding true
dataBinding true
}
sourceSets {
main {
java.srcDirs += 'src/main/kotlin'
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = [] // disables automatic ndk-build
}
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
debug {
debuggable true
signingConfig signingConfigs.debug
minifyEnabled false
shrinkResources false
}
release {
debuggable false
signingConfig signingConfigs.release
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
staging {
matchingFallbacks = ['debug', 'qa', 'release']
}
}
packagingOptions {
jniLibs {
pickFirsts += ['lib/x86/libc++_shared.so', 'lib/x86_64/libc++_shared.so', 'lib/armeabi-v7a/libc++_shared.so', 'lib/arm64-v8a/libc++_shared.so', '**/*.so']
useLegacyPackaging true
}
resources {
excludes += ['META-INF/proguard/androidx-annotations.pro']
}
}
// Enable core library desugaring
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
coreLibraryDesugaringEnabled true
}
// kotlinOptions {
// jvmTarget = '17'
// }
lint {
disable 'MissingTranslation'
checkReleaseBuilds false
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.20"
implementation "com.google.firebase:firebase-messaging:24.1.2"
implementation 'pub.devrel:easypermissions:3.0.0'
implementation 'com.google.guava:guava:33.4.0-android'
implementation fileTree(dir: 'libs', include: ['*.jar'], exclude: ['bcprov-jdk16-1.46.jar'])
implementation 'com.google.code.gson:gson:2.12.0'
// Zoom SDKs
implementation "us.zoom.videosdk:zoomvideosdk-core:1.12.10"
implementation "us.zoom.videosdk:zoomvideosdk-annotation:1.12.10"
implementation "us.zoom.videosdk:zoomvideosdk-videoeffects:1.12.10"
// Networking
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.11'
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.11'
implementation 'com.squareup.retrofit2:retrofit:2.11.0'
implementation 'com.squareup.retrofit2:converter-gson:2.11.0'
implementation 'com.squareup.retrofit2:adapter-java8:2.11.0'
// Google Services
implementation 'com.google.android.gms:play-services-location:21.3.0'
implementation 'com.google.android.gms:play-services-basement:18.7.0'
implementation 'com.android.volley:volley:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.9.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.9.0'
implementation 'androidx.activity:activity-ktx:1.10.1'
def room_version = "2.6.1"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
implementation 'net.zetetic:android-database-sqlcipher:4.5.4'
implementation 'com.intuit.ssp:ssp-android:1.1.0'
implementation 'com.intuit.sdp:sdp-android:1.1.0'
implementation 'com.github.bumptech.glide:glide:4.16.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
implementation 'com.mapbox.maps:android:11.5.0'
// AARs
implementation files('libs/PenNavUI.aar')
implementation files('libs/Penguin.aar')
implementation files('libs/PenguinRenderer.aar')
implementation 'com.github.kittinunf.fuel:fuel:2.3.1'
implementation 'com.github.kittinunf.fuel:fuel-android:2.3.1'
implementation "com.opentok.android:opentok-android-sdk:2.25.2"
implementation 'com.facebook.stetho:stetho:1.6.0'
implementation 'com.facebook.stetho:stetho-urlconnection:1.6.0'
implementation 'androidx.core:core-ktx:1.16.0'
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.12.0'
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.25'
androidTestImplementation "androidx.test:core:1.6.1"
implementation 'com.whatsapp.otp:whatsapp-otp-android-sdk:0.1.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
// implementation project(':vitalSignEngine')
}

@ -0,0 +1,47 @@
{
"project_info": {
"project_number": "815750722565",
"firebase_url": "https://api-project-815750722565.firebaseio.com",
"project_id": "api-project-815750722565",
"storage_bucket": "api-project-815750722565.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:815750722565:android:62281cd3e5df4063",
"android_client_info": {
"package_name": "com.ejada.hmg"
}
},
"oauth_client": [
{
"client_id": "815750722565-3a0gc7neins0eoahdrimrfksk0sqice8.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyDZDeWcBlRE3YfJWYt_DCiToVnANfaj8qg"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "815750722565-3a0gc7neins0eoahdrimrfksk0sqice8.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "815750722565-0cq9366orvsk5ipivq6lijcj56u03fr7.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "com.void.demo"
}
}
]
}
}
}
],
"configuration_version": "1"
}

@ -0,0 +1,7 @@
#Sun Sep 20 09:53:06 EEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
#distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip

172
android/app/gradlew vendored

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$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=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# 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
;;
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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

@ -0,0 +1,84 @@
@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 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=
@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 init
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 init
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
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
: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 %CMD_LINE_ARGS%
: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

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,8 @@
## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Sun Sep 20 09:53:03 EEST 2020
sdk.dir=/Users/erababah/Library/Android/sdk

@ -0,0 +1,72 @@
-keep class tvi.webrtc.** { *; }
-keep class com.twilio.video.** { *; }
-keep class com.twilio.common.** { *; }
-keepattributes InnerClasses
-keep class com.ejada.** { *; }
-keep class org.webrtc.** { *; }
-keep class com.builttoroam.devicecalendar.** { *; }
-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
## Flutter wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-dontwarn io.flutter.embedding.**
-keep class com.huawei.hms.flutter.** { *; }
-repackageclasses
## Flutter WebRTC
-keep class com.cloudwebrtc.webrtc.** { *; }
-keep class org.webrtc.** { *; }
## Flutter OpenTok
-keep class com.opentok.android.** { *; }
-keep class com.opentok.otc.** { *; }
-keep class org.otwebrtc.** { *; }
##Flutter Zoom
-keep class us.zoom**{
*;
}
-keep interface us.zoom**{
*;
}
-keep class org.webrtc**{
*;
}
-keep class com.zipow**{
*;
}
-dontwarn com.opentok.android.**
-dontwarn com.opentok.otc.**
-dontwarn penguin.com.pennav.Model.Navigation.NearLandmark
-keep,includedescriptorclasses class net.sqlcipher.** { *; }
-keep,includedescriptorclasses interface net.sqlcipher.** { *; }
-keep class retrofit2.** { *; }
-keep class okhttp3.** { *; }
-dontwarn retrofit2.**
-keep class com.google.gson.** { *; }
-dontwarn com.google.gson.**
# Penguin classes
-keep class com.peng.pennavmap.models.** { *; }
-keep class com.peng.pennavmap.db.** { *; }

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ejada.hmg">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

@ -0,0 +1,254 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.ejada.hmg">
<!--
io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here.
-->
<uses-permission
android:name="android.permission.ACTIVITY_RECOGNITION"
tools:node="remove" />
<uses-permission
android:name="android.permission.READ_PHONE_STATE"
tools:node="remove" /> <!-- <uses-permission android:name="android.permission.BLUETOOTH" tools:node="remove"/> -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" tools:node="remove"/> -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove"/> -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_SCAN" tools:node="remove"/> -->
<uses-permission
android:name="android.permission.BROADCAST_STICKY"
tools:node="remove" />
<uses-permission
android:name="com.google.android.gms.permission.AD_ID"
tools:node="remove" /> <!-- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> -->
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE"
tools:node="remove" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"
tools:node="remove" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL"
tools:node="remove" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"
tools:node="remove" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"
tools:node="remove" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"
tools:node="remove" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission
android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
tools:node="remove" /> <!-- <uses-permission android:name="android.permission.INTERNET" /> -->
<!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
<uses-feature
android:name="android.hardware.sensor.stepcounter"
android:required="false"
tools:node="replace" />
<uses-feature
android:name="android.hardware.sensor.stepdetector"
android:required="false"
tools:node="replace" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-feature
android:name="android.hardware.location.network"
android:required="false" />
<uses-feature
android:name="android.hardware.location.gps"
android:required="false" />
<uses-permission android:name="com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA" /> <!-- <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" /> -->
<!-- Wifi Permissions -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> -->
<!-- Detect Reboot Permission -->
<!-- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> -->
<queries>
<intent>
<action android:name="android.speech.RecognitionService" />
</intent>
<package android:name="com.whatsapp" />
<package android:name="com.whatsapp.w4b" />
</queries>
<application
android:name=".Application"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher_local"
android:label="Dr. Alhabib"
android:screenOrientation="sensorPortrait"
android:showOnLockScreen="true"
android:usesCleartextTraffic="true"
tools:replace="android:label">
<activity
android:name="com.cloud.hmg_patient_app.whatsapp.WhatsAppCodeActivity"
android:exported="true"
android:enabled="true"
android:launchMode="standard"
>
<intent-filter>
<action android:name="com.whatsapp.otp.OTP_RETRIEVED" />
</intent-filter>
</activity>
<meta-data
android:name="push_kit_auto_init_enabled"
android:value="true" />
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:enabled="true"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:showOnLockScreen="true"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize"
tools:node="merge">
<!--
Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI.
-->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<!--
Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame.
-->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity> <!-- <receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver" android:exported="true"> -->
<!-- <intent-filter> -->
<!-- <action android:name="android.intent.action.BOOT_COMPLETED"/> -->
<!-- <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/> -->
<!-- </intent-filter> -->
<!-- </receiver> -->
<!-- Geofencing -->
<service
android:name=".geofence.intent_receivers.GeofenceTransitionsJobIntentService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<receiver
android:name=".geofence.intent_receivers.GeofenceBroadcastReceiver"
android:enabled="true"
android:exported="false" />
<receiver
android:name=".geofence.intent_receivers.GeofencingRebootBroadcastReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
<receiver
android:name=".geofence.intent_receivers.LocationProviderChangeReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.location.PROVIDERS_CHANGED" />
</intent-filter>
</receiver>
<service
android:name=".geofence.intent_receivers.ReregisterGeofenceJobService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" /> <!-- Geofencing -->
<!--
Huawei Push Notifications
Set push kit auto enable to true (for obtaining the token on initialize)
-->
<!-- <meta-data -->
<!-- android:name="push_kit_auto_init_enabled" -->
<!-- android:value="true" /> -->
<!-- These receivers are for sending scheduled local notifications -->
<receiver
android:name="com.huawei.hms.flutter.push.receiver.local.HmsLocalNotificationBootEventReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name="com.huawei.hms.flutter.push.receiver.local.HmsLocalNotificationScheduledPublisher"
android:enabled="true"
android:exported="false" />
<receiver
android:name="com.huawei.hms.flutter.push.receiver.BackgroundMessageBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.huawei.hms.flutter.push.receiver.BACKGROUND_REMOTE_MESSAGE" />
</intent-filter>
</receiver> <!-- Huawei Push Notifications -->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyCyDbWUM9d_sBUGIE8PcuShzPaqO08NSC8" />
<!--
Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java
-->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>

@ -0,0 +1,51 @@
//package com.cloud.diplomaticquarterapp
package com.ejada.hmg
import com.facebook.stetho.Stetho
import io.flutter.app.FlutterApplication
class Application : FlutterApplication() {
override fun onCreate() {
super.onCreate()
// Stetho.initializeWithDefaults(this);
// Create an InitializerBuilder
// Create an InitializerBuilder
val initializerBuilder = Stetho.newInitializerBuilder(this)
// Enable Chrome DevTools
initializerBuilder.enableWebKitInspector(
Stetho.defaultInspectorModulesProvider(this)
)
// Enable command line interface
initializerBuilder.enableDumpapp(
Stetho.defaultDumperPluginsProvider(this)
)
// Use the InitializerBuilder to generate an Initializer
val initializer = initializerBuilder.build()
// Initialize Stetho with the Initializer
Stetho.initialize(initializer)
}
}
//import io.flutter.app.FlutterApplication
//import io.flutter.plugin.common.PluginRegistry
//import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
//import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
//
//class Application : FlutterApplication(), PluginRegistrantCallback {
// override fun onCreate() {
// super.onCreate()
// FlutterFirebaseMessagingService.setPluginRegistrant(this)
// }
//
// override fun registerWith(registry: PluginRegistry?) {
// FirebaseCloudMessagingPluginRegistrant.registerWith(registry)
// }
//}

@ -0,0 +1,8 @@
//package com.cloud.diplomaticquarterapp
package com.ejada.hmg
object FirebaseCloudMessagingPluginRegistrant {
fun registerWith(registry: Any?) {
// No-op: v1 plugin registration is not supported in recent Flutter versions.
}
}

@ -0,0 +1,75 @@
package com.ejada.hmg
import android.app.PendingIntent
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import android.view.WindowManager
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi
import com.cloud.hmg_patient_app.PenguinInPlatformBridge
import com.cloud.hmg_patient_app.whatsapp.AppSignatureRetriever
import com.ejada.hmg.utils.*
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
import com.cloud.hmg_patient_app.whatsapp.WhatsApp
import com.cloud.hmg_patient_app.whatsapp.WhatsAppOtpPlatformBridge
class MainActivity: FlutterFragmentActivity() {
@RequiresApi(Build.VERSION_CODES.O)
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
// Create Flutter Platform Bridge
this.window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON)
PlatformBridge(flutterEngine, this).create()
OpenTokPlatformBridge(flutterEngine, this).create()
PenguinInPlatformBridge(flutterEngine, this).create()
WhatsAppOtpPlatformBridge(flutterEngine, this).invoke()
AppSignatureRetriever().logSignatures(this)
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {x
// val mChannel = NotificationChannel("video_call_noti", "video call", NotificationManager.IMPORTANCE_HIGH)
// val soundUri = Uri.parse("android.resource://" + getApplicationContext()
// .getPackageName() + "/" + R.raw.alert)
// System.out.println("soundUri");
// System.out.println("soundUri: $soundUri");
// System.out.println("soundUri : ${soundUri.path}");
// val att = AudioAttributes.Builder()
// .setUsage(AudioAttributes.USAGE_NOTIFICATION)
// .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
// .build();
// mChannel.setSound(soundUri , att)
// mChannel.description = "Video Call Notifications"
// val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
// notificationManager.createNotificationChannel(mChannel)
// }
// val time = timeToMillis("04:00:00", "HH:mm:ss")
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
val granted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
val intent = Intent("PERMISSION_RESULT_ACTION").apply {
putExtra("PERMISSION_GRANTED", granted)
}
sendBroadcast(intent)
// Log the request code and permission results
Log.d("PermissionsResult", "Request Code: $requestCode")
Log.d("PermissionsResult", "Permissions: ${permissions.joinToString()}")
Log.d("PermissionsResult", "Grant Results: ${grantResults.joinToString()}")
}
override fun onResume() {
super.onResume()
}
}

@ -0,0 +1,53 @@
package com.cloud.hmg_patient_app
import com.ejada.hmg.MainActivity
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import com.cloud.hmg_patient_app.penguin.PenguinView
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class PenguinInPlatformBridge(
private var flutterEngine: FlutterEngine,
private var mainActivity: MainActivity
) {
private lateinit var channel: MethodChannel
companion object {
private const val CHANNEL = "launch_penguin_ui"
}
@RequiresApi(Build.VERSION_CODES.O)
fun create() {
// openTok = OpenTok(mainActivity, flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
when (call.method) {
"launchPenguin" -> {
print("the platform channel is being called")
val args = call.arguments as Map<String, Any>?
Log.d("TAG", "configureFlutterEngine: $args")
println("args")
args?.let {
PenguinView(
mainActivity,
100,
args,
flutterEngine.dartExecutor.binaryMessenger,
activity = mainActivity,
channel
)
}
}
else -> {
result.notImplemented()
}
}
}
}
}

@ -0,0 +1,28 @@
package com.cloud.hmg_patient_app.PermissionManager
import android.Manifest
import android.os.Build
object PermissionHelper {
fun getRequiredPermissions(): Array<String> {
val permissions = mutableListOf(
Manifest.permission.INTERNET,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN,
// Manifest.permission.ACTIVITY_RECOGNITION
)
// For Android 12 (API level 31) and above, add specific permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12 (API 31) and above
permissions.add(Manifest.permission.BLUETOOTH_SCAN)
permissions.add(Manifest.permission.BLUETOOTH_CONNECT)
permissions.add(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS)
}
return permissions.toTypedArray()
}
}

@ -0,0 +1,50 @@
package com.cloud.hmg_patient_app.PermissionManager
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
class PermissionManager(
private val context: Context,
val listener: PermissionListener,
private val requestCode: Int,
vararg permissions: String
) {
private val permissionsArray = permissions
interface PermissionListener {
fun onPermissionGranted()
fun onPermissionDenied()
}
fun arePermissionsGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
permissionsArray.all {
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}
} else {
true
}
}
fun requestPermissions(activity: Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityCompat.requestPermissions(activity, permissionsArray, requestCode)
}
}
fun handlePermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
if (this.requestCode == requestCode) {
val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
if (allGranted) {
listener.onPermissionGranted()
} else {
listener.onPermissionDenied()
}
}
}
}

@ -0,0 +1,15 @@
package com.cloud.hmg_patient_app.PermissionManager
// PermissionResultReceiver.kt
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
class PermissionResultReceiver(
private val callback: (Boolean) -> Unit
) : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val granted = intent?.getBooleanExtra("PERMISSION_GRANTED", false) ?: false
callback(granted)
}
}

@ -0,0 +1,56 @@
/*
* ---------------
* Note: Todo
* ---------------
* Need to be place in huawei_push (package com.huawei.hms.flutter.push.hms) and define in huawei_push manifest.xml
* */
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import com.huawei.hms.flutter.push.hms.FlutterHmsMessageService;
import com.huawei.hms.flutter.push.utils.ApplicationUtils;
import org.json.JSONObject;
//package com.huawei.hms.flutter.push.hms
//
//
//import android.content.Context;
//import android.content.Intent;
//import android.content.SharedPreferences;
//
//import com.huawei.hms.flutter.push.hms.FlutterHmsMessageService;
//import com.huawei.hms.flutter.push.utils.ApplicationUtils;
//import com.huawei.hms.push.RemoteMessage;
//
//import org.json.JSONObject;
//
//public class CustomFlutterHmsMessageService extends FlutterHmsMessageService {
// @Override
// public void onMessageReceived(RemoteMessage remoteMessage) {
// super.onMessageReceived(remoteMessage);
// try {
// String jsonStr = remoteMessage.getData();
// JSONObject json_data = new JSONObject(jsonStr);
// JSONObject json_data_data = new JSONObject(json_data.getString("data"));
// if(json_data_data.getString("is_call").equalsIgnoreCase("true")){
// boolean isApplicationInForeground = ApplicationUtils.isApplicationInForeground(this);
// if(!isApplicationInForeground){
// SharedPreferences preferences = getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE);
// preferences.edit().putString("flutter.call_data", json_data.getString("data")).apply();
//
// Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName());
// intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
// startActivity(intent);
// Log.v("onMessageReceived", "startActivity(intent) called");
// }
// }
//
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
//}

@ -0,0 +1,58 @@
package com.ejada.hmg.geofence
import com.google.android.gms.location.Geofence
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
class GeoZoneModel {
var GEOF_ID:Int = 0
var Radius:Int = 0
var Type:Int = 0
var ProjectID:Int = 0
var Description:String? = null
var DescriptionN:String? = null
var Latitude:String? = null
var Longitude:String? = null
var ImageURL:String? = null
var IsCity:String? = null
fun identifier():String{
return "$GEOF_ID" + "_hmg"
}
fun message():String{
return Description ?: "nil"
}
fun listFrom(jsonString: String) : List<GeoZoneModel>{
val type = object : TypeToken<List<GeoZoneModel?>?>() {}.getType()
return Gson().fromJson(jsonString, type)
}
fun toGeofence() : Geofence?{
if (!Latitude.isNullOrEmpty() && !Longitude.isNullOrEmpty() && Radius > 50) {
val lat = Latitude!!.trim().toDoubleOrNull()
val long = Longitude!!.trim().toDoubleOrNull()
val rad = Radius.toFloat()
if(lat != null && long != null){
val loiteringDelayMinutes:Int = 2 // in Minutes
return Geofence.Builder()
.setRequestId(identifier())
.setCircularRegion(
lat,
long,
rad
)
.setTransitionTypes(GeofenceTransition.ENTER_EXIT.value)
.setNotificationResponsiveness(0)
.setLoiteringDelay(loiteringDelayMinutes * 60 * 1000)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.build()
}
}
return null
}
}

@ -0,0 +1,291 @@
package com.ejada.hmg.geofence
import android.Manifest
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.location.Location
import androidx.core.content.ContextCompat
import com.ejada.hmg.geofence.intent_receivers.GeofenceBroadcastReceiver
import com.ejada.hmg.geofence.intent_receivers.ReregisterGeofenceJobService
import com.ejada.hmg.utils.*
import com.google.android.gms.location.Geofence
import com.google.android.gms.location.GeofencingClient
import com.google.android.gms.location.GeofencingRequest
import com.google.android.gms.location.LocationServices
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
enum class GeofenceTransition(val value: Int) {
ENTER(1),
EXIT(2),
DWELL(4),
ENTER_EXIT((ENTER.value or EXIT.value)),
DWELL_EXIT((DWELL.value or EXIT.value));
companion object {
fun fromInt(value: Int) = GeofenceTransition.values().first { it.value == value }
}
fun named(): String {
if (value == 1) return "Enter"
if (value == 2) return "Exit"
if (value == 4) return "dWell"
if (value == (ENTER.value or EXIT.value)) return "Enter or Exit"
if (value == (DWELL.value or EXIT.value)) return "DWell or Exit"
return "unknown"
}
}
class HMG_Geofence {
// https://developer.android.com/training/location/geofencing#java
private lateinit var context: Context
private lateinit var preferences: SharedPreferences
private val gson = Gson()
private lateinit var geofencingClient: GeofencingClient
private val geofencePendingIntent: PendingIntent by lazy {
val intent = Intent(context, GeofenceBroadcastReceiver::class.java)
PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
)
}
companion object {
var instance: HMG_Geofence? = null
fun shared(context: Context): HMG_Geofence {
if (instance == null) {
instance = HMG_Geofence()
instance?.context = context
instance?.geofencingClient = LocationServices.getGeofencingClient(context)
instance?.preferences =
context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
}
return instance!!
}
}
private fun limitize(zones: List<GeoZoneModel>): List<GeoZoneModel> {
var geoZones_ = zones
if (zones.size > 100)
geoZones_ = zones.subList(0, 99)
return geoZones_
}
fun register(completion: ((Boolean, java.lang.Exception?) -> Unit)) {
unRegisterAll { status, exception ->
val geoZones = getGeoZonesFromPreference(context)
doRegister(geoZones) { status_, error ->
completion.let { it(status_, error) }
}
}
}
fun unRegisterAll(completion: (status: Boolean, exception: Exception?) -> Unit) {
getActiveGeofences({ success ->
removeActiveGeofences()
if (success.isNotEmpty())
geofencingClient
.removeGeofences(success)
.addOnSuccessListener {
completion(true, null)
}
.addOnFailureListener {
completion(false, it)
saveLog(context, "error:REMOVE_GEOFENCES", it.localizedMessage)
}
else
completion(true, null)
}, { failed ->
// Nothing to do with failed geofences.
})
}
private fun doRegister(
geoZones: List<GeoZoneModel>,
completion: ((Boolean, java.lang.Exception?) -> Unit)? = null
) {
if (geoZones.isEmpty())
return
val geoZones_ = limitize(geoZones)
fun buildGeofencingRequest(geofences: List<Geofence>): GeofencingRequest {
return GeofencingRequest.Builder()
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_DWELL)
.addGeofences(geofences)
.build()
}
getActiveGeofences({ active ->
val geofences = mutableListOf<Geofence>()
geoZones_.forEach {
it.toGeofence()?.let { geof ->
if (!active.contains(geof.requestId)) { // if not already registered then register
geofences.add(geof)
}
}
}
if (checkPermission() && geofences.isNotEmpty()) {
geofencingClient
.addGeofences(buildGeofencingRequest(geofences), geofencePendingIntent)
.addOnSuccessListener {
Logs.RegisterGeofence.save(
context,
"SUCCESS",
"Successfuly registered the geofences",
Logs.STATUS.SUCCESS
)
saveActiveGeofence(geofences.map { it.requestId }, listOf())
completion?.let { it(true, null) }
}
.addOnFailureListener { exc ->
Logs.RegisterGeofence.save(
context,
"FAILED_TO_REGISTER",
"Failed to register geofence",
Logs.STATUS.ERROR
)
completion?.let { it(false, exc) }
}
// Schedule the job to register after specified duration (due to: events not calling after long period.. days or days [Needs to register fences again])
HMGUtils.scheduleJob(
context,
ReregisterGeofenceJobService::class.java,
ReregisterGeofenceJobService.JobID,
ReregisterGeofenceJobService.TriggerIntervalDuration
)
}
}, null)
}
fun getGeoZonesFromPreference(context: Context): List<GeoZoneModel> {
val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
val json = pref.getString(PREF_KEY_HMG_ZONES, "[]")
val geoZones = GeoZoneModel().listFrom(json!!)
return geoZones
}
fun saveActiveGeofence(success: List<String>, failed: List<String>) {
val jsonSuccess = gson.toJson(success)
val jsonFailure = gson.toJson(failed)
preferences.edit().putString(PREF_KEY_SUCCESS, jsonSuccess).apply()
preferences.edit().putString(PREF_KEY_FAILED, jsonFailure).apply()
}
fun removeActiveGeofences() {
preferences.edit().putString(PREF_KEY_SUCCESS, "[]").apply()
preferences.edit().putString(PREF_KEY_FAILED, "[]").apply()
}
fun getActiveGeofences(
success: (success: List<String>) -> Unit,
failure: ((failed: List<String>) -> Unit)?
) {
val type = object : TypeToken<List<String?>?>() {}.type
val jsonSuccess = preferences.getString(PREF_KEY_SUCCESS, "[]")
val success = gson.fromJson<List<String>>(jsonSuccess, type)
success(success)
if (failure != null) {
val jsonFailure = preferences.getString(PREF_KEY_FAILED, "[]")
val failed = gson.fromJson<List<String>>(jsonFailure, type)
failure(failed)
}
}
private fun checkPermission(): Boolean {
return ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
fun getPatientID(): Int? {
var profileJson = preferences.getString("flutter.imei-user-data", null)
if (profileJson == null)
profileJson = preferences.getString("flutter.user-profile", null)
val type = object : TypeToken<Map<String?, Any?>?>() {}.type
return gson.fromJson<Map<String?, Any?>?>(profileJson, type)
?.get("PatientID")
.toString()
.toDoubleOrNull()
?.toInt()
}
fun handleEvent(
triggerGeofences: List<Geofence>,
location: Location,
transition: GeofenceTransition
) {
getPatientID()?.let { patientId ->
getActiveGeofences({ activeGeofences ->
triggerGeofences.forEach { geofence ->
// Extract PointID from 'geofence.requestId' and find from active geofences
val pointID =
activeGeofences.firstOrNull { it == geofence.requestId }?.split('_')
?.first()
if (!pointID.isNullOrEmpty() && pointID.toIntOrNull() != null) {
val body = mutableMapOf<String, Any?>(
"PointsID" to pointID.toIntOrNull(),
"GeoType" to transition.value,
"PatientID" to patientId
)
body.putAll(HMGUtils.defaultHTTPParams(context))
httpPost<Map<String, Any>>(API.LOG_GEOFENCE, body, { response ->
saveLog(
context,
"HMG_GEOFENCE_NOTIFY",
"Success: Notified to server\uD83D\uDE0E."
)
sendNotification(
context,
transition.named(),
geofence.requestId,
"Notified to server.😎"
)
}, { exception ->
val errorMessage = "${transition.named()}, ${geofence.requestId}"
saveLog(
context,
"HMG_GEOFENCE_NOTIFY",
"failed: $errorMessage | error: ${exception.localizedMessage}"
)
sendNotification(
context,
transition.named(),
geofence.requestId,
"Failed to notify server😔 -> ${exception.localizedMessage}"
)
})
}
}
}, null)
}
}
}

@ -0,0 +1,71 @@
package com.ejada.hmg.geofence.intent_receivers
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.ejada.hmg.geofence.GeofenceTransition
import com.ejada.hmg.geofence.HMG_Geofence
import com.ejada.hmg.utils.Logs
import com.google.android.gms.location.GeofenceStatusCodes
import com.google.android.gms.location.GeofencingEvent
class GeofenceBroadcastReceiver : BroadcastReceiver() {
private val LOG_TAG = "GeofenceBroadcastReceiver"
@SuppressLint("LongLogTag")
override fun onReceive(context: Context, intent: Intent) {
val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent != null) {
if (geofencingEvent.hasError()) {
val errorMessage =
GeofenceErrorMessages.getErrorString(context, geofencingEvent.errorCode)
Log.e(LOG_TAG, errorMessage)
Logs.GeofenceEvent.save(
context,
LOG_TAG,
"Error while triggering geofence event",
Logs.STATUS.ERROR
)
doReRegisterIfRequired(context, geofencingEvent.errorCode)
return
}
}
if (geofencingEvent != null) {
Logs.GeofenceEvent.save(
context,
LOG_TAG,
"Geofence event triggered: ${GeofenceTransition.fromInt(geofencingEvent.geofenceTransition).value} for ${geofencingEvent.triggeringGeofences?.map { it.requestId }}",
Logs.STATUS.SUCCESS
)
geofencingEvent.triggeringLocation?.let {
geofencingEvent.triggeringGeofences?.let { it1 ->
HMG_Geofence.shared(context).handleEvent(
it1,
it, GeofenceTransition.fromInt(geofencingEvent.geofenceTransition)
)
}
}
};
}
fun doReRegisterIfRequired(context: Context, errorCode: Int) {
val errorRequiredReregister = listOf(
GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE,
GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES,
GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS,
GeofenceStatusCodes.GEOFENCE_REQUEST_TOO_FREQUENT
)
if (errorRequiredReregister.contains(errorCode))
HMG_Geofence.shared(context).register() { status, error ->
}
}
}

@ -0,0 +1,16 @@
package com.ejada.hmg.geofence.intent_receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.ejada.hmg.geofence.HMG_Geofence
import com.google.android.gms.location.GeofenceStatusCodes
class GeofenceBroadcastReceiverWithJobService : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
GeofenceTransitionsJobIntentService.enqueueWork(context, intent)
}
}

@ -0,0 +1,43 @@
package com.ejada.hmg.geofence.intent_receivers
import android.content.Context
import com.ejada.hmg.R
import com.ejada.hmg.geofence.HMG_Geofence
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.location.GeofenceStatusCodes
object GeofenceErrorMessages {
fun getErrorString(context: Context, e: Exception): String {
return if (e is ApiException) {
getErrorString(context, e.statusCode)
} else {
context.resources.getString(R.string.geofence_unknown_error)
}
}
fun getErrorString(context: Context, errorCode: Int): String {
val resources = context.resources
val errorMessage = when (errorCode) {
GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE ->
resources.getString(R.string.geofence_not_available)
GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES ->
resources.getString(R.string.geofence_too_many_geofences)
GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS ->
resources.getString(R.string.geofence_too_many_pending_intents)
GeofenceStatusCodes.GEOFENCE_INSUFFICIENT_LOCATION_PERMISSION ->
resources.getString(R.string.GEOFENCE_INSUFFICIENT_LOCATION_PERMISSION)
GeofenceStatusCodes.GEOFENCE_REQUEST_TOO_FREQUENT ->
resources.getString(R.string.GEOFENCE_REQUEST_TOO_FREQUENT)
else -> resources.getString(R.string.geofence_unknown_error)
}
return errorMessage
}
}

@ -0,0 +1,97 @@
/*
* Copyright (c) 2018 Razeware LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Notwithstanding the foregoing, you may not use, copy, modify, merge, publish,
* distribute, sublicense, create a derivative work, and/or sell copies of the
* Software in any work that is designed, intended, or marketed for pedagogical or
* instructional purposes related to programming, coding, application development,
* or information technology. Permission for such use, copying, modification,
* merger, publication, distribution, sublicensing, creation of derivative works,
* or sale is expressly withheld.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ejada.hmg.geofence.intent_receivers
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.core.app.JobIntentService
import com.ejada.hmg.geofence.GeofenceTransition
import com.ejada.hmg.geofence.HMG_Geofence
import com.ejada.hmg.utils.saveLog
import com.google.android.gms.location.GeofenceStatusCodes
import com.google.android.gms.location.GeofencingEvent
class GeofenceTransitionsJobIntentService : JobIntentService() {
companion object {
private const val LOG_TAG = "GeoTrIntentService"
private const val JOB_ID = 95902
var context_: Context? = null
fun enqueueWork(context: Context, intent: Intent) {
context_ = context
enqueueWork(
context,
GeofenceTransitionsJobIntentService::class.java, JOB_ID,
intent)
}
}
override fun onHandleWork(intent: Intent) {
val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent != null) {
if (geofencingEvent.hasError()) {
val errorMessage = GeofenceErrorMessages.getErrorString(context_!!, geofencingEvent.errorCode)
Log.e(LOG_TAG, errorMessage)
saveLog(context_!!,LOG_TAG,errorMessage)
doReRegisterIfRequired(context_!!, geofencingEvent.errorCode)
return
}
}
if (geofencingEvent != null) {
geofencingEvent.triggeringGeofences?.let { geofencingEvent.triggeringLocation?.let { it1 ->
HMG_Geofence.shared(context_!!).handleEvent(it,
it1, GeofenceTransition.fromInt(geofencingEvent.geofenceTransition))
} }
};
}
fun doReRegisterIfRequired(context: Context, errorCode: Int){
val errorRequiredReregister = listOf(
GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE,
GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES,
GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS,
GeofenceStatusCodes.GEOFENCE_REQUEST_TOO_FREQUENT
)
if(errorRequiredReregister.contains(errorCode))
HMG_Geofence.shared(context).register(){ status, exc -> }
}
}

@ -0,0 +1,23 @@
package com.ejada.hmg.geofence.intent_receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.ejada.hmg.geofence.HMG_Geofence
import com.ejada.hmg.utils.PREFS_STORAGE
class GeofencingRebootBroadcastReceiver : BroadcastReceiver(){
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.action)) {
// if (intent.action.equals("android.intent.action.BOOT_COMPLETE")) {
val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
pref.edit().putString("REBOOT_DETECTED","YES").apply()
HMG_Geofence.shared(context).register(){ status, error -> }
}
}
}

@ -0,0 +1,25 @@
package com.ejada.hmg.geofence.intent_receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.location.LocationManager
import com.ejada.hmg.geofence.HMG_Geofence
import com.ejada.hmg.utils.HMGUtils
import com.ejada.hmg.utils.PREFS_STORAGE
class LocationProviderChangeReceiver : BroadcastReceiver() {
private val LOG_TAG = "LocationProviderChangeReceiver"
override fun onReceive(context: Context, intent: Intent) {
if (LocationManager.PROVIDERS_CHANGED_ACTION.equals(intent.action)) {
val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
pref.edit().putString("LOCATION_PROVIDER_CHANGE","YES").apply()
HMG_Geofence.shared(context).register(){ s, e -> }
}
}
}

@ -0,0 +1,24 @@
package com.ejada.hmg.geofence.intent_receivers
import android.app.job.JobParameters
import android.app.job.JobService
import com.ejada.hmg.geofence.HMG_Geofence
import com.ejada.hmg.utils.Logs
class ReregisterGeofenceJobService : JobService(){
companion object{
val TriggerIntervalDuration:String = "06:00:00"
val JobID = 918273
}
override fun onStartJob(params: JobParameters?): Boolean {
Logs.save(applicationContext,"ReregisterGeofenceJobService.onStartJob", "triggered to re-register the geofences after $TriggerIntervalDuration >> [HH:mm:ss]")
HMG_Geofence.shared(applicationContext).register(){ status, error ->
jobFinished(params, true)
}
return true
}
override fun onStopJob(params: JobParameters?): Boolean {
return true
}
}

@ -0,0 +1,257 @@
package com.ejada.hmg.hmgwifi
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.*
import android.net.wifi.*
import android.os.Build
import android.os.PatternMatcher
import android.provider.Settings
import android.util.Log
import androidx.annotation.RequiresApi
import com.ejada.hmg.MainActivity
import com.ejada.hmg.utils.HMGUtils
class HMG_Guest(private var context: MainActivity, ssid: String) {
private val TAG = "HMG_Guest"
private val TEST = false
private var SSID = ssid
// private var SSID = "HMG-MOHEMM"
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager?
private lateinit var completionListener: ((status: Boolean, message: String) -> Unit)
fun completionOnUiThread(status: Boolean, message: String){
completionListener(status, message)
}
fun enableWifi(){
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
wifiManager?.setWifiEnabled(true)
HMGUtils.popFlutterText(context,"enablingWifi");
HMGUtils.timer(2000,false){
connectApiLessThen29()
}
}else {
val panelIntent = Intent(Settings.Panel.ACTION_WIFI)
context.startActivityForResult(panelIntent, 1)
}
}
/*
* Helpful:
* http://stackoverflow.com/questions/8818290/how-to-connect-to-a-specific-wifi-network-in-android-programmatically
*/
fun connectToHMGGuestNetwork(completion: (status: Boolean, message: String) -> Unit) {
completionListener = completion
wifiManager?.let { wm ->
if (!wm.isWifiEnabled){
enableWifi()
}else{
connectWifi()
}
}
}
private fun errorConnecting(){
completionOnUiThread(false, "errorConnectingHmgNetwork")
}
fun connectWifi(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
connectApiGreaterThen28()
}else {
connectApiLessThen29()
}
}
// I }else{f CompileSDK is greater and equals to APILevel 29
@RequiresApi(Build.VERSION_CODES.Q)
private fun connectApiGreaterThen28(){
Log.e(TAG, "connection wifi with Android Q+")
val networkRequest: NetworkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) //removeCapability added for hotspots without internet
.setNetworkSpecifier(
WifiNetworkSpecifier.Builder()
.setSsid(SSID)
.build()
).build()
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
connectivityManager.bindProcessToNetwork(network)
HMGUtils.timer(2000,false){
completionListener(true, "Success")
}
Log.e(TAG, "onAvailable")
}
override fun onLosing(network: Network, maxMsToLive: Int) {
super.onLosing(network, maxMsToLive)
Log.e(TAG, "onLosing")
completionListener(false, "fail")
}
override fun onLost(network: Network) {
super.onLost(network)
Log.e(TAG, "onLosing")
Log.e(TAG, "losing active connection")
completionListener(false, "fail")
}
override fun onUnavailable() {
super.onUnavailable()
Log.e(TAG, "onUnavailable")
completionListener(false, "fail")
}
}
//timeout add because "No devices found" wasn't handled correct and doesn't throw Unavailable
connectivityManager.requestNetwork(networkRequest, networkCallback, 30000)
}
fun connectApiLessThen29(){
val wifi = WifiConfiguration()
wifi.SSID = """"$SSID""""
wifi.status = WifiConfiguration.Status.ENABLED
wifi.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
wifi.networkId = ssidToNetworkId(wifi.SSID)
if (wifi.networkId == -1) {
wifiManager?.addNetwork(wifi)
} else {
Log.v(TAG, "WiFi found - updating it.\n")
wifiManager?.updateNetwork(wifi)
}
Log.v(TAG, "saving config.\n")
wifiManager?.saveConfiguration()
wifi.networkId = ssidToNetworkId(wifi.SSID)
Log.v(TAG, "wifi ID in device = " + wifi.networkId)
var supState: SupplicantState
val networkIdToConnect = wifi.networkId
if (networkIdToConnect >= 0) {
Log.v(TAG, "Start connecting...\n")
// We disable the network before connecting, because if this was the last connection before
// a disconnect(), this will not reconnect.
wifiManager?.disableNetwork(networkIdToConnect)
wifiManager?.enableNetwork(networkIdToConnect, true)
val wifiInfo: WifiInfo = wifiManager!!.connectionInfo
HMGUtils.timer(5000,false){
supState = wifiInfo.supplicantState
Log.i(TAG, "Done connect to network : status = $supState")
val successStates = listOf(SupplicantState.COMPLETED, SupplicantState.ASSOCIATED)
if (successStates.contains(supState))
completionListener(true,"Connected to internet Wifi")
else
completionListener(false,"errorConnectingHmgNetwork")
}
} else {
Log.v(TAG, "WifiWizard: cannot connect to network")
completionListener(false,"errorConnectingHmgNetwork")
}
// val wifi = WifiConfiguration()
// wifi.SSID = SSID
// wifi.status = WifiConfiguration.Status.ENABLED
// wifi.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
//
// wifi.networkId = ssidToNetworkId(SSID)
//
// // Set network to highest priority (deprecated in API >= 26)
// if(Build.VERSION.SDK_INT < 26) {
// wifi.priority = getMaxWifiPriority(wifiManager!!) + 1;
// }
//
// // After processing authentication types, add or update network
// if(wifi.networkId == -1) { // -1 means SSID configuration does not exist yet
//
// val newNetId = wifiManager?.addNetwork(wifi)!!
// if( newNetId > -1 ){
// completionListener(true,"Success")
// } else {
// completionListener(false, "ERROR_ADDING_NETWORK" )
// }
//
// } else {
//
// var updatedNetID = wifiManager?.updateNetwork(wifi)
//
// if(updatedNetID == -1)
// updatedNetID = wifiManager?.addNetwork(wifi)
//
// if(updatedNetID > -1) {
// callbackContext.success( updatedNetID )
// } else {
// callbackContext.error("ERROR_UPDATING_NETWORK")
// }
//
// }
//
// // WifiManager configurations are presistent for API 26+
// if(Build.VERSION.SDK_INT < 26) {
// wifiManager?.saveConfiguration(); // Call saveConfiguration for older < 26 API
// }
}
/**
* This method takes a given String, searches the current list of configured WiFi
* networks, and returns the networkId for the network if the SSID matches. If not,
* it returns -1.
*/
@SuppressLint("MissingPermission")
private fun ssidToNetworkId(ssid: String): Int {
val currentNetworks = wifiManager!!.configuredNetworks
var networkId = -1
// For each network in the list, compare the SSID with the given one
for (test in currentNetworks) {
if (test.SSID == ssid) {
networkId = test.networkId
break
}
}
return networkId
}
companion object{
/**
* Figure out what the highest priority network in the network list is and return that priority
*/
@RequiresApi(Build.VERSION_CODES.S)
fun getMaxWifiPriority(wifiManager:WifiManager) : Int {
val configurations = wifiManager.callerConfiguredNetworks
var maxPriority = 0
configurations.forEach {
if (it.priority > maxPriority) {
maxPriority = it.priority;
}
}
return maxPriority;
}
}
}

@ -0,0 +1,120 @@
package com.ejada.hmg.hmgwifi
import android.annotation.SuppressLint
import com.ejada.hmg.utils.API
import com.ejada.hmg.MainActivity
import com.github.kittinunf.fuel.core.extensions.jsonBody
import com.github.kittinunf.fuel.httpGet
import com.github.kittinunf.fuel.httpPost
import org.json.JSONObject
import java.util.*
@SuppressLint("MissingPermission")
class HMG_Internet(flutterMainActivity: MainActivity) {
private val TAG = "HMG_Wifi"
private val TEST = true
private var context = flutterMainActivity;
private lateinit var completionListener: ((status: Boolean, message: String) -> Unit)
private var SSID = "GUEST-POC"
fun completionOnUiThread(status: Boolean, message: String){
completionListener(status, message)
// context.runOnUiThread {
// .with(message){localized ->
// completionListener(status, localized)
// }
// }
}
/*
* Helpful:
* http://stackoverflow.com/questions/8818290/how-to-connect-to-a-specific-wifi-network-in-android-programmatically
*/
fun connectToHMGGuestNetwork(username: String, password: String, completion: (status: Boolean, message: String) -> Unit): HMG_Internet {
completionListener = completion
WpaEnterprise(context,SSID).connect(username,username) { status, message ->
completionOnUiThread(status,message)
}
return this
}
private fun haveInternet(completion: ((status: Boolean) -> Unit)){
if (TEST)
completion(true)
"https://captive.apple.com".httpGet().response { request, response, result ->
result.fold(success = {
val html = String(it).lowercase(Locale.ENGLISH)
.replace(" ", "", ignoreCase = true)
.replace("\n","",ignoreCase = true)
val have = html.contains("<title>success</title>", ignoreCase = true)
completion(have)
},failure = {
completion(false)
})
}
}
private fun getWifiCredentials(patientId:String, success: ((String?,String?) -> Unit)){
if (TEST){
SSID = "GUEST-POC"
success("2300", "0000")
return
}
val jsonBody = """{
"PatientID":$patientId
"VersionID": 8.8,
"Channel": 3,
"LanguageID": 2,
"IPAdress": "10.20.10.20",
"generalid": "Cs2020@2016$2958",
"PatientOutSA": 0,
"SessionID": "@admin",
"isDentalAllowedBackend": false,
"DeviceTypeID": 2,
"TokenID": "@admin",
"PatientTypeID": 1,
"PatientType": 1
}""".trimMargin()
API.WIFI_CREDENTIALS.
httpPost()
.jsonBody(jsonBody, Charsets.UTF_8)
.response { request, response, result ->
result.fold(success = { data ->
val jsonString = String(data)
val jsonObject = JSONObject(jsonString)
if(!jsonObject.getString("ErrorMessage").equals("null")){
val errorMsg = jsonObject.getString("ErrorMessage")
completionOnUiThread(false, errorMsg)
}else{
jsonObject.getJSONArray("Hmg_SMS_Get_By_ProjectID_And_PatientIDList").let { array ->
array.getJSONObject(0).let { object_ ->
if (object_.has("UserName") && object_.has("UserName")){
try {
val userName = object_.getString("UserName")
val password = object_.getString("Password")
success(userName, password)
}catch (e:Exception){
success(null, null)
}
}else{
completionOnUiThread(false, "somethingWentWrong")
}
}
}
}
},failure = { error ->
completionOnUiThread(false, "somethingWentWrong" )
})
}
}
}

@ -0,0 +1,105 @@
package com.ejada.hmg.hmgwifi
import android.annotation.SuppressLint
import android.content.Context
import android.net.ConnectivityManager
import android.net.wifi.*
import android.net.wifi.SupplicantState.ASSOCIATED
import android.net.wifi.SupplicantState.COMPLETED
import android.util.Log
import com.ejada.hmg.MainActivity
import com.ejada.hmg.utils.HMGUtils
class WPA(mainActivity: MainActivity, SSID:String) {
private var TAG = "WPA"
private var SSID = "GUEST-POC"
private var wifiManager_: WifiManager? = null
private var connectivityManager_: ConnectivityManager? = null
init {
wifiManager_ = mainActivity.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager?
connectivityManager_ = mainActivity.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
}
fun connect(identity:String, password:String, completion: (status: Boolean, message: String) -> Unit) {
if(wifiManager_ == null || connectivityManager_ == null){
completion(false,"errorConnectingHmgNetwork")
return
}
val wifiManager = wifiManager_!!
val connectivityManager = connectivityManager_!!
// Initialize the WifiConfiguration object
val enterpriseConfig = WifiEnterpriseConfig()
val wifi = WifiConfiguration()
wifi.SSID = """"$SSID""""
wifi.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP)
wifi.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X)
enterpriseConfig.eapMethod = WifiEnterpriseConfig.Eap.PEAP
enterpriseConfig.identity = identity
enterpriseConfig.password = password
wifi.enterpriseConfig = enterpriseConfig
wifi.networkId = ssidToNetworkId(wifi.SSID)
if (wifi.networkId == -1) {
wifiManager.addNetwork(wifi)
} else {
Log.v(TAG, "WiFi found - updating it.\n")
wifiManager.updateNetwork(wifi)
}
Log.v(TAG, "saving config.\n")
wifiManager.saveConfiguration()
wifi.networkId = ssidToNetworkId(wifi.SSID)
Log.v(TAG, "wifi ID in device = " + wifi.networkId)
var supState: SupplicantState
val networkIdToConnect = wifi.networkId
if (networkIdToConnect >= 0) {
Log.v(TAG, "Start connecting...\n")
// We disable the network before connecting, because if this was the last connection before
// a disconnect(), this will not reconnect.
wifiManager.disableNetwork(networkIdToConnect)
wifiManager.enableNetwork(networkIdToConnect, true)
val wifiInfo: WifiInfo = wifiManager.connectionInfo
HMGUtils.timer(5000,false){
supState = wifiInfo.supplicantState
Log.i(TAG, "WifiWizard: Done connect to network : status = $supState")
val successStates = listOf(COMPLETED, ASSOCIATED)
if (successStates.contains(COMPLETED /*supState*/))
completion(true,"Connected to internet Wifi")
else
completion(false,"errorConnectingHmgNetwork")
}
} else {
Log.v(TAG, "WifiWizard: cannot connect to network")
completion(false,"errorConnectingHmgNetwork")
}
}
/**
* This method takes a given String, searches the current list of configured WiFi
* networks, and returns the networkId for the network if the SSID matches. If not,
* it returns -1.
*/
@SuppressLint("MissingPermission")
private fun ssidToNetworkId(ssid: String): Int {
val currentNetworks = wifiManager_!!.configuredNetworks
var networkId = -1
// For each network in the list, compare the SSID with the given one
for (test in currentNetworks) {
if (test.SSID == ssid) {
networkId = test.networkId
break
}
}
return networkId
}
}

@ -0,0 +1,166 @@
package com.ejada.hmg.hmgwifi
import android.annotation.SuppressLint
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.net.wifi.*
import android.net.wifi.SupplicantState.ASSOCIATED
import android.net.wifi.SupplicantState.COMPLETED
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import com.ejada.hmg.MainActivity
import com.ejada.hmg.utils.HMGUtils
import java.security.cert.X509Certificate
class WpaEnterprise(private val mainActivity: MainActivity, private var SSID: String) {
private var TAG = "WpaEnterprise"
private lateinit var identity:String
private lateinit var password:String
private lateinit var completion:((status: Boolean, message: String) -> Unit)
fun connect(identity:String, password:String, completion: (status: Boolean, message: String) -> Unit) {
this.password = password
this.identity = identity
this.completion = completion
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
apiGreaterThen28()
}else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
apiLessThen29()
}
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun apiLessThen29(){
val wifiManager = mainActivity.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifi = WifiConfiguration()
wifi.SSID = """"$SSID""""
wifi.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP)
wifi.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X)
wifi.enterpriseConfig = enterpriseConfig()
wifi.networkId = ssidToNetworkId(wifi.SSID, wifiManager)
if (wifi.networkId == -1) {
wifiManager.addNetwork(wifi)
} else {
Log.v(TAG, "WiFi found - updating it.\n")
wifiManager.updateNetwork(wifi)
}
Log.v(TAG, "saving config.\n")
wifiManager.saveConfiguration()
wifi.networkId = ssidToNetworkId(wifi.SSID, wifiManager)
Log.v(TAG, "wifi ID in device = " + wifi.networkId)
var supState: SupplicantState
val networkIdToConnect = wifi.networkId
if (networkIdToConnect >= 0) {
Log.v(TAG, "Start connecting...\n")
// We disable the network before connecting, because if this was the last connection before
// a disconnect(), this will not reconnect.
wifiManager.disableNetwork(networkIdToConnect)
wifiManager.enableNetwork(networkIdToConnect, true)
val wifiInfo: WifiInfo = wifiManager.connectionInfo
HMGUtils.timer(5000,false){
supState = wifiInfo.supplicantState
Log.i(TAG, "Done connect to network : status = $supState")
val successStates = listOf(COMPLETED, ASSOCIATED)
if (successStates.contains(supState))
completion(true,"Connected to internet Wifi")
else
completion(false,"errorConnectingHmgNetwork")
}
} else {
Log.v(TAG, "WifiWizard: cannot connect to network")
completion(false,"errorConnectingHmgNetwork")
}
}
/**
* This method takes a given String, searches the current list of configured WiFi
* networks, and returns the networkId for the network if the SSID matches. If not,
* it returns -1.
*/
@SuppressLint("MissingPermission")
private fun ssidToNetworkId(ssid: String, wifiManager: WifiManager): Int {
val currentNetworks = wifiManager.configuredNetworks
var networkId = -1
// For each network in the list, compare the SSID with the given one
for (test in currentNetworks) {
if (test.SSID == ssid) {
networkId = test.networkId
break
}
}
return networkId
}
@RequiresApi(Build.VERSION_CODES.Q)
fun apiGreaterThen28(){
val connectivityManager = mainActivity.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
Log.e(TAG, "connection wifi with Android Q+")
val wifiNetworkSpecifier: WifiNetworkSpecifier = WifiNetworkSpecifier.Builder()
.setSsid(SSID)
.setWpa2EnterpriseConfig(enterpriseConfig())
.build()
val networkRequest: NetworkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setNetworkSpecifier(wifiNetworkSpecifier)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) //removeCapability added for hotspots without internet
.build()
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
connectivityManager.bindProcessToNetwork(network)
completion(true, "200")
Log.e(TAG, "onAvailable")
}
override fun onLosing(network: Network, maxMsToLive: Int) {
super.onLosing(network, maxMsToLive)
Log.e(TAG, "onLosing")
}
override fun onLost(network: Network) {
super.onLost(network)
Log.e(TAG, "onLosing")
Log.e(TAG, "losing active connection")
}
override fun onUnavailable() {
super.onUnavailable()
completion(false, "401")
Log.e(TAG, "onUnavailable")
}
}
//timeout add because "No devices found" wasn't handled correct and doesn't throw Unavailable
connectivityManager.requestNetwork(networkRequest, networkCallback, 30000)
}
fun enterpriseConfig() : WifiEnterpriseConfig{
// Initialize the WifiConfiguration object
val enterpriseConfig = WifiEnterpriseConfig()
enterpriseConfig.eapMethod = WifiEnterpriseConfig.Eap.PEAP
enterpriseConfig.identity = identity
enterpriseConfig.password = password
enterpriseConfig.phase2Method = WifiEnterpriseConfig.Phase2.NONE
// enterpriseConfig.caCertificates = WifiEnterpriseConfig.Phase2.
return enterpriseConfig;
}
}

@ -0,0 +1,58 @@
package com.ejada.hmg.opentok
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import android.widget.LinearLayout
import com.ejada.hmg.R
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
class LocalVideoFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
companion object {
private lateinit var view: LocalVideoPlatformView
fun getViewInstance(context: Context): LocalVideoPlatformView {
if(!this::view.isInitialized) {
view = LocalVideoPlatformView(context)
}
return view
}
}
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
return getViewInstance(context)
}
}
class LocalVideoPlatformView(context: Context) : PlatformView {
private val videoContainer: LocalVideoContainer = LocalVideoContainer(context)
val container get() = videoContainer.publisherContainer
override fun getView(): View {
return videoContainer
}
override fun dispose() {}
}
class LocalVideoContainer @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0,
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyle, defStyleRes) {
var publisherContainer: FrameLayout private set
init {
val view = LayoutInflater.from(context).inflate(R.layout.local_video, this, true)
publisherContainer = view.findViewById(R.id.publisher_container)
}
}

@ -0,0 +1,181 @@
package com.ejada.hmg.opentok
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.ViewGroup
import com.facebook.stetho.urlconnection.StethoURLConnectionManager
import com.opentok.android.*
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
enum class OpenTokSDKState {
LOGGED_OUT,
LOGGED_IN,
WAIT,
ERROR
}
class OpenTok(private var context: Context, private var flutterEngine: FlutterEngine){
private lateinit var remoteVideoPlatformView: RemoteVideoPlatformView
private lateinit var localVideoPlatformView: LocalVideoPlatformView
init {
remoteVideoPlatformView = RemoteVideoFactory.getViewInstance(context)
flutterEngine
.platformViewsController
.registry
.registerViewFactory("remote-video-container", RemoteVideoFactory())
localVideoPlatformView = LocalVideoFactory.getViewInstance(context)
flutterEngine
.platformViewsController
.registry
.registerViewFactory("local-video-container", LocalVideoFactory())
}
private var session: Session? = null
private var publisher: Publisher? = null
private var subscriber: Subscriber? = null
private val sessionListener: Session.SessionListener = object: Session.SessionListener {
override fun onConnected(session: Session) {
// Connected to session
Log.d("MainActivity", "Connected to session ${session.sessionId}")
publisher = Publisher.Builder(context).build().apply {
setPublisherListener(publisherListener)
renderer?.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL)
view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
localVideoPlatformView.container.addView(view)
}
notifyFlutter(OpenTokSDKState.LOGGED_IN)
session.publish(publisher)
}
override fun onDisconnected(session: Session) {
notifyFlutter(OpenTokSDKState.LOGGED_OUT)
}
override fun onStreamReceived(session: Session, stream: Stream) {
Log.d(
"MainActivity",
"onStreamReceived: New Stream Received " + stream.streamId + " in session: " + session.sessionId
)
if (subscriber == null) {
subscriber = Subscriber.Builder(context, stream).build().apply {
renderer?.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL)
setSubscriberListener(subscriberListener)
session.subscribe(this)
remoteVideoPlatformView.container.addView(view)
}
}
}
override fun onStreamDropped(session: Session, stream: Stream) {
Log.d(
"MainActivity",
"onStreamDropped: Stream Dropped: " + stream.streamId + " in session: " + session.sessionId
)
if (subscriber != null) {
subscriber = null
remoteVideoPlatformView.container.removeAllViews()
}
}
override fun onError(session: Session, opentokError: OpentokError) {
Log.d("MainActivity", "Session error: " + opentokError.message)
notifyFlutter(OpenTokSDKState.ERROR)
}
}
private val publisherListener: PublisherKit.PublisherListener = object : PublisherKit.PublisherListener {
override fun onStreamCreated(publisherKit: PublisherKit, stream: Stream) {
Log.d("MainActivity", "onStreamCreated: Publisher Stream Created. Own stream " + stream.streamId)
}
override fun onStreamDestroyed(publisherKit: PublisherKit, stream: Stream) {
Log.d("MainActivity", "onStreamDestroyed: Publisher Stream Destroyed. Own stream " + stream.streamId)
}
override fun onError(publisherKit: PublisherKit, opentokError: OpentokError) {
Log.d("MainActivity", "PublisherKit onError: " + opentokError.message)
notifyFlutter(OpenTokSDKState.ERROR)
}
}
var subscriberListener: SubscriberKit.SubscriberListener = object : SubscriberKit.SubscriberListener {
override fun onConnected(subscriberKit: SubscriberKit) {
Log.d("MainActivity", "onConnected: Subscriber connected. Stream: " + subscriberKit.stream.streamId)
}
override fun onDisconnected(subscriberKit: SubscriberKit) {
Log.d("MainActivity", "onDisconnected: Subscriber disconnected. Stream: " + subscriberKit.stream.streamId)
notifyFlutter(OpenTokSDKState.LOGGED_OUT)
}
override fun onError(subscriberKit: SubscriberKit, opentokError: OpentokError) {
Log.d("MainActivity", "SubscriberKit onError: " + opentokError.message)
notifyFlutter(OpenTokSDKState.ERROR)
}
}
fun initSession(call: MethodCall, result: MethodChannel.Result) {
val apiKey = requireNotNull(call.argument<String>("apiKey"))
val sessionId = requireNotNull(call.argument<String>("sessionId"))
val token = requireNotNull(call.argument<String>("token"))
notifyFlutter(OpenTokSDKState.WAIT)
session = Session.Builder(context, apiKey, sessionId).build()
session?.setSessionListener(sessionListener)
session?.connect(token)
result.success("")
}
fun swapCamera(call: MethodCall, result: MethodChannel.Result) {
publisher?.cycleCamera()
result.success(true)
}
fun toggleAudio(call: MethodCall, result: MethodChannel.Result) {
if (publisher != null) {
publisher?.publishAudio = !(publisher!!.publishAudio)
result.success(true)
}else{
result.success(false)
}
}
fun toggleVideo(call: MethodCall, result: MethodChannel.Result) {
if (publisher != null) {
publisher?.publishVideo = !(publisher!!.publishVideo)
result.success(true)
}else{
result.success(false)
}
}
fun hangupCall(call: MethodCall, result: MethodChannel.Result) {
session?.disconnect()
result.success(true)
}
private fun notifyFlutter(state: OpenTokSDKState) {
Handler(Looper.getMainLooper()).post {
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "OpenTok-Platform-Bridge")
.invokeMethod("updateState", state.toString())
}
}
}

@ -0,0 +1,58 @@
package com.ejada.hmg.opentok
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import android.widget.LinearLayout
import com.ejada.hmg.R
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
class RemoteVideoFactory : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
companion object {
private lateinit var view: RemoteVideoPlatformView
fun getViewInstance(context: Context): RemoteVideoPlatformView {
if(!this::view.isInitialized) {
view = RemoteVideoPlatformView(context)
}
return view
}
}
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
return getViewInstance(context)
}
}
class RemoteVideoPlatformView(context: Context) : PlatformView {
private val videoContainer: RemoteVideoContainer = RemoteVideoContainer(context)
val container get() = videoContainer.subscriberContainer
override fun getView(): View {
return videoContainer
}
override fun dispose() {}
}
class RemoteVideoContainer @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0,
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyle, defStyleRes) {
var subscriberContainer: FrameLayout private set
init {
val view = LayoutInflater.from(context).inflate(R.layout.remote_video, this, true)
subscriberContainer = view.findViewById(R.id.subscriber_container)
}
}

@ -0,0 +1,13 @@
package com.cloud.hmg_patient_app.penguin
enum class PenguinMethod {
// initializePenguin("initializePenguin"),
// configurePenguin("configurePenguin"),
// showPenguinUI("showPenguinUI"),
// onPenNavUIDismiss("onPenNavUIDismiss"),
// onReportIssue("onReportIssue"),
// onPenNavSuccess("onPenNavSuccess"),
onPenNavInitializationError // onLocationOffCampus("onLocationOffCampus"),
// navigateToPOI("navigateToPOI"),
// openSharedLocation("openSharedLocation");
}

@ -0,0 +1,97 @@
package com.cloud.hmg_patient_app.penguin
import android.content.Context
import com.google.gson.Gson
import com.peng.pennavmap.PlugAndPlaySDK
import com.peng.pennavmap.connections.ApiController
import com.peng.pennavmap.interfaces.RefIdDelegate
import com.peng.pennavmap.models.TokenModel
import com.peng.pennavmap.models.postmodels.PostToken
import com.peng.pennavmap.utils.AppSharedData
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import android.util.Log
class PenguinNavigator() {
fun navigateTo(mContext: Context, refID: String, delegate: RefIdDelegate,clientID : String,clientKey : String ) {
val postToken = PostToken(clientID, clientKey)
getToken(mContext, postToken, object : RefIdDelegate {
override fun onRefByIDSuccess(PoiId: String?) {
Log.e("navigateTo", "PoiId is+++++++ $PoiId")
PlugAndPlaySDK.navigateTo(mContext, refID, object : RefIdDelegate {
override fun onRefByIDSuccess(PoiId: String?) {
Log.e("navigateTo", "PoiId 2is+++++++ $PoiId")
delegate.onRefByIDSuccess(refID)
}
override fun onGetByRefIDError(error: String?) {
delegate.onRefByIDSuccess(error)
}
})
}
override fun onGetByRefIDError(error: String?) {
delegate.onRefByIDSuccess(error)
}
})
}
fun getToken(mContext: Context, postToken: PostToken?, apiTokenCallBack: RefIdDelegate) {
try {
// Create the API call
val purposesCall: Call<ResponseBody> = ApiController.getInstance(mContext)
.apiMethods
.getToken(postToken)
// Enqueue the call for asynchronous execution
purposesCall.enqueue(object : Callback<ResponseBody?> {
override fun onResponse(
call: Call<ResponseBody?>,
response: Response<ResponseBody?>
) {
if (response.isSuccessful() && response.body() != null) {
try {
response.body()?.use { responseBody ->
val responseBodyString: String = responseBody.string() // Use `string()` to get the actual response content
if (responseBodyString.isNotEmpty()) {
val tokenModel = Gson().fromJson(responseBodyString, TokenModel::class.java)
if (tokenModel != null && tokenModel.token != null) {
AppSharedData.apiToken = tokenModel.token
apiTokenCallBack.onRefByIDSuccess(tokenModel.token)
} else {
apiTokenCallBack.onGetByRefIDError("Failed to parse token model")
}
} else {
apiTokenCallBack.onGetByRefIDError("Response body is empty")
}
}
} catch (e: Exception) {
apiTokenCallBack.onGetByRefIDError("An error occurred: ${e.message}")
}
} else {
apiTokenCallBack.onGetByRefIDError("Unsuccessful response: " + response.code())
}
}
override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
apiTokenCallBack.onGetByRefIDError(t.message)
}
})
} catch (error: Exception) {
apiTokenCallBack.onGetByRefIDError("Exception during API call: $error")
}
}
}

@ -0,0 +1,320 @@
package com.cloud.hmg_patient_app.penguin
import android.app.Activity
import android.content.Context
import android.content.Context.RECEIVER_EXPORTED
import android.content.IntentFilter
import android.graphics.Color
import android.os.Build
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
import android.widget.Toast
import androidx.annotation.RequiresApi
import com.cloud.hmg_patient_app.PermissionManager.PermissionHelper
import com.cloud.hmg_patient_app.PermissionManager.PermissionManager
import com.cloud.hmg_patient_app.PermissionManager.PermissionResultReceiver
import com.ejada.hmg.MainActivity
import com.peng.pennavmap.PlugAndPlayConfiguration
import com.peng.pennavmap.PlugAndPlaySDK
import com.peng.pennavmap.enums.InitializationErrorType
import com.peng.pennavmap.interfaces.PenNavUIDelegate
import com.peng.pennavmap.utils.Languages
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.platform.PlatformView
import com.cloud.hmg_patient_app.penguin.PenguinNavigator
import com.peng.pennavmap.interfaces.PIEventsDelegate
import com.peng.pennavmap.interfaces.PILocationDelegate
import com.peng.pennavmap.interfaces.RefIdDelegate
import com.peng.pennavmap.models.PIReportIssue
/**
* Custom PlatformView for displaying Penguin UI components within a Flutter app.
* Implements `PlatformView` for rendering the view, `MethodChannel.MethodCallHandler` for handling method calls,
* and `PenNavUIDelegate` for handling SDK events.
*/
@RequiresApi(Build.VERSION_CODES.O)
internal class PenguinView(
context: Context,
id: Int,
val creationParams: Map<String, Any>,
messenger: BinaryMessenger,
activity: MainActivity,
val channel: MethodChannel
) : PlatformView, MethodChannel.MethodCallHandler, PenNavUIDelegate {
// The layout for displaying the Penguin UI
private val mapLayout: RelativeLayout = RelativeLayout(context)
private val _context: Context = context
private val permissionResultReceiver: PermissionResultReceiver
private val permissionIntentFilter = IntentFilter("PERMISSION_RESULT_ACTION")
private companion object {
const val PERMISSIONS_REQUEST_CODE = 1
}
private lateinit var permissionManager: PermissionManager
// Reference to the main activity
private var _activity: Activity = activity
private lateinit var mContext: Context
lateinit var navigator: PenguinNavigator
init {
// Set layout parameters for the mapLayout
mapLayout.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
)
mContext = context
permissionResultReceiver = PermissionResultReceiver { granted ->
if (granted) {
onPermissionsGranted()
} else {
onPermissionsDenied()
}
}
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mContext.registerReceiver(
permissionResultReceiver,
permissionIntentFilter,
RECEIVER_EXPORTED
)
} else {
mContext.registerReceiver(
permissionResultReceiver,
permissionIntentFilter,
)
}
// Set the background color of the layout
mapLayout.setBackgroundColor(Color.RED)
permissionManager = PermissionManager(
context = mContext,
listener = object : PermissionManager.PermissionListener {
override fun onPermissionGranted() {
// Handle permissions granted
onPermissionsGranted()
}
override fun onPermissionDenied() {
// Handle permissions denied
onPermissionsDenied()
}
},
requestCode = PERMISSIONS_REQUEST_CODE,
PermissionHelper.getRequiredPermissions().get(0)
)
if (!permissionManager.arePermissionsGranted()) {
permissionManager.requestPermissions(_activity)
} else {
// Permissions already granted
permissionManager.listener.onPermissionGranted()
}
}
private fun onPermissionsGranted() {
// Handle the actions when permissions are granted
Log.d("PermissionsResult", "onPermissionsGranted")
// Register the platform view factory for creating custom views
// Initialize the Penguin SDK
initPenguin()
}
private fun onPermissionsDenied() {
// Handle the actions when permissions are denied
Log.d("PermissionsResult", "onPermissionsDenied")
}
/**
* Returns the view associated with this PlatformView.
*
* @return The main view for this PlatformView.
*/
override fun getView(): View {
return mapLayout
}
/**
* Cleans up resources associated with this PlatformView.
*/
override fun dispose() {
// Cleanup code if needed
}
/**
* Handles method calls from Dart code.
*
* @param call The method call from Dart.
* @param result The result callback to send responses back to Dart.
*/
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
// Handle method calls from Dart code here
}
/**
* Initializes the Penguin SDK with custom configuration and delegates.
*/
private fun initPenguin() {
navigator = PenguinNavigator()
// Configure the PlugAndPlaySDK
val language = when (creationParams["languageCode"] as String) {
"ar" -> Languages.ar
"en" -> Languages.en
else -> {
Languages.en
}
}
Log.d(
"TAG",
"initPenguin: ${Languages.getLanguageEnum(creationParams["languageCode"] as String)}"
)
PlugAndPlaySDK.configuration = PlugAndPlayConfiguration.Builder()
.setBaseUrl(
creationParams["dataURL"] as String,
creationParams["positionURL"] as String
)
.setServiceName(
creationParams["dataServiceName"] as String,
creationParams["positionServiceName"] as String
)
.setClientData(
creationParams["clientID"] as String,
creationParams["clientKey"] as String
)
.setUserName(creationParams["username"] as String)
// .setLanguageID(Languages.en)
.setLanguageID(language)
.setSimulationModeEnabled(creationParams["isSimulationModeEnabled"] as Boolean)
.setEnableBackButton(true)
// .setDeepLinkData("deeplink")
.setCustomizeColor("#2CA0AF")
.setDeepLinkSchema("")
.setIsEnableReportIssue(true)
.build()
// Set location delegate to handle location updates
// PlugAndPlaySDK.setPiLocationDelegate {
// Example code to handle location updates
// Uncomment and modify as needed
// if (location.size() > 0)
// Toast.makeText(_context, "Location Info Latitude: ${location[0]}, Longitude: ${location[1]}", Toast.LENGTH_SHORT).show()
// }
// Set events delegate for reporting issues
// PlugAndPlaySDK.setPiEventsDelegate(new PIEventsDelegate() {
// @Override
// public void onReportIssue(PIReportIssue issue) {
// Log.e("Issue Reported: ", issue.getReportType());
// }
// // Implement issue reporting logic here }
// @Override
// public void onSharedLocation(String link) {
// // Implement Shared location logic here
// }
// })
// Start the Penguin SDK
PlugAndPlaySDK.start(mContext, this)
}
/**
* Navigates to the specified reference ID.
*
* @param refID The reference ID to navigate to.
*/
fun navigateTo(refID: String) {
try {
if (refID.isBlank()) {
Log.e("navigateTo", "Invalid refID: The reference ID is blank.")
}
// referenceId = refID
navigator.navigateTo(mContext, refID,object : RefIdDelegate {
override fun onRefByIDSuccess(PoiId: String?) {
Log.e("navigateTo", "PoiId is penguin view+++++++ $PoiId")
// channelFlutter.invokeMethod(
// PenguinMethod.navigateToPOI.name,
// "navigateTo Success"
// )
}
override fun onGetByRefIDError(error: String?) {
Log.e("navigateTo", "error is penguin view+++++++ $error")
// channelFlutter.invokeMethod(
// PenguinMethod.navigateToPOI.name,
// "navigateTo Failed: Invalid refID"
// )
}
} , creationParams["clientID"] as String, creationParams["clientKey"] as String )
} catch (e: Exception) {
Log.e("navigateTo", "Exception occurred during navigation: ${e.message}", e)
// channelFlutter.invokeMethod(
// PenguinMethod.navigateToPOI.name,
// "Failed: Exception - ${e.message}"
// )
}
}
/**
* Called when Penguin UI setup is successful.
*
* @param warningCode Optional warning code received from the SDK.
*/
override fun onPenNavSuccess(warningCode: String?) {
val clinicId = creationParams["clinicID"] as String
if(clinicId.isEmpty()) return
navigateTo(clinicId)
}
/**
* Called when there is an initialization error with Penguin UI.
*
* @param description Description of the error.
* @param errorType Type of initialization error.
*/
override fun onPenNavInitializationError(
description: String?,
errorType: InitializationErrorType?
) {
val arguments: Map<String, Any?> = mapOf(
"description" to description,
"type" to errorType?.name
)
channel.invokeMethod(PenguinMethod.onPenNavInitializationError.name, arguments)
Toast.makeText(mContext, "Navigation Error: $description", Toast.LENGTH_SHORT).show()
}
/**
* Called when Penguin UI is dismissed.
*/
override fun onPenNavUIDismiss() {
// Handle UI dismissal if needed
try {
mContext.unregisterReceiver(permissionResultReceiver)
dispose();
} catch (e: IllegalArgumentException) {
Log.e("PenguinView", "Receiver not registered: $e")
}
}
}

@ -0,0 +1,11 @@
package com.ejada.hmg.utils
class API {
companion object{
private val BASE = "https://hmgwebservices.com"
private val SERVICE = "Services/Patients.svc/REST"
val WIFI_CREDENTIALS = "$BASE/$SERVICE/Hmg_SMS_Get_By_ProjectID_And_PatientID"
val LOG_GEOFENCE = "$BASE/$SERVICE/GeoF_InsertPatientFileInfo"
}
}

@ -0,0 +1,8 @@
package com.ejada.hmg.utils
const val PREFS_STORAGE = "FlutterSharedPreferences"
const val PREF_KEY_SUCCESS = "HMG_GEOFENCE_SUCCESS"
const val PREF_KEY_FAILED = "HMG_GEOFENCE_FAILED"
const val PREF_KEY_HMG_ZONES = "flutter.hmg-geo-fences"
const val PREF_KEY_LANGUAGE = "flutter.language"

@ -0,0 +1,36 @@
package com.ejada.hmg.utils
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.Result
class FlutterText{
companion object{
fun with(key:String, completion:(String)->Unit){
HMGUtils.getPlatformChannel().invokeMethod("localizedValue",key, object:MethodChannel.Result{
override fun success(result: Any?) {
val localized = result as String?
if (localized != null){
completion(localized)
}else{
completion(key)
}
}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
completion(key)
require(false){
"'localizedValue' $errorMessage"
}
}
override fun notImplemented() {
require(false){
"'localizedValue' method not implemented at flutter"
}
}
})
}
}
}

@ -0,0 +1,224 @@
package com.ejada.hmg.utils
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.job.JobInfo
import android.app.job.JobScheduler
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
import android.widget.Toast
import androidx.annotation.Nullable
import androidx.core.app.NotificationCompat
import androidx.core.app.TaskStackBuilder
import com.ejada.hmg.BuildConfig
import com.ejada.hmg.MainActivity
import com.ejada.hmg.R
import com.ejada.hmg.geofence.GeoZoneModel
import com.github.kittinunf.fuel.core.extensions.jsonBody
import com.github.kittinunf.fuel.httpPost
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import io.flutter.plugin.common.MethodChannel
//import org.jetbrains.anko.doAsyncResult
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.*
import kotlin.concurrent.timerTask
class HMGUtils {
companion object{
private lateinit var platformChannel: MethodChannel
fun getPlatformChannel():MethodChannel{
return platformChannel
}
fun setPlatformChannel(channel: MethodChannel){
platformChannel = channel
}
fun timer(delay: Long, repeat: Boolean, tick: (Timer) -> Unit) : Timer{
val timer = Timer()
if(repeat)
timer.schedule(timerTask {
tick(timer)
}, delay, delay)
else
timer.schedule(timerTask {
tick(timer)
}, delay)
return timer
}
fun popMessage(context: MainActivity, message: String){
context.runOnUiThread {
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
}
}
fun popFlutterText(context: MainActivity, key: String){
context.runOnUiThread {
FlutterText.with(key){
Toast.makeText(context, it, Toast.LENGTH_LONG).show()
}
}
}
fun getLanguageCode(context: Context) : Int {
val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
val lang = pref.getString(PREF_KEY_LANGUAGE, "ar")
return if (lang == "ar") 2 else 1
}
fun defaultHTTPParams(context: Context) : Map<String, Any?>{
return mapOf(
"ZipCode" to "966",
"VersionID" to 5.8,
"Channel" to 3,
"LanguageID" to getLanguageCode(context),
"IPAdress" to "10.20.10.20",
"generalid" to "Cs2020@2016$2958",
"PatientOutSA" to 0,
"SessionID" to null,
"isDentalAllowedBackend" to false,
"DeviceTypeID" to 2)
}
fun <T>scheduleJob(context: Context, pendingIntentClassType:Class<T>, jobId:Int, intervalDuration:String, deadlineMillis:Long = (30 * 1000)) { // default deadline: 30 Seconds
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
val jobScheduler: JobScheduler = context.getSystemService(JobScheduler::class.java)
val serviceComponent = ComponentName(context, pendingIntentClassType)
val builder = JobInfo.Builder(jobId, serviceComponent)
builder.setPersisted(true)
builder.setBackoffCriteria(30000, JobInfo.BACKOFF_POLICY_LINEAR)
val intervalMillis = timeToMillis(intervalDuration,"HH:mm:ss")
builder.setMinimumLatency(intervalMillis) // wait at least
builder.setOverrideDeadline((intervalMillis + deadlineMillis)) // maximum delay
if (jobScheduler.schedule(builder.build()) == JobScheduler.RESULT_SUCCESS){
Logs.save(context,"ScheduleJob", "${pendingIntentClassType.simpleName}: Job scheduled to trigger after duration $intervalDuration >> HH:mm:ss --('MinimumLatency:$intervalMillis Deadline:${(intervalMillis + deadlineMillis)}')--",Logs.STATUS.SUCCESS)
}else{
Logs.save(context,"ScheduleJob", "${pendingIntentClassType.simpleName}: Failed to scheduled Job",Logs.STATUS.ERROR)
}
} else {
Logs.save(context,"ScheduleJob", "${pendingIntentClassType.simpleName}: Failed to scheduled Job on VERSION.SDK_INT < ${android.os.Build.VERSION_CODES.M}",Logs.STATUS.ERROR)
}
}
}
}
private const val NOTIFICATION_CHANNEL_ID = BuildConfig.APPLICATION_ID + ".channel"
fun timeToMillis(time:String, format:String):Long{
val sdf = SimpleDateFormat(format, Locale.US)
val millis = sdf.parse(time).time + TimeZone.getDefault().rawOffset
return millis
}
fun sendNotification(context: Context, title: String, @Nullable subtitle: String?, message: String?) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null) {
val name = context.getString(R.string.app_name)
val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID,
name,
NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
val intent = Intent(context, MainActivity::class.java)
val stackBuilder = TaskStackBuilder.create(context)
.addParentStack(MainActivity::class.java)
.addNextIntent(intent)
val notificationPendingIntent = stackBuilder.getPendingIntent(getUniqueId(), PendingIntent.FLAG_UPDATE_CURRENT)
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher_local)
.setContentIntent(notificationPendingIntent)
.setAutoCancel(true)
.setContentTitle(title)
subtitle.let { notification.setContentText(it) }
message.let { notification.setSubText(it) }
notificationManager.notify(getUniqueId(), notification.build())
}
//-------------------------
// Open Helper Methods
//-------------------------
fun getUniqueId() = ((System.currentTimeMillis() % 10000).toInt())
object DateUtils {
@JvmStatic
fun dateTimeNow() : String {
val format = SimpleDateFormat("dd-MMM-yyy hh:mm:ss")
return format.format(Date())
}
}
fun isJSONValid(jsonString: String?): Boolean {
try { JSONObject(jsonString) } catch (ex: JSONException) {
try { JSONArray(jsonString) } catch (ex1: JSONException) {
return false
}
}
return true
}
fun saveLog(context: Context, tag: String, message: String){
val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
var logs = pref.getString("LOGS", "")
logs += "$tag -> $message \n"
pref.edit().putString("LOGS", logs).apply();
}
fun getLogs(context: Context) : String?{
val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
return pref.getString("LOGS", "")
}
class HTTPResponse<T>(data: T){
final var data:T = data
}
fun <T>httpPost(url: String, body: Map<String, Any?>, onSuccess: (response: HTTPResponse<T>) -> Unit, onError: (error: Exception) -> Unit){
val gson = Gson()
val type = object : TypeToken<T>() {}.type
val jsonBody = gson.toJson(body)
url.httpPost()
.jsonBody(jsonBody, Charsets.UTF_8)
.timeout(10000)
.header("Content-Type", "application/json")
.header("Allow", "*/*")
.response { request, response, result ->
result.fold({ data ->
val dataString = String(data)
if (isJSONValid(dataString)) {
val responseData = gson.fromJson<T>(dataString, type)
onSuccess(HTTPResponse(responseData))
} else {
onError(Exception("Invalid response from server (Not a valid JSON)"))
}
}, {
onError(it)
})
}
}

@ -0,0 +1,351 @@
//package com.ejada.hmg.utils
//
//import android.annotation.SuppressLint
//import android.content.Context
//import android.net.ConnectivityManager
//import android.net.Network
//import android.net.NetworkCapabilities
//import android.net.NetworkRequest
//import android.net.wifi.ScanResult
//import android.net.wifi.WifiConfiguration
//import android.net.wifi.WifiManager
//import android.util.Log
//import com.ejada.hmg.utils.API
//import com.ejada.hmg.FlutterMainActivity
//import com.github.kittinunf.fuel.core.extensions.jsonBody
//import com.github.kittinunf.fuel.httpGet
//import com.github.kittinunf.fuel.httpPost
//import org.json.JSONObject
//import java.util.*
//
//
//@SuppressLint("MissingPermission")
//class HMG_Wifi_(flutterMainActivity: FlutterMainActivity) {
// val TAG = "WIFI"
// val TEST = true
//
// var context = flutterMainActivity;
// var completionListener: ((status: Boolean, message: String) -> Unit)? = null
//
//
// private var SSID = "HMG-GUEST"
// private var USER_NAME = ""
// private var PASSWORD = ""
// var NETWORK_ID = -1 // HMG-GUEST Assigned Network ID by Android
// private lateinit var PATIENT_ID:String
// /*
// * Helpful:
// * http://stackoverflow.com/questions/5452940/how-can-i-get-android-wifi-scan-results-into-a-list
// */
// fun triggerWifiScan(context: Context) {
// val wifi = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
// wifi.startScan()
// }
//
// /*
// * Helpful:
// * http://stackoverflow.com/questions/8818290/how-to-connect-to-a-specific-wifi-network-in-android-programmatically
// */
// fun connectToWifiNetworkWith(patientId: String): HMG_Wifi_ {
//
// val connectivityManager = context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
//
// PATIENT_ID = patientId
//
// val security = "OPEN"
// val networkPass = ""
// Log.d(TAG, "Connecting to SSID \"$SSID\" with password \"$networkPass\" and with security \"$security\" ...")
//
// // You need to create WifiConfiguration instance like this:
// val conf = WifiConfiguration()
// conf.SSID = "\"" + SSID + "\""
//
// if (security == "OPEN") {
// conf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
// } else if (security == "WEP") {
// conf.wepKeys[0] = "\"" + networkPass + "\""
// conf.wepTxKeyIndex = 0
// conf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
// conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40)
// } else {
// conf.preSharedKey = "\"" + networkPass + "\""
// }
//
// // Then, you need to add it to Android wifi manager settings:
// val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
//
// NETWORK_ID = wifiManager.addNetwork(conf)
// Log.d(TAG, "Network ID: $NETWORK_ID")
//
// //wifiManager.disconnect();
// val result = wifiManager.enableNetwork(NETWORK_ID, true)
// //wifiManager.reconnect();
// wifiManager.saveConfiguration()
//
// if(result == true){
// authNetworkConnection(NETWORK_ID);
// }else{
// completionListener?.let { it(false, "Error connecting to HMG network") }
// }
// return this
// }
//
// private var authTimer:Timer? = null
// fun authNetworkConnection(networkId: Int){
// authTimer = Timer()
// authTimer?.scheduleAtFixedRate(object : TimerTask() {
// override fun run() {
// if (connectedNetworkId() == networkId && connectedNetworkIPAddress() > 0) {
// authServerCall()
// authTimer?.cancel()
// }
// }
//
// }, 2000, 1000)
//
// // If wifi not connected in 5 sec terminate with fail status
// Timer().schedule(object : TimerTask() {
// override fun run() {
// if (null != authTimer) {
// authTimer?.cancel()
// completionListener?.let { it(false, "Error connecting to HMG network") }
// }
// }
// }, 5000)
//
// }
//
// fun authServerCall(){
//
// fun call(){
//
// forceNetworkCallOverWifi()
//
// val params = listOf("cmd" to "authenticate", "password" to PASSWORD, "user" to USER_NAME)
// val serverUrl = "https://captiveportal-login.hmg.com/cgi-bin/login"
//// val serverUrl = "http://192.168.102.223/cgi-bin/login"
// serverUrl
// .httpPost(params)
// .timeout(10000)
// .response { request, response, result ->
// Log.v(TAG, response.statusCode.toString())
//
// haveInternet { have ->
// if(have){
// Log.v(TAG, "Connected to internet via $SSID network at HMG")
// completionListener?.let { it(true, "Successfully connected to the internet") }
// }else{
// Log.e(TAG, "failed to connect to internet via $SSID network at HMG")
// completionListener?.let { it(false, "Authentication failed or you are already using your credentials on another device") }
// }
// }
// }
// }
//
// haveInternet { has ->
// if (has){
// getAuthCredentials {
// call()
// }
// }else{
// completionListener?.let { it(false, "You must have active internet connection to connect with HMG Network") }
// }
// }
// }
//
// fun haveInternet(completion: ((status: Boolean) -> Unit)){
// if (TEST)
// completion(true)
//
// "https://captive.apple.com".httpGet().response { request, response, result ->
// val have = response.statusCode == 200 && String(response.data).contains("<TITLE>Success</TITLE>", true)
// completion(have)
// }
// }
//
// fun getAuthCredentials(completion: (() -> Unit)){
// if (TEST){
// USER_NAME = "2300"
// PASSWORD = "1820"
// completion()
// return
// }
//
// val jsonBody = """{"PatientID":$PATIENT_ID}"""
// API.WIFI_CREDENTIALS
// .httpPost()
// .jsonBody(jsonBody, Charsets.UTF_8)
// .response { request, response, result ->
// val jsonString = String(response.data)
// Log.d(TAG, "JSON $jsonString")
//
// if (response.statusCode == 200){
//
// val jsonObject = JSONObject(jsonString)
// if(!jsonObject.getString("ErrorMessage").equals("null")){
// val errorMsg = jsonObject.getString("ErrorMessage")
// completionListener?.let { it(false, errorMsg) }
//
// }else{
// jsonObject.getJSONArray("Hmg_SMS_Get_By_ProjectID_And_PatientIDList").let { array ->
// array.getJSONObject(0).let { object_ ->
// if (object_.has("UserName") && object_.has("UserName")){
// USER_NAME = object_.getString("UserName")
// PASSWORD = object_.getString("Password")
// completion()
// }else{
// completionListener?.let { it(false, "Failed to get your internet credentials") }
// }
// }
// }
// }
//
// }else{
// completionListener?.let { it(false, "Failed to get your internet credentials") }
// }
// }
// }
//
// fun forceNetworkCallOverWifi(){
// val connectivityManager = context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
//// val network = Network
//// connectivityManager.activeNetwork
// // Exit app if Network disappears.
// // Exit app if Network disappears.
//// val networkCapabilities: NetworkCapabilities = ConnectivityManager.from(context).getNetworkCapabilities(network)
//// val networkCapabilities: NetworkCapabilities = connectivityManager.getNetworkCapabilities(network)
//
//// if (networkCapabilities == null) {
//// return
//// }
//
// val mNetworkCallback = object : ConnectivityManager.NetworkCallback() {
// override fun onLost(lostNetwork: Network?) {
//// if (network.equals(lostNetwork)){
//// //GlyphLayout.done(false)
//// }
// }
// }
// val builder: NetworkRequest.Builder = NetworkRequest.Builder()
//// for (transportType in networkCapabilities.getTransportTypes()) {
//// builder.addTransportType(transportType)
//// }
// connectivityManager.registerNetworkCallback(builder.build(), mNetworkCallback)
// }
//
// /*
// * Helpful:
// * http://stackoverflow.com/questions/6517314/android-wifi-connection-programmatically
// */
// fun getScanResultSecurity(result: ScanResult): String? {
// val capabilities: String = result.capabilities
// val securityModes = arrayOf("WEP", "PSK", "EAP")
// for (securityMode in securityModes) {
// if (capabilities.contains(securityMode)) {
// return securityMode
// }
// }
// return "OPEN"
// }
//
// //connects to the given ssid
// fun connectToWPAWiFi(ssid: String, password: String){
//
// WifiUtils.withContext(context)
// .connectWith(ssid, "")
// .setTimeout(40000)
// .onConnectionResult(object : ConnectionSuccessListener {
// override fun success() {
// Log.v(TAG,"Success")
// }
//
// override fun failed(@NonNull errorCode: ConnectionErrorCode) {
// Log.v(TAG,"Failed")
// }
// })
// .start()
// if(isConnectedTo(ssid)){ //see if we are already connected to the given ssid
// return
// }
//
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Log.e(TAG, "connection wifi Q")
//
// val wifiNetworkSpecifier: WifiNetworkSpecifier = WifiNetworkSpecifier.Builder()
// .setSsid(ssid)
// .setWpa2Passphrase(password)
// .build()
//
// val networkRequest: NetworkRequest = NetworkRequest.Builder()
// .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
// .setNetworkSpecifier(wifiNetworkSpecifier)
// .build()
//
// var connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
// var networkCallback = object : ConnectivityManager.NetworkCallback() {
// override fun onAvailable(network: Network) {
// super.onAvailable(network)
// connectivityManager.bindProcessToNetwork(network)
// Log.e(TAG, "onAvailable")
// }
//
// override fun onLosing(network: Network, maxMsToLive: Int) {
// super.onLosing(network, maxMsToLive)
// Log.e(TAG, "onLosing")
// }
//
// override fun onLost(network: Network) {
// super.onLost(network)
// Log.e(TAG, "onLosing")
// Log.e(TAG, "losing active connection")
// }
//
// override fun onUnavailable() {
// super.onUnavailable()
// Log.e(TAG, "onUnavailable")
// }
// }
// connectivityManager.requestNetwork(networkRequest, networkCallback)
//
// }else{
//
// try {
// val wm:WifiManager= context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
//
// Log.e(TAG, "connection wifi pre Q")
//
// var netId: Int = wm.addNetwork(getWifiConfig(ssid))
// if (netId == -1) netId = getExistingNetworkId(ssid);
// wm.saveConfiguration()
// if(wm.enableNetwork(netId, true)){
// Log.v(TAG,"HMG-GUEST Connected")
// }else{
// Log.v(TAG,"HMG-GUEST failed to connect")
// }
// } catch (e: Exception) {
// e.printStackTrace()
// Log.v(TAG,"HMG-GUEST failed to connect")
// }
// }
//
// }
//
// fun connectedNetworkId():Int{
// val wm:WifiManager= context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
// return wm.connectionInfo.networkId
// }
//
// fun connectedNetworkIPAddress():Int{
// val wm:WifiManager= context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
// return wm.connectionInfo.ipAddress
// }
//
// fun isConnectedTo(bssid: String):Boolean{
// val wm:WifiManager= context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
// if(wm.connectionInfo.bssid == bssid){
// return true
// }
// return false
// }
//
//}

@ -0,0 +1,145 @@
package com.ejada.hmg.utils
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
import com.ejada.hmg.BuildConfig
import com.google.gson.Gson
class Logs {
enum class STATUS{
SUCCESS,
ERROR;
}
class GeofenceEvent{
companion object{
fun save(context: Context, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){
Logs.Common.save(context,"GeofenceEvent", tag, message, status)
}
fun list(context: Context, tag:String? = null, status:Logs.STATUS? = null):List<LogModel>{
return Logs.Common.list(context,"GeofenceEvent", tag, status)
}
fun raw(context: Context):String{
return Logs.Common.raw(context,"GeofenceEvent")
}
}
}
class RegisterGeofence{
companion object{
fun save(context: Context, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){
Logs.Common.save(context,"RegisterGeofence", tag, message, status)
}
fun list(context: Context, tag:String? = null, status:Logs.STATUS? = null):List<LogModel>{
return Logs.Common.list(context,"RegisterGeofence", tag, status)
}
fun raw(context: Context):String{
return Logs.Common.raw(context,"RegisterGeofence");
}
}
}
companion object{
private var pref:SharedPreferences? = null
fun save(context: Context, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){
Logs.Common.save(context,"Logs", tag, message, status)
}
fun list(context: Context, tag:String? = null, status:Logs.STATUS? = null):List<LogModel>{
return Logs.Common.list(context,"Logs", tag, status)
}
fun raw(context: Context):String{
return Logs.Common.raw(context,"Logs");
}
private fun storage(context: Context):SharedPreferences{
if(pref == null) {
pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE)
}
return pref!!
}
}
private class Common{
companion object{
private val gson = Gson()
fun save(context: Context, key:String, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){
if(!BuildConfig.DEBUG)
return
val pref = Logs.storage(context)
val string = pref.getString(key,"{}")
val json = gson.fromJson<LogsContainerModel>(string,LogsContainerModel::class.java)
json.add(
LogModel().apply {
this.TAG = tag
this.MESSAGE = message
this.STATUS = status.name
this.DATE = DateUtils.dateTimeNow()
}
)
pref.edit().putString(key,gson.toJson(json)).apply()
}
fun list(context: Context, key:String, tag:String? = null, status:Logs.STATUS? = null):List<LogModel>{
val pref = Logs.storage(context)
val string = pref.getString(key,"{}")
val json = gson.fromJson<LogsContainerModel>(string,LogsContainerModel::class.java)
if(tag == null && status == null) {
return json.LOGS
}else if(tag != null && status != null){
return json.LOGS.filter { (it.TAG == tag && it.STATUS == status.name) }
}else if(tag != null){
return json.LOGS.filter { (it.TAG == tag) }
}else if(status != null){
return json.LOGS.filter { (it.STATUS == status.name) }
}
return listOf()
}
fun raw(context: Context, key:String):String{
val pref = Logs.storage(context)
val string = pref.getString(key,"{}")
return string!!
}
}
}
class LogModel{
lateinit var TAG:String
lateinit var MESSAGE:String
lateinit var STATUS:String
lateinit var DATE:String
companion object{
fun with(tag:String, message:String, status:String):LogModel{
return LogModel().apply {
this.TAG = tag
this.MESSAGE = message
this.STATUS = status
this.DATE = DateUtils.dateTimeNow()
}
}
}
}
class LogsContainerModel{
var LOGS = mutableListOf<LogModel>()
fun add(log:LogModel){
LOGS.add(log)
}
}
}

@ -0,0 +1,45 @@
package com.ejada.hmg.utils
import com.ejada.hmg.MainActivity
import com.ejada.hmg.opentok.OpenTok
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class OpenTokPlatformBridge(private var flutterEngine: FlutterEngine, private var mainActivity: MainActivity) {
private lateinit var channel: MethodChannel
private lateinit var openTok: OpenTok
companion object {
private const val CHANNEL = "OpenTok-Platform-Bridge"
}
fun create(){
openTok = OpenTok(mainActivity, flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
when (call.method) {
"initSession" -> {
openTok.initSession(call, result)
}
"swapCamera" -> {
openTok.swapCamera(call, result)
}
"toggleAudio" -> {
openTok.toggleAudio(call, result)
}
"toggleVideo" -> {
openTok.toggleVideo(call, result)
}
"hangupCall" -> {
openTok.hangupCall(call, result)
}
else -> {
result.notImplemented()
}
}
}
}
}

@ -0,0 +1,199 @@
package com.ejada.hmg.utils
import android.content.Context
import android.content.Intent
import android.content.Intent.getIntent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.widget.Toast
import androidx.core.app.ActivityCompat.startActivityForResult
import android.net.wifi.WifiManager
import android.util.Log
import com.ejada.hmg.MainActivity
import com.ejada.hmg.hmgwifi.HMG_Guest
import com.ejada.hmg.geofence.GeoZoneModel
import com.ejada.hmg.geofence.HMG_Geofence
import com.ejada.hmg.hmgwifi.WpaEnterprise
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class PlatformBridge(private var flutterEngine: FlutterEngine, private var mainActivity: MainActivity) {
private lateinit var channel: MethodChannel
companion object {
private const val CHANNEL = "HMG-Platform-Bridge"
private const val HMG_INTERNET_WIFI_CONNECT_METHOD = "connectHMGInternetWifi"
private const val HMG_GUEST_WIFI_CONNECT_METHOD = "connectHMGGuestWifi"
private const val ENABLE_WIFI_IF_NOT = "enableWifiIfNot"
private const val REGISTER_HMG_GEOFENCES = "registerHmgGeofences"
private const val UN_REGISTER_HMG_GEOFENCES = "unRegisterHmgGeofences"
private const val IS_DRAW_OVER_APPS_PERMISSION_ALLOWED = "isDrawOverAppsPermissionAllowed"
private const val ASK_DRAW_OVER_APPS_PERMISSION = "askDrawOverAppsPermission"
private const val GET_INTENT = "getIntent"
}
fun create() {
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
HMGUtils.setPlatformChannel(channel)
channel.setMethodCallHandler { methodCall: MethodCall, result: MethodChannel.Result ->
if (methodCall.method == HMG_INTERNET_WIFI_CONNECT_METHOD) {
connectHMGInternetWifi(methodCall, result)
} else if (methodCall.method == HMG_GUEST_WIFI_CONNECT_METHOD) {
connectHMGGuestWifi(methodCall, result)
} else if (methodCall.method == ENABLE_WIFI_IF_NOT) {
enableWifiIfNot(methodCall, result)
} else if (methodCall.method == REGISTER_HMG_GEOFENCES) {
registerHmgGeofences(methodCall, result)
} else if (methodCall.method == UN_REGISTER_HMG_GEOFENCES) {
unRegisterHmgGeofences(methodCall, result)
} else if (methodCall.method == IS_DRAW_OVER_APPS_PERMISSION_ALLOWED) {
isDrawOverAppsPermissionAllowed(methodCall, result)
} else if (methodCall.method == ASK_DRAW_OVER_APPS_PERMISSION) {
askDrawOverAppsPermission(methodCall, result)
} else if (methodCall.method == GET_INTENT) {
getIntentData(methodCall, result)
} else {
result.notImplemented()
}
}
val res = channel.invokeMethod("localizedValue", "errorConnectingHmgNetwork")
}
private fun connectHMGInternetWifi(methodCall: MethodCall, result: MethodChannel.Result) {
(methodCall.arguments as ArrayList<*>).let {
require(it.size == 3 && (it[0] is String) && (it[1] is String), lazyMessage = {
"Missing or invalid arguments (Must have three argument of 'String'"
})
val ssid = it[0].toString()
val username = it[1].toString()
val password = it[2].toString()
WpaEnterprise(mainActivity,ssid).connect(username,password) { status, message ->
HMGUtils.timer(2000,false){
mainActivity.runOnUiThread {
if(status)
result.success(if (status) 1 else 0)
else
result.error(message, null, null)
}
}
}
// HMG_Internet(mainActivity)
// .connectToHMGGuestNetwork(username, password) { status, message ->
// mainActivity.runOnUiThread {
// result.success(if (status) 1 else 0)
//
// HMGUtils.popFlutterText(mainActivity, message)
// Log.v(this.javaClass.simpleName, "$status | $message")
// }
//
// }
}
}
private fun connectHMGGuestWifi(methodCall: MethodCall, result: MethodChannel.Result) {
(methodCall.arguments as ArrayList<*>).let {
require(it.size == 1 && (it[0] is String), lazyMessage = {
"Missing or invalid arguments (Must have one argument 'String at 0'"
})
val ssid = it[0].toString()
HMG_Guest(mainActivity, ssid).connectToHMGGuestNetwork { status, message ->
mainActivity.runOnUiThread {
result.success(if (status) 1 else 0)
HMGUtils.popFlutterText(mainActivity, message)
Log.v(this.javaClass.simpleName, "$status | $message")
}
}
}
}
private fun enableWifiIfNot(methodCall: MethodCall, result: MethodChannel.Result) {
val wm = mainActivity.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager?
if (wm != null) {
if (!wm.isWifiEnabled)
wm.isWifiEnabled = true
result.success(true)
} else
result.error("101", "Error while opening wifi, Please try to open wifi yourself and try again", "'WifiManager' service failed");
}
private fun registerHmgGeofences(methodCall: MethodCall, result: MethodChannel.Result) {
channel.invokeMethod("getGeoZones", null, object : MethodChannel.Result {
override fun success(result: Any?) {
if (result is String) {
val geoZones = GeoZoneModel().listFrom(result)
HMG_Geofence.shared(mainActivity).register() { s, e -> }
}
}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {}
override fun notImplemented() {}
})
}
private fun unRegisterHmgGeofences(methodCall: MethodCall, result: MethodChannel.Result) {
HMG_Geofence.shared(mainActivity).unRegisterAll { status, exception ->
if (status)
result.success(true)
else
result.error("101", exception?.localizedMessage, exception);
}
}
private fun isDrawOverAppsPermissionAllowed(methodCall: MethodCall, result: MethodChannel.Result) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (
Settings.canDrawOverlays(mainActivity)
) {
result.success(true)
} else {
result.success(false)
}
} else {
result.success(false)
}
}
private fun askDrawOverAppsPermission(methodCall: MethodCall, result: MethodChannel.Result) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
val uri = Uri.parse("package:" + mainActivity.getPackageName())
intent.setData(uri)
startActivityForResult(mainActivity, intent, 102, null)
result.success(true)
} else {
result.success(false)
}
}
private fun getIntentData(methodCall: MethodCall, result: MethodChannel.Result) {
val bundle: Bundle? = getIntent("").extras
if (bundle != null) {
val message = bundle.getString("notification") // 1
System.out.println("BundleExtra:" + message)
Toast.makeText(this.mainActivity, message + "", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this.mainActivity, "Bundle Null", Toast.LENGTH_SHORT).show();
}
result.success(true);
}
}

@ -0,0 +1,81 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.cloud.hmg_patient_app.whatsapp;
import static java.sql.DriverManager.println;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.util.Base64;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
public class AppSignatureRetriever {
private static final String HASH_TYPE = "SHA-256";
public static final int NUM_HASHED_BYTES = 9;
public static final int NUM_BASE64_CHAR = 11;
public void logSignatures(Context context) {
Collection<String> appSignatures = getAppSignatures(context);
appSignatures.forEach(signature -> println("Signature: " + signature));
}
/**
* Get all the app signatures for the current package.
*
* @return signatures for current app
*/
public Collection<String> getAppSignatures(Context context) {
try {
// Get all package signatures for the current package
String packageName = context.getPackageName();
println("Package name: " + packageName);
PackageManager packageManager = context.getPackageManager();
Signature[] signatures = packageManager.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES).signatures;
// For each signature create a compatible hash
Collection<String> appCodes = Arrays.stream(signatures)
.map(signature -> hash(packageName, signature.toCharsString()))
.collect(Collectors.toList());
return appCodes;
} catch (PackageManager.NameNotFoundException e) {
println("Unable to find package to obtain hash.");
throw new RuntimeException("Unable to find package to obtain hash.", e);
}
}
private String hash(String packageName, String signature) {
String appInfo = packageName + " " + signature;
try {
MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
byte[] hashSignature = messageDigest.digest();
// truncated into NUM_HASHED_BYTES
hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
// encode into Base64
String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);
println(String.format("pkg: %s -- hash: %s", packageName, base64Hash));
return base64Hash;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to generate hash for application", e);
}
}
}

@ -0,0 +1,31 @@
package com.cloud.hmg_patient_app.whatsapp
import android.content.Context
import android.content.Intent
import com.whatsapp.otp.android.sdk.WhatsAppOtpHandler
import com.whatsapp.otp.android.sdk.WhatsAppOtpIncomingIntentHandler
import java.lang.ref.WeakReference
object WhatsApp {
val whatsAppOtpHandler = WhatsAppOtpHandler()
inline fun handleOTP ( intent: Intent, crossinline validateOTP:(code: String )-> Unit) =
WhatsAppOtpIncomingIntentHandler().processOtpCode(
intent,
// call your function to validate
{code -> validateOTP(code) },
{error,exception->
println("the error is ${error.name}")
println("the exception stacktrace is ${exception.message}")
println("the exception is cause ${exception.cause}")
})
fun performHandShake(context : WeakReference<Context>) = whatsAppOtpHandler.sendOtpIntentToWhatsApp(context.get()!!)
fun isWhatsAppInstalled(context : WeakReference<Context>) : Boolean = whatsAppOtpHandler.isWhatsAppInstalled(context.get()!!)
}

@ -0,0 +1,17 @@
package com.cloud.hmg_patient_app.whatsapp
import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
import com.cloud.hmg_patient_app.whatsapp.WhatsApp
import com.cloud.hmg_patient_app.whatsapp.WhatsAppOtpPlatformBridge
import io.flutter.embedding.android.FlutterFragmentActivity
class WhatsAppCodeActivity : FlutterFragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WhatsApp.handleOTP(intent){code ->
WhatsAppOtpPlatformBridge.result?.success(code);
finish()
}
}
}

@ -0,0 +1,48 @@
package com.cloud.hmg_patient_app.whatsapp
import com.ejada.hmg.MainActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import java.lang.ref.WeakReference
class WhatsAppOtpPlatformBridge(
private var flutterEngine: FlutterEngine,
private var mainActivity: MainActivity
) {
private lateinit var channel: MethodChannel
companion object {
private const val CHANNEL = "whats_app_otp"
var result: MethodChannel.Result? = null
}
fun invoke() {
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
when (call.method) {
"isWhatsAppInstalled" -> {
val isAppInstalled =
WhatsApp.isWhatsAppInstalled(WeakReference(mainActivity))
result.success(isAppInstalled)
}
"performHandShake" -> {
WhatsApp.performHandShake(WeakReference(mainActivity))
}
"startListening" -> {
WhatsAppOtpPlatformBridge.result = result
}
else -> {
result.notImplemented()
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal">
<FrameLayout
android:id="@+id/publisher_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF9800" />
</LinearLayout>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal">
<FrameLayout
android:id="@+id/subscriber_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#3F51B5" />
<TextView
android:text="Remote"
android:textColor="#FFFFFF"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@drawable/*,@raw/slow_spring_board" />

@ -0,0 +1,23 @@
<resources>
<string name="app_name">HMG Patient App</string>
<string name="geofence_unknown_error">
Unknown error: the Geofence service is not available now.
</string>
<string name="geofence_not_available">
Geofence service is not available now. Go to Settings>Location>Mode and choose High accuracy.
</string>
<string name="geofence_too_many_geofences">
Your app has registered too many geofences.
</string>
<string name="geofence_too_many_pending_intents">
You have provided too many PendingIntents to the addGeofences() call.
</string>
<string name="GEOFENCE_INSUFFICIENT_LOCATION_PERMISSION">
App do not have permission to access location service.
</string>
<string name="GEOFENCE_REQUEST_TOO_FREQUENT">
Geofence requests happened too frequently.
</string>
<string name="mapbox_access_token" translatable="false">sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg</string>
</resources>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@android:color/white</item>
</style>
</resources>

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ejada.hmg">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

@ -0,0 +1,88 @@
allprojects {
repositories {
google()
mavenCentral()
maven {
url 'https://developer.huawei.com/repo/'
}
maven {
url "https://artifactory.ess-dev.com/artifactory/gradle-dev-local"
}
maven {
url 'https://api.mapbox.com/downloads/v2/releases/maven'
credentials {
username = 'mapbox'
password = "sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg"
if (password == null || password == "") {
throw new GradleException("MAPBOX_DOWNLOADS_TOKEN isn't set. Set it to the project properties or to the environment variables.")
}
}
authentication {
basic(BasicAuthentication)
}
}
}
// Exclude old BouncyCastle globally to avoid duplicate classes
configurations.all {
exclude group: 'org.bouncycastle', module: 'bcprov-jdk16'
}
tasks.withType(JavaCompile).configureEach {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
jvmTarget = "21"
}
}
subprojects {
afterEvaluate { project ->
if (project.hasProperty('android')) {
project.android {
if (namespace == null) {
namespace project.group
}
buildFeatures {
if (buildConfig == null) {
buildConfig true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
}
}
}
// Force Java 17 for all JavaCompile tasks in all subprojects (including plugins)
project.tasks.withType(JavaCompile).configureEach {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
// Force Kotlin JVM target for all subprojects
project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
jvmTarget = "21"
}
}
}
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.layout.buildDirectory
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,47 @@
{
"project_info": {
"project_number": "815750722565",
"firebase_url": "https://api-project-815750722565.firebaseio.com",
"project_id": "api-project-815750722565",
"storage_bucket": "api-project-815750722565.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:815750722565:android:62281cd3e5df4063",
"android_client_info": {
"package_name": "com.ejada.hmg"
}
},
"oauth_client": [
{
"client_id": "815750722565-3a0gc7neins0eoahdrimrfksk0sqice8.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyDZDeWcBlRE3YfJWYt_DCiToVnANfaj8qg"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "815750722565-3a0gc7neins0eoahdrimrfksk0sqice8.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "815750722565-0cq9366orvsk5ipivq6lijcj56u03fr7.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "com.void.demo"
}
}
]
}
}
}
],
"configuration_version": "1"
}

@ -0,0 +1,7 @@
org.gradle.jvmargs=-Xmx4096m
kotlin.daemon.jvmargs=-Xmx4096m
android.useAndroidX=true
android.enableJetifier=true
android.suppressUnsupportedCompileSdk=33
MAPBOX_USER_NAME = "mapbox"
MAPBOX_DOWNLOADS_TOKEN="sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg"

@ -0,0 +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
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

@ -0,0 +1,198 @@
//pluginManagement {
// def flutterSdkPath = {
// def properties = new Properties()
// file("local.properties").withInputStream { properties.load(it) }
// def flutterSdkPath = properties.getProperty("flutter.sdk")
// assert flutterSdkPath != null : "flutter.sdk not set in local.properties"
// return flutterSdkPath
// }()
//
// includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
//
// repositories {
// google()
// mavenCentral()
// gradlePluginPortal()
// }
//
// plugins {
// id "dev.flutter.flutter-plugin-loader" version "1.0.0"
// id "com.android.application" version "8.1.0" apply false
// id "org.jetbrains.kotlin.android" version "2.1.10" apply false
// id "com.google.gms.google-services" version "4.4.2" apply false
// id "com.google.firebase.crashlytics" version "3.0.3" apply false
//
// }
//}
//
//
//dependencyResolutionManagement {
// repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
//
// repositories {
// google()
// mavenCentral()
// flatDir {
// dirs 'libs'
// }
// maven {
// url 'https://developer.huawei.com/repo/'
// }
// maven {
// url "https://artifactory.ess-dev.com/artifactory/gradle-dev-local"
// }
// maven {
// url 'https://api.mapbox.com/downloads/v2/releases/maven'
//
// credentials {
// username = 'mapbox'
// password = "sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg"
// if (password == null || password == "") {
// throw new GradleException("MAPBOX_DOWNLOADS_TOKEN isn't set. Set it to the project properties or to the environment variables.")
// }
// }
// authentication {
// basic(BasicAuthentication)
// }
// }
// }
//}
//
//
//// Load local properties
//def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
//def properties = new Properties()
//assert localPropertiesFile.exists()
//localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
//
//// Get Flutter SDK path
//def flutterSdkPath = properties.getProperty('flutter.sdk')
//assert flutterSdkPath != null : "flutter.sdk not set in local.properties"
//
//// Get vital-sign-engine path
//def vitalSignEnginePath = properties.getProperty('vital.sign.engine.path')
//assert vitalSignEnginePath != null : "vital.sign.engine.path not set in local.properties"
//
//// Include modules
//include ':app'
////project(':vital-sign-engine').projectDir = file(vitalSignEnginePath)
//// ':vital-sign-engine'
//include ':app'
//
//def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
//
//def plugins = new Properties()
//def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
//if (pluginsFile.exists()) {
// pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
//}
//
//plugins.each { name, path ->
// def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
// include ":$name"
// project(":$name").projectDir = pluginDirectory
//}
//// Load local properties
//def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
//def properties = new Properties()
//assert localPropertiesFile.exists()
//localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
//
//// Get Flutter SDK path
//def flutterSdkPath = properties.getProperty('flutter.sdk')
//assert flutterSdkPath != null : "flutter.sdk not set in local.properties"
//
//// Get vital-sign-engine path
//def vitalSignEnginePath = properties.getProperty('vital.sign.engine.path')
//assert vitalSignEnginePath != null : "vital.sign.engine.path not set in local.properties"
//
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
// Get vital-sign-engine path
def vitalSignEngine = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def vitalSignEnginePath = properties.getProperty("vital.sign.engine.path")
assert vitalSignEnginePath != null, "vital.sign.engine.path not set in local.properties"
return vitalSignEnginePath
}()
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
flatDir {
dirs 'libs'
}
maven {
url 'https://developer.huawei.com/repo/'
}
maven {
url "https://artifactory.ess-dev.com/artifactory/gradle-dev-local"
}
maven {
url 'https://api.mapbox.com/downloads/v2/releases/maven'
credentials {
username = 'mapbox'
password = "sk.eyJ1IjoicndhaWQiLCJhIjoiY2x6NWo0bTMzMWZodzJrcGZpemYzc3Z4dSJ9.uSSZuwNSGCcCdPAiORECmg"
if (password == null || password == "") {
throw new GradleException("MAPBOX_DOWNLOADS_TOKEN isn't set. Set it to the project properties or to the environment variables.")
}
}
authentication {
basic(BasicAuthentication)
}
}
}
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version '8.11.0' apply false
id("org.jetbrains.kotlin.android") version "2.2.0" apply false
id("com.google.gms.google-services") version "4.4.3" apply false
id("com.google.firebase.crashlytics") version "3.0.4" apply false
id('org.gradle.toolchains.foojay-resolver-convention') version '0.9.0' apply false
}
include ":app"
//// Add the following to include vitalSignEngine as a module
//def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
//def properties = new Properties()
//assert localPropertiesFile.exists()
//localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
//def vitalSignEnginePath = properties.getProperty('vital.sign.engine.path')
//assert vitalSignEnginePath != null : "vital.sign.engine.path not set in local.properties"
//
//include ':vitalSignEngine'
//project(':vitalSignEngine').projectDir = file(vitalSignEnginePath)

@ -0,0 +1 @@
include ':app'

@ -0,0 +1,68 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "app_icon_16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "app_icon_32.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "app_icon_32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "app_icon_64.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "app_icon_128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "app_icon_256.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "app_icon_256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "app_icon_512.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "app_icon_512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "app_icon_1024.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Loading…
Cancel
Save