Browse Source

update add fido mechanism and release for 5.0.45

alqindiirsyam 1 month ago
parent
commit
8fdea654df
67 changed files with 1246 additions and 813 deletions
  1. BIN
      .DS_Store
  2. 30 26
      AppBuilder/AppBuilder.xcodeproj/project.pbxproj
  3. 2 0
      AppBuilder/AppBuilder/AppDelegate.swift
  4. 17 95
      AppBuilder/AppBuilder/FourthTabViewController.swift
  5. 30 0
      AppBuilder/AppBuilder/GoogleService-Info.plist
  6. 9 0
      AppBuilder/AppBuilder/Info.plist
  7. 12 12
      AppBuilder/AppBuilder/SecondTabViewController.swift
  8. 5 10
      AppBuilder/AppBuilder/ViewController.swift
  9. BIN
      DigiXLite/.DS_Store
  10. BIN
      ExampleCode/.DS_Store
  11. 1 0
      NexilisLite/NexilisLite.podspec
  12. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/Attachment.imageset/Attachment.png
  13. 0 21
      NexilisLite/NexilisLite/Resource/Assets.xcassets/Conversation---Purple.imageset/Contents.json
  14. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/Conversation---Purple.imageset/Conversation---Purple.png
  15. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/Q-Button-PNG.imageset/Q-Button-PNG.png
  16. 0 21
      NexilisLite/NexilisLite/Resource/Assets.xcassets/confidential_icon_gray.imageset/Contents.json
  17. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/confidential_icon_gray.imageset/confidential_icon_gray.png
  18. 0 21
      NexilisLite/NexilisLite/Resource/Assets.xcassets/gaspol_icon.imageset/Contents.json
  19. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/gaspol_icon.imageset/nexilis_icon.png
  20. 0 21
      NexilisLite/NexilisLite/Resource/Assets.xcassets/ic_launcher_pb.imageset/Contents.json
  21. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/ic_launcher_pb.imageset/ic_launcher_pb.png
  22. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/palio_button.imageset/palio_button.png
  23. 0 21
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ball_b.imageset/Contents.json
  24. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ball_b.imageset/ic_launcher_fit.png
  25. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_fb_bni2.imageset/pb_fb_bni2.png
  26. 0 21
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_fb_expora2.imageset/Contents.json
  27. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_fb_expora2.imageset/pb_fb_expora2.png
  28. 1 1
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ic_msisdn.imageset/Contents.json
  29. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ic_msisdn.imageset/pb_ic_msisdn.png
  30. 1 1
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ic_name.imageset/Contents.json
  31. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ic_name.imageset/pb_ic_name.png
  32. 1 1
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_stg_email.imageset/Contents.json
  33. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_stg_email.imageset/pb_stg_email.png
  34. 1 1
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_user.imageset/Contents.json
  35. BIN
      NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_user.imageset/pb_user.png
  36. 5 3
      NexilisLite/NexilisLite/Resource/Palio.storyboard
  37. 106 28
      NexilisLite/NexilisLite/Source/APIS.swift
  38. 40 2
      NexilisLite/NexilisLite/Source/CoreMessage_TMessageBank.swift
  39. 4 0
      NexilisLite/NexilisLite/Source/CoreMessage_TMessageCode.swift
  40. 3 0
      NexilisLite/NexilisLite/Source/CoreMessage_TMessageKey.swift
  41. 17 15
      NexilisLite/NexilisLite/Source/Extension.swift
  42. 244 0
      NexilisLite/NexilisLite/Source/MasterKeyUtil.swift
  43. 48 70
      NexilisLite/NexilisLite/Source/Nexilis.swift
  44. 12 1
      NexilisLite/NexilisLite/Source/Utils.swift
  45. 1 1
      NexilisLite/NexilisLite/Source/View/Call/CreateConferenceCallController.swift
  46. 0 13
      NexilisLite/NexilisLite/Source/View/Call/QmeraAudioViewController.swift
  47. 0 13
      NexilisLite/NexilisLite/Source/View/Call/QmeraVideoViewController.swift
  48. 0 13
      NexilisLite/NexilisLite/Source/View/Call/VideoConferenceViewController.swift
  49. 2 2
      NexilisLite/NexilisLite/Source/View/Chat/ArchivedChatView.swift
  50. 2 2
      NexilisLite/NexilisLite/Source/View/Chat/ChatWALikeVC.swift
  51. 1 1
      NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift
  52. 49 26
      NexilisLite/NexilisLite/Source/View/Chat/ListGroupImages.swift
  53. 2 7
      NexilisLite/NexilisLite/Source/View/Chat/SecureFolderView.swift
  54. 1 1
      NexilisLite/NexilisLite/Source/View/Control/BroadcastMembersTableViewController.swift
  55. 1 1
      NexilisLite/NexilisLite/Source/View/Control/BroadcastViewController.swift
  56. 3 25
      NexilisLite/NexilisLite/Source/View/Control/ChangeDeviceViewController.swift
  57. 3 3
      NexilisLite/NexilisLite/Source/View/Control/ChangeNamePassswordViewController.swift
  58. 4 7
      NexilisLite/NexilisLite/Source/View/Control/ContactChatViewController.swift
  59. 2 2
      NexilisLite/NexilisLite/Source/View/Control/HistoryBroadcastViewController.swift
  60. 6 14
      NexilisLite/NexilisLite/Source/View/Control/ProfileViewController.swift
  61. 10 5
      NexilisLite/NexilisLite/Source/View/Control/SettingTableViewController.swift
  62. 120 0
      NexilisLite/NexilisLite/Source/View/Control/SignInOption.swift
  63. 442 282
      NexilisLite/NexilisLite/Source/View/Control/SignUpSignIn.swift
  64. 6 1
      NexilisLite/NexilisLite/Source/View/Control/VerifyEmail.swift
  65. 1 1
      NexilisLite/NexilisLite/Source/View/Streaming/QmeraCreateStreamingViewController.swift
  66. 1 1
      NexilisLite/NexilisLite/Source/View/Streaming/QmeraGroupChooserViewController.swift
  67. BIN
      NexilisUC/.DS_Store

BIN
.DS_Store


+ 30 - 26
AppBuilder/AppBuilder.xcodeproj/project.pbxproj

@@ -8,6 +8,7 @@
 
 /* Begin PBXBuildFile section */
 		12960AE02892361000A467DD /* FourthTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12960ADF2892361000A467DD /* FourthTabViewController.swift */; };
+		207AC549A39F23917B1C1F2C /* Pods_AppBuilder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 321CD27623D0A7A68A20F1A1 /* Pods_AppBuilder.framework */; };
 		2401CE9A275490DB00B323BB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2401CE99275490DB00B323BB /* AppDelegate.swift */; };
 		2401CE9C275490DB00B323BB /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2401CE9B275490DB00B323BB /* SceneDelegate.swift */; };
 		2401CE9E275490DB00B323BB /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2401CE9D275490DB00B323BB /* ViewController.swift */; };
@@ -32,7 +33,7 @@
 		CD9D59E32BEE1D30008014B4 /* nu_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = CD9D59D82BEE1D30008014B4 /* nu_icon.png */; };
 		CDFA682D2D841F6200A13E90 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CDFA68292D841F6200A13E90 /* MainInterface.storyboard */; };
 		CDFA682E2D841F6200A13E90 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFA682A2D841F6200A13E90 /* ShareViewController.swift */; };
-		E0308FDDFDA3B4D338D1CB01 /* Pods_AppBuilder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 723FBB4755C089F11F51DAFE /* Pods_AppBuilder.framework */; };
+		CDFD90D12E05361400F1E245 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = CDFD90D02E05361400F1E245 /* GoogleService-Info.plist */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -70,9 +71,8 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
-		0A806FC385105116A13143A0 /* Pods-AppBuilder.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppBuilder.debug.xcconfig"; path = "Target Support Files/Pods-AppBuilder/Pods-AppBuilder.debug.xcconfig"; sourceTree = "<group>"; };
-		0CEFC760D412D7044F939773 /* Pods-AppBuilder.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppBuilder.release.xcconfig"; path = "Target Support Files/Pods-AppBuilder/Pods-AppBuilder.release.xcconfig"; sourceTree = "<group>"; };
 		12960ADF2892361000A467DD /* FourthTabViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FourthTabViewController.swift; sourceTree = "<group>"; };
+		179B641B36103480967D747F /* Pods-AppBuilder.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppBuilder.release.xcconfig"; path = "Target Support Files/Pods-AppBuilder/Pods-AppBuilder.release.xcconfig"; sourceTree = "<group>"; };
 		2401CE96275490DB00B323BB /* AppBuilder.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppBuilder.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		2401CE99275490DB00B323BB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		2401CE9B275490DB00B323BB /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
@@ -81,7 +81,8 @@
 		2401CEA2275490E600B323BB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		2401CEA5275490E600B323BB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		2401CEA7275490E600B323BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		723FBB4755C089F11F51DAFE /* Pods_AppBuilder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppBuilder.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		321CD27623D0A7A68A20F1A1 /* Pods_AppBuilder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppBuilder.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		791C498D24080D50B50FF3EE /* Pods-AppBuilder.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppBuilder.debug.xcconfig"; path = "Target Support Files/Pods-AppBuilder/Pods-AppBuilder.debug.xcconfig"; sourceTree = "<group>"; };
 		A413B18627EACB20006D16EB /* PrefsUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsUtil.swift; sourceTree = "<group>"; };
 		A42ED92127F30BA200B0FAB7 /* FirstTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstTabViewController.swift; sourceTree = "<group>"; };
 		A42ED92327F3FC2F00B0FAB7 /* SecondTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondTabViewController.swift; sourceTree = "<group>"; };
@@ -103,6 +104,7 @@
 		CDFA68272D841F6200A13E90 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		CDFA68282D841F6200A13E90 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
 		CDFA682A2D841F6200A13E90 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
+		CDFD90D02E05361400F1E245 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -110,7 +112,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				E0308FDDFDA3B4D338D1CB01 /* Pods_AppBuilder.framework in Frameworks */,
+				207AC549A39F23917B1C1F2C /* Pods_AppBuilder.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -131,7 +133,7 @@
 				CDFA682B2D841F6200A13E90 /* AppBuilderShare */,
 				6E32BCCF4DE50EE1A90E8AAE /* Pods */,
 				2401CE97275490DB00B323BB /* Products */,
-				73E5C47B30DFE705A67831A0 /* Frameworks */,
+				F0647ED14926508B9A5FFEEF /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
@@ -149,6 +151,7 @@
 			children = (
 				CDE27BA42D53641D006298BD /* AppBuilder.entitlements */,
 				2401CEA7275490E600B323BB /* Info.plist */,
+				CDFD90D02E05361400F1E245 /* GoogleService-Info.plist */,
 				CD9D59D42BEE1D30008014B4 /* bi_icon.png */,
 				CD9D59D32BEE1D30008014B4 /* bpkh_icon.png */,
 				CD9D59D52BEE1D30008014B4 /* diginets_icon.png */,
@@ -178,20 +181,12 @@
 		6E32BCCF4DE50EE1A90E8AAE /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				0A806FC385105116A13143A0 /* Pods-AppBuilder.debug.xcconfig */,
-				0CEFC760D412D7044F939773 /* Pods-AppBuilder.release.xcconfig */,
+				791C498D24080D50B50FF3EE /* Pods-AppBuilder.debug.xcconfig */,
+				179B641B36103480967D747F /* Pods-AppBuilder.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
 		};
-		73E5C47B30DFE705A67831A0 /* Frameworks */ = {
-			isa = PBXGroup;
-			children = (
-				723FBB4755C089F11F51DAFE /* Pods_AppBuilder.framework */,
-			);
-			name = Frameworks;
-			sourceTree = "<group>";
-		};
 		CDFA682B2D841F6200A13E90 /* AppBuilderShare */ = {
 			isa = PBXGroup;
 			children = (
@@ -203,6 +198,14 @@
 			path = AppBuilderShare;
 			sourceTree = "<group>";
 		};
+		F0647ED14926508B9A5FFEEF /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				321CD27623D0A7A68A20F1A1 /* Pods_AppBuilder.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -210,13 +213,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 2401CEC0275490E600B323BB /* Build configuration list for PBXNativeTarget "AppBuilder" */;
 			buildPhases = (
-				A2E40DAFEDA903E5068C358F /* [CP] Check Pods Manifest.lock */,
+				5300666D165DA92EBA73470C /* [CP] Check Pods Manifest.lock */,
 				2401CE92275490DB00B323BB /* Sources */,
 				2401CE93275490DB00B323BB /* Frameworks */,
 				2401CE94275490DB00B323BB /* Resources */,
 				247E0A722796969200430E5F /* Embed Frameworks */,
 				CDEE3DD129B06E1E00B420E5 /* Embed Foundation Extensions */,
-				AF9BB136451518BD2AD9CF68 /* [CP] Embed Pods Frameworks */,
+				C307E7C3E40CEAF1208EABD2 /* [CP] Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -288,6 +291,7 @@
 			files = (
 				CD9D59DA2BEE1D30008014B4 /* ikn_icon.png in Resources */,
 				CD9D59E32BEE1D30008014B4 /* nu_icon.png in Resources */,
+				CDFD90D12E05361400F1E245 /* GoogleService-Info.plist in Resources */,
 				CD9D59E02BEE1D30008014B4 /* diginets_icon.png in Resources */,
 				CD9D59DD2BEE1D30008014B4 /* nxsport_icon.png in Resources */,
 				CD9D59DC2BEE1D30008014B4 /* nxcook_icon.png in Resources */,
@@ -314,7 +318,7 @@
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
-		A2E40DAFEDA903E5068C358F /* [CP] Check Pods Manifest.lock */ = {
+		5300666D165DA92EBA73470C /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -336,7 +340,7 @@
 			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
 			showEnvVarsInLog = 0;
 		};
-		AF9BB136451518BD2AD9CF68 /* [CP] Embed Pods Frameworks */ = {
+		C307E7C3E40CEAF1208EABD2 /* [CP] Embed Pods Frameworks */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -539,7 +543,7 @@
 		};
 		2401CEC1275490E600B323BB /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 0A806FC385105116A13143A0 /* Pods-AppBuilder.debug.xcconfig */;
+			baseConfigurationReference = 791C498D24080D50B50FF3EE /* Pods-AppBuilder.debug.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
@@ -560,7 +564,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 5.0.44;
+				MARKETING_VERSION = 5.0.45;
 				PRODUCT_BUNDLE_IDENTIFIER = io.nexilis.appbuilder;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
@@ -575,7 +579,7 @@
 		};
 		2401CEC2275490E600B323BB /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 0CEFC760D412D7044F939773 /* Pods-AppBuilder.release.xcconfig */;
+			baseConfigurationReference = 179B641B36103480967D747F /* Pods-AppBuilder.release.xcconfig */;
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
@@ -596,7 +600,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 5.0.44;
+				MARKETING_VERSION = 5.0.45;
 				PRODUCT_BUNDLE_IDENTIFIER = io.nexilis.appbuilder;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
@@ -632,7 +636,7 @@
 					"@executable_path/../../Frameworks",
 				);
 				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
-				MARKETING_VERSION = 5.0.44;
+				MARKETING_VERSION = 5.0.45;
 				OTHER_CFLAGS = "-fstack-protector-strong";
 				PRODUCT_BUNDLE_IDENTIFIER = io.nexilis.appbuilder.AppBuilderShare;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -671,7 +675,7 @@
 					"@executable_path/../../Frameworks",
 				);
 				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
-				MARKETING_VERSION = 5.0.44;
+				MARKETING_VERSION = 5.0.45;
 				OTHER_CFLAGS = "-fstack-protector-strong";
 				PRODUCT_BUNDLE_IDENTIFIER = io.nexilis.appbuilder.AppBuilderShare;
 				PRODUCT_NAME = "$(TARGET_NAME)";

+ 2 - 0
AppBuilder/AppBuilder/AppDelegate.swift

@@ -10,6 +10,7 @@ import NexilisLite
 import NotificationBannerSwift
 import StreamShield
 import PushKit
+import FirebaseCore
 
 @main
 class AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate {
@@ -24,6 +25,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate {
         APIS.connect(appName: appName , apiKey: apikey, delegate: self, showButton: showButton, fromMAB: true) //23091CF494A11149F5A8FC8D17FF690DC69AE656F91B86070A11506ED24144F5(BPKH) //38747683290F62E9667A018F490396EAE47BC16ADECD85B7E865C733E6DBD6A2(OneApp)
         registerForPushNotifications()
         registerForVoIPPushNotifications()
+        FirebaseApp.configure()
         return true
     }
 

+ 17 - 95
AppBuilder/AppBuilder/FourthTabViewController.swift

@@ -72,9 +72,22 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
         tapGesture.delegate = self
         self.view.addGestureRecognizer(tapGesture)
         
+        NotificationCenter.default.addObserver(self, selector: #selector(onRefresh(notification:)), name: NSNotification.Name(rawValue: "onRefreshWebView"), object: nil)
+        
 //        navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(didTapCancel))
     }
     
+    @objc func onRefresh(notification: NSNotification) {
+        DispatchQueue.main.async {
+            self.makeMenu()
+            self.tableView.reloadData()
+            FirstTabViewController.forceRefresh = true
+            ThirdTabViewController.forceRefresh = true
+            FirstTabViewController.showModal = false
+            ThirdTabViewController.showModal = false
+        }
+    }
+    
     @objc func collapseDocked() {
         if ViewController.isExpandButton {
             ViewController.expandButton()
@@ -141,7 +154,7 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
         SecureUserDefaults.shared.set(switchAutoDownload.isOn, forKey: "autoDownload")
     }
     
-    func makeMenu(imageSignIn: String = ""){
+    func makeMenu(){
         let isChangeProfile = Utils.getSetProfile()
         if Database.shared.database == nil {
             Item.menus["Personal"] = [
@@ -201,11 +214,6 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
                                 if FileManager().fileExists(atPath: file.path) {
                                     let image = UIImage(contentsOfFile: file.path)
                                     Item.menus["Personal"]?[0].icon = image?.circleMasked
-                                    if !imageSignIn.isEmpty {
-                                        var dataImage: [AnyHashable : Any] = [:]
-                                        dataImage["name"] = imageSignIn
-                                        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
-                                    }
                                 } else if FileEncryption.shared.isSecureExists(filename: image!) {
                                     do {
                                         if var data = try FileEncryption.shared.readSecure(filename: image!) {
@@ -215,11 +223,6 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
                                             }
                                             let image = UIImage(data: data)
                                             Item.menus["Personal"]?[0].icon = image?.circleMasked
-                                            if !imageSignIn.isEmpty {
-                                                var dataImage: [AnyHashable : Any] = [:]
-                                                dataImage["name"] = imageSignIn
-                                                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
-                                            }
                                         }
                                     } catch {
                                         
@@ -241,11 +244,6 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
                                                         let image = UIImage(data: data)
                                                         Item.menus["Personal"]?[0].icon = image?.circleMasked
                                                         self.tableView.reloadData()
-                                                        if !imageSignIn.isEmpty {
-                                                            var dataImage: [AnyHashable : Any] = [:]
-                                                            dataImage["name"] = imageSignIn
-                                                            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
-                                                        }
                                                     }
                                                 } catch {
                                                     
@@ -258,74 +256,6 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
                         }
                     }
                     cursorUser.close()
-                } else {
-                    Item.menus["Personal"] = [
-                        Item(icon: UIImage(systemName: "person"), title: "Personal Information".localized()),
-                        Item(icon: UIImage(systemName: "textformat.abc"), title: "Change Language".localized()),
-                        Item(icon: UIImage(systemName: "textformat.size"), title: "Chat Font Size".localized()),
-//                        Item(icon: UIImage(systemName: "photo"), title: "Chat Wallpaper".localized()),
-//                        Item(icon: UIImage(systemName: "person.badge.key"), title: "Access Admin / Internal Features".localized()),
-                    ]
-                    Item.menus["Personal"]?.append(Item(icon: UIImage(systemName: "arrow.up.and.person.rectangle.portrait"), title: "Sign-Up/Sign-In".localized()))
-                    if !imageSignIn.isEmpty {
-                        do {
-                            let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
-                            let file = documentDir.appendingPathComponent(imageSignIn)
-                            if FileManager().fileExists(atPath: file.path) {
-                                let image = UIImage(contentsOfFile: file.path)
-                                Item.menus["Personal"]?[0].icon = image?.circleMasked
-                                var dataImage: [AnyHashable : Any] = [:]
-                                dataImage["name"] = imageSignIn
-                                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
-                            } else if FileEncryption.shared.isSecureExists(filename: imageSignIn) {
-                                do {
-                                    if var data = try FileEncryption.shared.readSecure(filename: imageSignIn) {
-                                        let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: data)
-                                        if dataDecrypt != nil {
-                                            data = dataDecrypt!
-                                        }
-                                        let image = UIImage(data: data)
-                                        Item.menus["Personal"]?[0].icon = image?.circleMasked
-                                        if !imageSignIn.isEmpty {
-                                            var dataImage: [AnyHashable : Any] = [:]
-                                            dataImage["name"] = imageSignIn
-                                            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
-                                        }
-                                    }
-                                } catch {
-                                    
-                                }
-                            } else {
-                                Download().startHTTP(forKey: imageSignIn) { (name, progress) in
-                                    guard progress == 100 else {
-                                        return
-                                    }
-                                    DispatchQueue.main.async {
-                                        if FileEncryption.shared.isSecureExists(filename: imageSignIn) {
-                                            do {
-                                                if var data = try FileEncryption.shared.readSecure(filename: imageSignIn) {
-                                                    let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: data)
-                                                    if dataDecrypt != nil {
-                                                        data = dataDecrypt!
-                                                    }
-                                                    let image = UIImage(data: data)
-                                                    Item.menus["Personal"]?[0].icon = image?.circleMasked
-                                                    self.tableView.reloadData()
-                                                    if !imageSignIn.isEmpty {
-                                                        var dataImage: [AnyHashable : Any] = [:]
-                                                        dataImage["name"] = imageSignIn
-                                                        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
-                                                    }
-                                                }
-                                            } catch {
-                                                
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        } catch {}
-                    }
                 }
             })
         }
@@ -392,7 +322,7 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
         navigationController?.navigationBar.tintColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
         tabBarController?.navigationItem.leftBarButtonItem = nil
         tabBarController?.navigationItem.searchController = nil
-        checkBurgerMode()
+//        checkBurgerMode()
         backgroundImage.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .black : .white
         backgroundImage.image = nil
         DispatchQueue.global().async {
@@ -772,20 +702,12 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
         } else if item.title == "Sign-In".localized() {
             let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "changeDevice") as! ChangeDeviceViewController
             controller.isDismiss = { newThumb in
-                self.makeMenu(imageSignIn: newThumb)
+                self.makeMenu()
                 self.tableView.reloadData()
             }
             navigationController?.show(controller, sender: nil)
         } else if item.title == "Sign-Up/Sign-In".localized() {
-            let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
-            controller.isDismiss = { newThumb in
-                self.makeMenu(imageSignIn: newThumb)
-                self.tableView.reloadData()
-                FirstTabViewController.forceRefresh = true
-                ThirdTabViewController.forceRefresh = true
-                FirstTabViewController.showModal = false
-                ThirdTabViewController.showModal = false
-            }
+            guard let controller = APIS.getControllerSign() else { return }
             navigationController?.show(controller, sender: nil)
         } else if item.title == "Sign-Out".localized() {
             let alert = LibAlertController(title: "Sign-Out".localized(), message: "Are you sure want to logout?".localized(), preferredStyle: .alert)

+ 30 - 0
AppBuilder/AppBuilder/GoogleService-Info.plist

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>API_KEY</key>
+	<string>AIzaSyDEQ2l6JZFBRqaiyvL0fkrg7OUJoZCuUus</string>
+	<key>GCM_SENDER_ID</key>
+	<string>724335723932</string>
+	<key>PLIST_VERSION</key>
+	<string>1</string>
+	<key>BUNDLE_ID</key>
+	<string>io.nexilis.appbuilder</string>
+	<key>PROJECT_ID</key>
+	<string>nexilissmsauth</string>
+	<key>STORAGE_BUCKET</key>
+	<string>nexilissmsauth.firebasestorage.app</string>
+	<key>IS_ADS_ENABLED</key>
+	<false></false>
+	<key>IS_ANALYTICS_ENABLED</key>
+	<false></false>
+	<key>IS_APPINVITE_ENABLED</key>
+	<true></true>
+	<key>IS_GCM_ENABLED</key>
+	<true></true>
+	<key>IS_SIGNIN_ENABLED</key>
+	<true></true>
+	<key>GOOGLE_APP_ID</key>
+	<string>1:724335723932:ios:a44ff57702014606beb459</string>
+</dict>
+</plist>

+ 9 - 0
AppBuilder/AppBuilder/Info.plist

@@ -147,6 +147,15 @@
     <string>This app requires Photo Library access for Get File Photos from Local Dictionary for Send Message and Content Creation.</string>
     <key>NSSpeechRecognitionUsageDescription</key>
     <string>This app uses Speech Recognition to convert your voice into text for hands-free dictation and voice commands.</string>
+    <key>CFBundleURLTypes</key>
+    <array>
+        <dict>
+            <key>CFBundleURLSchemes</key>
+            <array>
+                <string>app-1-724335723932-ios-a44ff57702014606beb459</string>
+            </array>
+        </dict>
+    </array>
 	<key>NSUserActivityTypes</key>
 	<array>
 		<string>INSendMessageIntent</string>

+ 12 - 12
AppBuilder/AppBuilder/SecondTabViewController.swift

@@ -1069,9 +1069,6 @@ class SecondTabViewController: UIViewController, UIScrollViewDelegate, UIGesture
     }
     
     private func getOpenGroups(listGroups: [Group], completion: @escaping ([Group]) -> ()) {
-        while Nexilis.isProcessWriteSync {
-            Thread.sleep(forTimeInterval: 0.5)
-        }
         if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getOpenGroups(p_account: "1,2,3,6,5,7", offset: "0", search: "")) {
             var dataGroups: [Group] = []
             if (response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "00") {
@@ -2038,12 +2035,12 @@ extension SecondTabViewController: UITableViewDelegate, UITableViewDataSource {
                     }
                 } else {
                     if data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL || data.isParent || data.pin == "-999" {
-                        getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : (data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL) ? "Profile---Purple" : "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
+                        getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : (data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL) ? "Profile---Purple" : "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
                             imageView.image = image
                         })
                     } else {
                         leadingAnchor = imageView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 40.0)
-                        let image = UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+                        let image = UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
                         imageView.image = image
                     }
                 }
@@ -2329,7 +2326,7 @@ extension SecondTabViewController: UITableViewDelegate, UITableViewDataSource {
                 cell.accessoryType = .none
             }
             content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
-            getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+            getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
                 content.image = image
             }
             cell.contentConfiguration = content
@@ -2612,6 +2609,7 @@ extension SecondTabViewController: UITableViewDelegate, UITableViewDataSource {
     }
     
     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath)
         let data = fillteredData[indexPath.row] as! Chat
         let imgData = data.image
         let vidData = data.video
@@ -2623,8 +2621,11 @@ extension SecondTabViewController: UITableViewDelegate, UITableViewDataSource {
             if selectedTag == PHOTOS_TAG {
                 let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imgData)
                 if FileManager.default.fileExists(atPath: imageURL.path) {
-                    let image = UIImage(contentsOfFile: imageURL.path) ?? UIImage()
-                    APIS.openImageNexilis(image: image)
+                    do {
+                        APIS.openImageNexilis(imageView: (cell.contentView.subviews[0] as? UIImageView) ?? UIImageView(), data: try Data(contentsOf: imageURL))
+                    } catch {
+                        
+                    }
                 } else if FileEncryption.shared.isSecureExists(filename: imgData) {
                     do {
                         if var data = try FileEncryption.shared.readSecure(filename: imgData) {
@@ -2632,8 +2633,7 @@ extension SecondTabViewController: UITableViewDelegate, UITableViewDataSource {
                             if dataDecrypt != nil {
                                 data = dataDecrypt!
                             }
-                            let image = UIImage(data: data) ?? UIImage()
-                            APIS.openImageNexilis(image: image)
+                            APIS.openImageNexilis(imageView: (cell.contentView.subviews[0] as? UIImageView) ?? UIImageView(), data: data)
                         }
                     }
                     catch {
@@ -2682,7 +2682,7 @@ extension SecondTabViewController: UITableViewDelegate, UITableViewDataSource {
                 if FileManager.default.fileExists(atPath: gifURL.path) {
                     do {
                         let data = try Data(contentsOf: gifURL)
-                        APIS.openImageNexilis(image: UIImage(), data: data, isGIF: true)
+                        APIS.openImageNexilis(imageView: (cell.contentView.subviews[0] as? UIImageView) ?? UIImageView(), data: data, isGIF: true)
                     } catch {
                         
                     }
@@ -2693,7 +2693,7 @@ extension SecondTabViewController: UITableViewDelegate, UITableViewDataSource {
                             if dataDecrypt != nil {
                                 secureData = dataDecrypt!
                             }
-                            APIS.openImageNexilis(image: UIImage(), data: secureData, isGIF: true)
+                            APIS.openImageNexilis(imageView: (cell.contentView.subviews[0] as? UIImageView) ?? UIImageView(), data: secureData, isGIF: true)
                         }
                     } catch {
                         

+ 5 - 10
AppBuilder/AppBuilder/ViewController.swift

@@ -427,13 +427,11 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
             }))
             alertChangeProfile.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {(_) in
                 ViewController.resetTabSelected()
-                let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
-                controller.forceLogin = true
-                controller.isDismiss = { newThumb in
-                    FirstTabViewController.forceRefresh = true
-                    ThirdTabViewController.forceRefresh = true
-                    FirstTabViewController.showModal = false
-                    ThirdTabViewController.showModal = false
+                guard let controller = APIS.getControllerSign() else { return }
+                if let controller = controller as? SignUpSignIn {
+                    controller.forceLogin = true
+                } else if let controller = controller as? SignInOption {
+                    controller.forceLogin = true
                 }
                 let navigationController = CustomNavigationController(rootViewController: controller)
                 navigationController.modalPresentationStyle = .fullScreen
@@ -1184,9 +1182,6 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
                     }
                     return
                 }
-                while Nexilis.isProcessWriteSync {
-                    Thread.sleep(forTimeInterval: 0.5)
-                }
                 if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.pullFloatingButton(), timeout: 30 * 1000) {
                     if response.isOk() {
                         let data = response.getBody(key: CoreMessage_TMessageKey.DATA, default_value: "")

BIN
DigiXLite/.DS_Store


BIN
ExampleCode/.DS_Store


+ 1 - 0
NexilisLite/NexilisLite.podspec

@@ -33,6 +33,7 @@ Pod::Spec.new do |spec|
   spec.dependency 'SwiftLinkPreview', '~> 3.4.0'
   spec.dependency 'KeychainAccess', '~> 4.2.2'
   spec.dependency 'Popover', '~> 1.3.0'
+  spec.dependency 'Firebase/Auth', '~> 11.14.0'
 # spec.static_framework = true
 # spec.dependency 'iOS-WebP'
 #  spec.vendored_frameworks = 'nuSDKService.framework'

BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/Attachment.imageset/Attachment.png


+ 0 - 21
NexilisLite/NexilisLite/Resource/Assets.xcassets/Conversation---Purple.imageset/Contents.json

@@ -1,21 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "Conversation---Purple.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}

BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/Conversation---Purple.imageset/Conversation---Purple.png


BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/Q-Button-PNG.imageset/Q-Button-PNG.png


+ 0 - 21
NexilisLite/NexilisLite/Resource/Assets.xcassets/confidential_icon_gray.imageset/Contents.json

@@ -1,21 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "confidential_icon_gray.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}

BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/confidential_icon_gray.imageset/confidential_icon_gray.png


+ 0 - 21
NexilisLite/NexilisLite/Resource/Assets.xcassets/gaspol_icon.imageset/Contents.json

@@ -1,21 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "nexilis_icon.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}

BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/gaspol_icon.imageset/nexilis_icon.png


+ 0 - 21
NexilisLite/NexilisLite/Resource/Assets.xcassets/ic_launcher_pb.imageset/Contents.json

@@ -1,21 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "ic_launcher_pb.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}

BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/ic_launcher_pb.imageset/ic_launcher_pb.png


BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/palio_button.imageset/palio_button.png


+ 0 - 21
NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ball_b.imageset/Contents.json

@@ -1,21 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "ic_launcher_fit.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}

BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ball_b.imageset/ic_launcher_fit.png


BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_fb_bni2.imageset/pb_fb_bni2.png


+ 0 - 21
NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_fb_expora2.imageset/Contents.json

@@ -1,21 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "pb_fb_expora2.png",
-      "idiom" : "universal",
-      "scale" : "1x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "2x"
-    },
-    {
-      "idiom" : "universal",
-      "scale" : "3x"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  }
-}

BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_fb_expora2.imageset/pb_fb_expora2.png


+ 1 - 1
NexilisLite/NexilisLite/Resource/Assets.xcassets/Q-Button-PNG.imageset/Contents.json → NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ic_msisdn.imageset/Contents.json

@@ -1,7 +1,7 @@
 {
   "images" : [
     {
-      "filename" : "Q-Button-PNG.png",
+      "filename" : "pb_ic_msisdn.png",
       "idiom" : "universal",
       "scale" : "1x"
     },

BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ic_msisdn.imageset/pb_ic_msisdn.png


+ 1 - 1
NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_fb_bni2.imageset/Contents.json → NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ic_name.imageset/Contents.json

@@ -1,7 +1,7 @@
 {
   "images" : [
     {
-      "filename" : "pb_fb_bni2.png",
+      "filename" : "pb_ic_name.png",
       "idiom" : "universal",
       "scale" : "1x"
     },

BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_ic_name.imageset/pb_ic_name.png


+ 1 - 1
NexilisLite/NexilisLite/Resource/Assets.xcassets/palio_button.imageset/Contents.json → NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_stg_email.imageset/Contents.json

@@ -1,7 +1,7 @@
 {
   "images" : [
     {
-      "filename" : "palio_button.png",
+      "filename" : "pb_stg_email.png",
       "idiom" : "universal",
       "scale" : "1x"
     },

BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_stg_email.imageset/pb_stg_email.png


+ 1 - 1
NexilisLite/NexilisLite/Resource/Assets.xcassets/Attachment.imageset/Contents.json → NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_user.imageset/Contents.json

@@ -1,7 +1,7 @@
 {
   "images" : [
     {
-      "filename" : "Attachment.png",
+      "filename" : "pb_user.png",
       "idiom" : "universal",
       "scale" : "1x"
     },

BIN
NexilisLite/NexilisLite/Resource/Assets.xcassets/pb_user.imageset/pb_user.png


+ 5 - 3
NexilisLite/NexilisLite/Resource/Palio.storyboard

@@ -3129,7 +3129,7 @@
                                     <constraint firstAttribute="height" constant="2" id="0as-Zh-jRl"/>
                                 </constraints>
                             </view>
-                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="pb_cd_person" translatesAutoresizingMaskIntoConstraints="NO" id="uMG-xq-HNf">
+                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="pb_user" translatesAutoresizingMaskIntoConstraints="NO" id="uMG-xq-HNf">
                                 <rect key="frame" x="87" y="136" width="240" height="128"/>
                                 <constraints>
                                     <constraint firstAttribute="width" constant="240" id="PBy-BT-r38"/>
@@ -3182,7 +3182,6 @@
                             <constraint firstItem="RRi-TQ-BYu" firstAttribute="trailing" secondItem="OGC-mO-8T0" secondAttribute="trailing" constant="20" id="P6s-86-sIR"/>
                             <constraint firstItem="Q4b-oV-sHi" firstAttribute="top" secondItem="uMG-xq-HNf" secondAttribute="bottom" constant="10" id="RzA-zE-Hrr"/>
                             <constraint firstItem="icL-yb-DMM" firstAttribute="leading" secondItem="RRi-TQ-BYu" secondAttribute="leading" constant="40" id="VGd-8t-NGv"/>
-                            <constraint firstItem="icL-yb-DMM" firstAttribute="top" secondItem="5jw-G4-iGy" secondAttribute="bottom" constant="30" id="i5h-FB-69z"/>
                             <constraint firstItem="RRi-TQ-BYu" firstAttribute="trailing" secondItem="5jw-G4-iGy" secondAttribute="trailing" constant="20" id="iNS-JR-wnP"/>
                             <constraint firstAttribute="trailingMargin" secondItem="Q4b-oV-sHi" secondAttribute="trailing" constant="20" id="kmp-rV-hmS"/>
                             <constraint firstItem="RRi-TQ-BYu" firstAttribute="trailing" secondItem="icL-yb-DMM" secondAttribute="trailing" constant="40" id="lPZ-ru-Zxi"/>
@@ -3191,6 +3190,7 @@
                             <constraint firstItem="OGC-mO-8T0" firstAttribute="leading" secondItem="RRi-TQ-BYu" secondAttribute="leading" constant="20" id="qxd-Nm-RjI"/>
                             <constraint firstItem="kwq-ci-Qkk" firstAttribute="leading" secondItem="RRi-TQ-BYu" secondAttribute="leading" constant="40" id="wUA-gp-1pR"/>
                             <constraint firstItem="uMG-xq-HNf" firstAttribute="centerX" secondItem="j0h-JE-i7E" secondAttribute="centerX" id="z3S-Pf-Oua"/>
+                            <constraint firstItem="icL-yb-DMM" firstAttribute="top" secondItem="OGC-mO-8T0" secondAttribute="bottom" constant="74" id="zNR-bd-1S8"/>
                         </constraints>
                     </view>
                     <connections>
@@ -3198,12 +3198,13 @@
                         <outlet property="descSignUpSignIn" destination="kwq-ci-Qkk" id="z3Q-k3-wbp"/>
                         <outlet property="passwordField" destination="5jw-G4-iGy" id="o5r-JE-N89"/>
                         <outlet property="showPasswordButton" destination="SAR-sw-Oq9" id="xhD-bu-DFc"/>
+                        <outlet property="topConstDesc" destination="zNR-bd-1S8" id="xlR-2H-KBg"/>
                         <outlet property="usernameField" destination="OGC-mO-8T0" id="kOP-TB-DFc"/>
                     </connections>
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="mnb-fv-1Pu" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
             </objects>
-            <point key="canvasLocation" x="4672" y="1443"/>
+            <point key="canvasLocation" x="4671.0144927536239" y="1442.4107142857142"/>
         </scene>
         <!--Seminar List View Controller-->
         <scene sceneID="9jc-wF-EYe">
@@ -3446,6 +3447,7 @@
         <image name="mic.slash.fill" catalog="system" width="108" height="128"/>
         <image name="pb_cd_person" width="512" height="512"/>
         <image name="pb_seminar_speaking" width="512" height="512"/>
+        <image name="pb_user" width="556" height="449"/>
         <image name="pdf-icon" width="860" height="901"/>
         <image name="pencil" catalog="system" width="128" height="113"/>
         <image name="person.badge.plus.fill" catalog="system" width="128" height="126"/>

+ 106 - 28
NexilisLite/NexilisLite/Source/APIS.swift

@@ -17,6 +17,7 @@ import Intents
 
 public class APIS: NSObject {
     private static var isAlertPresented = false
+    private static var transitioningDelegateRef: ZoomTransitioningDelegate?
     public static func connect(appName: String, apiKey: String, delegate: ConnectDelegate, showButton: Bool = true, fromMAB: Bool = false) {
         APIS.appNm = appName.trimmingCharacters(in: .whitespacesAndNewlines)
         Nexilis.connect(apiKey: apiKey, delegate: delegate, showButton: showButton, fromMAB: fromMAB)
@@ -63,8 +64,12 @@ public class APIS: NSObject {
         let alert = LibAlertController(title: "Set Profile".localized(), message: "You must set your profile to use this feature".localized(), preferredStyle: .alert)
         alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {(_) in
             isAlertPresented = false
-            let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
-            controller.forceLogin = true
+            guard let controller = APIS.getControllerSign() else { return }
+            if let controller = controller as? SignUpSignIn {
+                controller.forceLogin = true
+            } else if let controller = controller as? SignInOption {
+                controller.forceLogin = true
+            }
             let navigationController = CustomNavigationController(rootViewController: controller)
             navigationController.defaultStyle()
             if UIApplication.shared.visibleViewController?.navigationController != nil {
@@ -651,8 +656,12 @@ public class APIS: NSObject {
             APIS.showChangeProfile()
             return
         }
-        let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
-        controller.forceLogin = true
+        guard let controller = APIS.getControllerSign() else { return }
+        if let controller = controller as? SignUpSignIn {
+            controller.forceLogin = true
+        } else if let controller = controller as? SignInOption {
+            controller.forceLogin = true
+        }
         let navigationController = CustomNavigationController(rootViewController: controller)
         navigationController.defaultStyle()
         if UIApplication.shared.visibleViewController?.navigationController != nil {
@@ -1702,20 +1711,6 @@ public class APIS: NSObject {
         checkDataForShareExtension()
         UIApplication.shared.applicationIconBadgeNumber = 0
         UNUserNotificationCenter.current().removeAllDeliveredNotifications()
-//        DispatchQueue.global().async {
-//            while API.nGetCLXConnState() == 0 {
-//                Thread.sleep(forTimeInterval: 0.5)
-//            }
-//            if let vers = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.checkVersion()) {
-//                let dataVersion = vers.getBody(key: CoreMessage_TMessageKey.DATA)
-//                let type = vers.getBody(key: CoreMessage_TMessageKey.TYPE)
-//                if dataVersion != "1" {
-//                    DispatchQueue.main.async {
-//                        showExpiredVersion(mandatory: type == "1")
-//                    }
-//                }
-//            }
-//        }
         if Utils.getSecureFolderOffline() == "0" && afterEnterBackground && Database.shared.database == nil && Utils.getSetProfile() {
             Database.recreateInstance()
             NotificationCenter.default.post(name: NSNotification.Name(rawValue: "disconnected_nexilis"), object: nil, userInfo: nil)
@@ -1727,6 +1722,22 @@ public class APIS: NSObject {
 //            Nexilis.getFeatureAccessWithKey(key: ["secure_folder_encrypt_key", "secure_folder_encrypt_iv", "secure_folder_offline"])
             Nexilis.getFeatureAccess()
         }
+        if FloatingButton.datePull == nil || !afterEnterBackground {
+            DispatchQueue.global().async {
+                while API.nGetCLXConnState() == 0 || User.getMyPin() == nil {
+                    Thread.sleep(forTimeInterval: 0.5)
+                }
+                if let vers = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.checkVersion()) {
+                    let dataVersion = vers.getBody(key: CoreMessage_TMessageKey.DATA)
+                    let type = vers.getBody(key: CoreMessage_TMessageKey.TYPE)
+                    if dataVersion != "1" {
+                        DispatchQueue.main.async {
+                            showExpiredVersion(mandatory: type == "1")
+                        }
+                    }
+                }
+            }
+        }
         afterEnterBackground = true
     }
     
@@ -2280,18 +2291,43 @@ public class APIS: NSObject {
         nameGroupShared = name
     }
     
-    public static func openImageNexilis(image: UIImage, data: Data? = nil, isGIF: Bool = false) {
-        let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
-        previewImageVC.image = image
-        previewImageVC.isHiddenTextField = true
-        previewImageVC.isGIF = isGIF
-        previewImageVC.dataGIF = data
-        previewImageVC.modalPresentationStyle = .custom
-        previewImageVC.modalTransitionStyle  = .crossDissolve
+    public static func openImageNexilis(imageView: UIImageView, data: Data? = nil, isGIF: Bool = false) {
+        let image = UIImage(data: data ?? Data())
+        let imageViewer = MediaViewerViewController()
+        if !isGIF {
+            imageViewer.media = .image(image ?? UIImage())
+        } else {
+            imageViewer.media = .gif(UIImage.gifImageWithData(data ?? Data()) ?? UIImage())
+        }
+        
+        let navigationController = UINavigationController(rootViewController: imageViewer)
+        navigationController.defaultStyle()
+        navigationController.view.backgroundColor = .clear
+        navigationController.modalPresentationCapturesStatusBarAppearance = true
+        navigationController.modalPresentationStyle = .overFullScreen
+        
+        let backAction = UIAction { _ in
+            navigationController.dismiss(animated: true)
+        }
+        let backButton = UIBarButtonItem(title: nil, image: UIImage(systemName: "chevron.backward"), primaryAction: backAction, menu: nil)
+        imageViewer.navigationItem.leftBarButtonItem = backButton
+        
+        let name = ""
+        imageViewer.title = name
+        
+        let transitionDelegate = ZoomTransitioningDelegate()
+        transitionDelegate.originImageView = imageView
+        navigationController.transitioningDelegate = transitionDelegate
+        self.transitioningDelegateRef = transitionDelegate
+        
         if UIApplication.shared.visibleViewController?.navigationController != nil {
-            UIApplication.shared.visibleViewController?.navigationController?.present(previewImageVC, animated: true, completion: nil)
+            UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true) {
+                imageViewer.animateBackgroundIn()
+            }
         } else {
-            UIApplication.shared.visibleViewController?.present(previewImageVC, animated: true, completion: nil)
+            UIApplication.shared.visibleViewController?.present(navigationController, animated: true) {
+                imageViewer.animateBackgroundIn()
+            }
         }
     }
     
@@ -2307,6 +2343,48 @@ public class APIS: NSObject {
         }
     }
     
+    public static func checkSignMethod() -> (Int, Int) {
+        var countMethod = 0
+        var typeMethod = 0
+        if Nexilis.checkingAccess(key: "sign_in_up_msisdn") {
+            countMethod+=1
+            typeMethod = 0
+        }
+        if Nexilis.checkingAccess(key: "sign_in_up_email") {
+            countMethod+=1
+            typeMethod = 1
+        }
+        if Nexilis.checkingAccess(key: "sign_in_up_username") {
+            countMethod+=1
+            typeMethod = 2
+        }
+        return (countMethod,typeMethod)
+    }
+    
+    public static func getControllerSign() -> UIViewController? {
+        let data = APIS.checkSignMethod()
+        let count = data.0
+        let type = data.1
+        if count > 0 {
+            var controller: UIViewController!
+            if count == 1 {
+                let vc = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
+                if type == 0 {
+                    vc.isMSISDN = true
+                    controller = vc
+                } else if type == 1 {
+                    vc.isEmail = true
+                }
+                controller = vc
+            } else {
+                let vc = SignInOption()
+                controller = vc
+            }
+            return controller
+        }
+        return nil
+    }
+    
     private static var appNm = "";
     public static func getAppNm() -> String {
         return appNm

+ 40 - 2
NexilisLite/NexilisLite/Source/CoreMessage_TMessageBank.swift

@@ -1425,7 +1425,7 @@ public class CoreMessage_TMessageBank {
         return tmessage
     }
 
-    public static func getSendVerifyChangeDevice(p_email: String, p_vercode: String) -> TMessage {
+    public static func getSendVerifyChangeDevice(p_email: String, p_vercode: String, number: String = "", deviceFingerprint: String = "", publicKey: String = "", signature: String = "") -> TMessage {
         let me = User.getMyPin()!
         let tmessage = TMessage()
         tmessage.mCode = CoreMessage_TMessageCode.SEND_VERIFY_LOGIN
@@ -1433,6 +1433,16 @@ public class CoreMessage_TMessageBank {
         tmessage.mPIN = me
         tmessage.mBodies[CoreMessage_TMessageKey.EMAIL] = p_email
         tmessage.mBodies[CoreMessage_TMessageKey.OTP] = p_vercode
+        tmessage.mBodies[CoreMessage_TMessageKey.PHONE_NUMBER] = number
+        if !deviceFingerprint.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.FINGERPRINT] = deviceFingerprint
+        }
+        if !publicKey.isEmpty{
+            tmessage.mBodies[CoreMessage_TMessageKey.PUBLIC_KEY] = publicKey
+        }
+        if !signature.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.SIGNATURE] = signature
+        }
         return tmessage;
     }
     
@@ -2295,7 +2305,7 @@ public class CoreMessage_TMessageBank {
         return tmessage
     }
     
-    public static func getSignUpSignInAPI(p_name: String, p_password: String) -> TMessage {
+    public static func getSignUpSignInAPI(p_name: String, p_password: String, deviceFingerprint: String = "", publicKey: String = "", signature: String = "") -> TMessage {
         let tmessage = TMessage()
         tmessage.mCode = CoreMessage_TMessageCode.SIGN_UP_AND_SIGN_IN_API
         tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
@@ -2306,6 +2316,15 @@ public class CoreMessage_TMessageBank {
         tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_APP_NAME] = APIS.getAppNm()
         tmessage.mBodies[CoreMessage_TMessageKey.CPAAS_VERSION] = Utils.CPAAS_VERSION
         tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_PACKAGE_NAME] = (Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String) ?? ""
+        if !deviceFingerprint.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.FINGERPRINT] = deviceFingerprint
+        }
+        if !publicKey.isEmpty{
+            tmessage.mBodies[CoreMessage_TMessageKey.PUBLIC_KEY] = publicKey
+        }
+        if !signature.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.SIGNATURE] = signature
+        }
         return tmessage
     }
     
@@ -2727,4 +2746,23 @@ public class CoreMessage_TMessageBank {
         return tMessage
     }
     
+    public static func getChalanger() -> TMessage {
+        let tMessage = NexilisLite.TMessage()
+        let me = User.getMyPin() ?? ""
+        tMessage.mPIN = me
+        tMessage.mCode = CoreMessage_TMessageCode.AUTH_REQUEST
+        tMessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        return tMessage
+    }
+    
+    public static func getCheckMSISDN(number: String) -> TMessage {
+        let tMessage = NexilisLite.TMessage()
+        let me = User.getMyPin() ?? ""
+        tMessage.mPIN = me
+        tMessage.mCode = CoreMessage_TMessageCode.CHECK_USER_MSISDN
+        tMessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tMessage.mBodies[CoreMessage_TMessageKey.PHONE_NUMBER] = number
+        return tMessage
+    }
+    
 }

+ 4 - 0
NexilisLite/NexilisLite/Source/CoreMessage_TMessageCode.swift

@@ -808,4 +808,8 @@ public class CoreMessage_TMessageCode {
     public static let GET_PUSH_PREFS = "GPR";
     public static let UPDATE_VERSION = "UPV";
     public static let GET_WHITELIST = "GWL";
+    
+    public static let AUTH_REQUEST = "AR01";
+    public static let CHECK_USER_MSISDN = "CUM1";
+
 }

+ 3 - 0
NexilisLite/NexilisLite/Source/CoreMessage_TMessageKey.swift

@@ -502,4 +502,7 @@ public class CoreMessage_TMessageKey {
     public static let IS_DELETED_RETENTION = "idl";
     public static let IS_FORWARDED_MESSAGE = "ifm";
     
+    public static let FINGERPRINT = "FPR";
+    public static let SIGNATURE = "SIG";
+    public static let PUBLIC_KEY = "PUK";
 }

+ 17 - 15
NexilisLite/NexilisLite/Source/Extension.swift

@@ -1425,13 +1425,14 @@ public class ImageCache {
         let imageName = "\(sanitizedKey).png"
         if FileEncryption.shared.isSecureExists(filename: imageName) {
             do {
-                var data = try FileEncryption.shared.readSecure(filename: imageName)
-                if let decrypted = FileEncryption.shared.decryptFileFromServer(data: data!) {
-                    data = decrypted
-                }
-                if let image = UIImage(data: data!) {
-                    cache.setObject(image, forKey: sanitizedKey as NSString)
-                    return image
+                if var data = try FileEncryption.shared.readSecure(filename: imageName) {
+                    if let decrypted = FileEncryption.shared.decryptFileFromServer(data: data) {
+                        data = decrypted
+                    }
+                    if let image = UIImage(data: data) {
+                        cache.setObject(image, forKey: sanitizedKey as NSString)
+                        return image
+                    }
                 }
             } catch {
                 print("Failed to read or decrypt image from disk: \(error)")
@@ -1498,15 +1499,16 @@ public class ImageCache {
             guard FileEncryption.shared.isSecureExists(filename: fileName) else { continue }
 
             do {
-                var data = try FileEncryption.shared.readSecure(filename: fileName)
-                if let decrypted = FileEncryption.shared.decryptFileFromServer(data: data!) {
-                    data = decrypted
-                }
+                if var data = try FileEncryption.shared.readSecure(filename: fileName) {
+                    if let decrypted = FileEncryption.shared.decryptFileFromServer(data: data) {
+                        data = decrypted
+                    }
 
-                if isGif {
-                    cacheGif.setObject(data! as NSData, forKey: sanitizedKey as NSString)
-                } else if let image = UIImage(data: data!) {
-                    cache.setObject(image, forKey: sanitizedKey as NSString)
+                    if isGif {
+                        cacheGif.setObject(data as NSData, forKey: sanitizedKey as NSString)
+                    } else if let image = UIImage(data: data) {
+                        cache.setObject(image, forKey: sanitizedKey as NSString)
+                    }
                 }
             } catch {
                 print("Error loading \(fileExtension) from disk: \(error)")

+ 244 - 0
NexilisLite/NexilisLite/Source/MasterKeyUtil.swift

@@ -173,3 +173,247 @@ public class MasterKeyUtil {
         return try AES.GCM.open(sealedBox, using: key)
     }
 }
+
+class StableDeviceFingerprint {
+    static func generate() -> String {
+        let device = UIDevice.current
+        let screen = UIScreen.main
+        let vendorId = device.identifierForVendor?.uuidString ?? "unknown"
+        return [
+            device.model,
+            device.name,
+            device.systemName,
+            vendorId,
+            "\(screen.bounds.width)x\(screen.bounds.height)"
+        ].joined(separator: "|")
+    }
+}
+
+class KeychainHelper {
+    static func save(key: String, data: Data) {
+        let query: [String: Any] = [
+            kSecClass as String: kSecClassKey,
+            kSecAttrApplicationTag as String: key,
+            kSecValueData as String: data,
+            kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
+        ]
+        SecItemDelete(query as CFDictionary)
+        SecItemAdd(query as CFDictionary, nil)
+    }
+    static func load(key: String) -> Data? {
+        let query: [String: Any] = [
+            kSecClass as String: kSecClassKey,
+            kSecAttrApplicationTag as String: key,
+            kSecReturnData as String: true,
+            kSecMatchLimit as String: kSecMatchLimitOne
+        ]
+        var result: AnyObject?
+        SecItemCopyMatching(query as CFDictionary, &result)
+        return result as? Data
+    }
+}
+
+class AppSecretManager {
+    static func getOrCreateKey() -> SymmetricKey {
+        let keyTag = "io.nexilis.device.key.\(Bundle.main.infoDictionary?["CFBundleName"] as! String)"
+        if let keyData = KeychainHelper.load(key: keyTag) {
+            return SymmetricKey(data: keyData)
+        } else {
+            let key = SymmetricKey(size: .bits256)
+            let keyData = key.withUnsafeBytes { Data($0) }
+            KeychainHelper.save(key: keyTag, data: keyData)
+            return key
+        }
+    }
+}
+
+class HMACDeviceFingerprintNexilis {
+    static func generate() -> String {
+        let raw = StableDeviceFingerprint.generate()
+        let key = AppSecretManager.getOrCreateKey()
+        let mac = HMAC<SHA256>.authenticationCode(for: raw.data(using: .utf8)!, using: key)
+        return Data(mac).base64EncodedString()
+    }
+}
+
+class KeyManagerNexilis {
+    static let tag = "io.nexilis.fido2.key.\(Bundle.main.infoDictionary?["CFBundleName"] as! String)".data(using: .utf8)!
+    static let keyMarkerTag = "io.nexilis.fido2.key.\(Bundle.main.infoDictionary?["CFBundleName"] as! String).marker".data(using: .utf8)!
+    static let markerAccount = "nexilis.key.\(Bundle.main.infoDictionary?["CFBundleName"] as! String).marker"
+    static func generateKey() {
+        let accessControl = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.userPresence], nil)!
+
+        let attributes: [String: Any] = [
+            kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
+            kSecAttrKeySizeInBits as String: 2048,
+            kSecPrivateKeyAttrs as String: [
+                kSecAttrIsPermanent as String: true,
+                kSecAttrApplicationTag as String: tag,
+                kSecAttrAccessControl as String: accessControl
+            ]
+        ]
+
+        var error: Unmanaged<CFError>?
+        guard SecKeyCreateRandomKey(attributes as CFDictionary, &error) != nil else {
+            print("Failed to generate RSA key: \(String(describing: error))")
+            return
+        }
+
+        print("RSA key and marker generated successfully.")
+        return
+    }
+    
+    static func saveMarker() {
+        let deleteQuery: [String: Any] = [
+            kSecClass as String: kSecClassGenericPassword,
+            kSecAttrAccount as String: markerAccount
+        ]
+        SecItemDelete(deleteQuery as CFDictionary) // Clean up if exists
+
+        let addQuery: [String: Any] = [
+            kSecClass as String: kSecClassGenericPassword,
+            kSecAttrAccount as String: markerAccount,
+            kSecValueData as String: Data([1]),
+            kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
+        ]
+        
+        let status = SecItemAdd(addQuery as CFDictionary, nil)
+        print("Marker save status: \(status)") // Should be 0
+    }
+    
+    static func getPublicKey(privateKey: SecKey) -> SecKey? {
+        return SecKeyCopyPublicKey(privateKey)
+    }
+    
+    static func getRSAX509PublicKeyBase64(privateKey: SecKey) -> String? {
+        guard let publicKey = getPublicKey(privateKey: privateKey) else {
+            print("No public key available")
+            return nil
+        }
+
+        var error: Unmanaged<CFError>?
+        guard let pubKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
+            print("Failed to extract public key: \(String(describing: error))")
+            return nil
+        }
+
+        // X.509 header for RSA 2048 (OID: 1.2.840.113549.1.1.1 for RSA encryption)
+        let rsaOIDHeader: [UInt8] = [
+            0x30, 0x82, // SEQUENCE
+            // ... we'll calculate length dynamically
+        ]
+
+        // Standard ASN.1 header for RSA public key
+        let rsaAlgorithmIdentifier: [UInt8] = [
+            0x30, 0x0D,
+            0x06, 0x09,
+            0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, // OID: 1.2.840.113549.1.1.1
+            0x05, 0x00 // NULL
+        ]
+
+        // Wrap raw key inside BIT STRING
+        let pubKeyBitStringPrefix: [UInt8] = [0x03] // BIT STRING
+        let pubKeyBitString = [0x00] + [UInt8](pubKeyData) // prepend 0x00 for padding
+
+        let bitStringLength = pubKeyBitString.count
+        let fullPubKeyBitString = pubKeyBitStringPrefix + encodeASN1Length(bitStringLength) + pubKeyBitString
+
+        let algorithmBlock = rsaAlgorithmIdentifier
+        let subjectPublicKeyInfo = [0x30] + encodeASN1Length(algorithmBlock.count + fullPubKeyBitString.count) +
+            algorithmBlock + fullPubKeyBitString
+
+        let finalData = Data(subjectPublicKeyInfo)
+        return finalData.base64EncodedString()
+    }
+    
+    private static func encodeASN1Length(_ length: Int) -> [UInt8] {
+        if length < 128 {
+            return [UInt8(length)]
+        }
+
+        var len = length
+        var bytes: [UInt8] = []
+        while len > 0 {
+            bytes.insert(UInt8(len & 0xFF), at: 0)
+            len = len >> 8
+        }
+
+        return [0x80 | UInt8(bytes.count)] + bytes
+    }
+    
+    static func getPrivateKey() -> SecKey? {
+        let context = LAContext()
+        context.localizedReason = "Verify your identity to continue with login.".localized()
+
+        var error: NSError?
+        guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else {
+            print("Biometric auth not available: \(String(describing: error))")
+            return nil
+        }
+
+        let query: [String: Any] = [
+            kSecClass as String: kSecClassKey,
+            kSecAttrApplicationTag as String: tag,
+            kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
+            kSecReturnRef as String: true,
+            kSecUseAuthenticationContext as String: context
+        ]
+
+        var item: CFTypeRef?
+        let status = SecItemCopyMatching(query as CFDictionary, &item)
+        guard status == errSecSuccess, let key = item else {
+            print("Private key not found. Status: \(status)")
+            return nil
+        }
+
+        return (key as! SecKey)
+    }
+    
+    static func deleteKey() {
+        let keyQuery: [String: Any] = [
+            kSecClass as String: kSecClassKey,
+            kSecAttrApplicationTag as String: tag
+        ]
+        SecItemDelete(keyQuery as CFDictionary)
+    }
+    
+    static func hasGeneratedKey() -> Bool {
+        let query: [String: Any] = [
+            kSecClass as String: kSecClassGenericPassword,
+            kSecAttrAccount as String: markerAccount,
+            kSecReturnData as String: false,
+            kSecMatchLimit as String: kSecMatchLimitOne
+        ]
+
+        let status = SecItemCopyMatching(query as CFDictionary, nil)
+        return status == errSecSuccess
+    }
+    
+    static func deleteMarker() {
+        let deleteQuery: [String: Any] = [
+            kSecClass as String: kSecClassGenericPassword,
+            kSecAttrAccount as String: markerAccount
+        ]
+        SecItemDelete(deleteQuery as CFDictionary)
+    }
+    
+    static func sign(data: Data, privateKey: SecKey) -> Data? {
+        let algorithm = SecKeyAlgorithm.rsaSignatureMessagePKCS1v15SHA256
+
+        guard SecKeyIsAlgorithmSupported(privateKey, .sign, algorithm) else {
+            print("Algorithm not supported for this key.")
+            return nil
+        }
+
+        var error: Unmanaged<CFError>?
+        guard let signature = SecKeyCreateSignature(privateKey,
+                                                    algorithm,
+                                                    data as CFData,
+                                                    &error) as Data? else {
+            print("Failed to sign: \(String(describing: error))")
+            return nil
+        }
+
+        return signature
+    }
+}

+ 48 - 70
NexilisLite/NexilisLite/Source/Nexilis.swift

@@ -19,7 +19,7 @@ import CryptoKit
 import WebKit
 
 public class Nexilis: NSObject {
-    public static var cpaasVersion = "5.0.44"
+    public static var cpaasVersion = "5.0.45"
     public static var sAPIKey = ""
     
     public static var ADDRESS = ""
@@ -448,9 +448,6 @@ public class Nexilis: NSObject {
     }
     
     private static func getPullGroupNoMember() {
-        while Nexilis.isProcessWriteSync {
-            Thread.sleep(forTimeInterval: 0.5)
-        }
         if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.pullGroupNoMember(), timeout: 30 * 1000), response.isOk() {
             let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
             if !data.isEmpty {
@@ -526,20 +523,7 @@ public class Nexilis: NSObject {
         }
         isGettingFeatureAccess = true
         DispatchQueue.global().async {
-//            Utils.postDataWithCookiesAndUserAgent(from: URL(string: Utils.getDomainOpr() + "get_feature_access_new")!) { data, response, error in
-//                let response = response as? HTTPURLResponse
-//                if response?.statusCode != 200 || error != nil {
-//                    return
-//                }
-//                if let data = data, let responseString = String(data: data, encoding: .utf8) {
-//                    if let jsonArray = try? JSONSerialization.jsonObject(with: responseString.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
-//                    }
-//                }
-//            }
-            while Nexilis.isProcessWriteSync {
-                Thread.sleep(forTimeInterval: 0.5)
-            }
-            if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getFeatureAccessAll(), timeout: 5000), response.isOk() {
+            if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getFeatureAccessAll(), timeout: 10000), response.isOk() {
                 let data = response.getBody(key: CoreMessage_TMessageKey.DATA, default_value: "[]")
                 do {
                     if let data = data.data(using: .utf8) {
@@ -657,7 +641,7 @@ public class Nexilis: NSObject {
     public static func checkingAccess(key: String) -> Bool {
         let dataAccess = Utils.getFeatureAccess()
         if dataAccess.isEmpty {
-            if key == "sms" || key == "email" || key == "whatsapp" || key == "battery_optimization_force" || key == "backup_restore" || key == "check_sim_swap" || key == "admin_features" || key == "can_config_fb" || key == "friend_request_approval" || key == "authentication" {
+            if key == "sms" || key == "email" || key == "whatsapp" || key == "battery_optimization_force" || key == "backup_restore" || key == "check_sim_swap" || key == "admin_features" || key == "can_config_fb" || key == "friend_request_approval" || key == "authentication" || key == "sign_in_up_msisdn" || key == "sign_in_up_email" {
                 return false
             } else {
                 return true
@@ -666,7 +650,7 @@ public class Nexilis: NSObject {
             if jsonArray[key] != nil {
                 return jsonArray[key] as! String == "1"
             } else {
-                if key == "sms" || key == "email" || key == "whatsapp" || key == "battery_optimization_force" || key == "backup_restore" || key == "check_sim_swap" || key == "admin_features" || key == "can_config_fb" || key == "friend_request_approval" || key == "authentication" {
+                if key == "sms" || key == "email" || key == "whatsapp" || key == "battery_optimization_force" || key == "backup_restore" || key == "check_sim_swap" || key == "admin_features" || key == "can_config_fb" || key == "friend_request_approval" || key == "authentication" || key == "sign_in_up_email" {
                     return false
                 } else {
                     return true
@@ -699,9 +683,6 @@ public class Nexilis: NSObject {
     }
     
     private static func getPullWorkingArea() {
-        while Nexilis.isProcessWriteSync {
-            Thread.sleep(forTimeInterval: 0.5)
-        }
         if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getWorkingAreaContactCenter(), timeout: 30 * 1000), response.isOk() {
             let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
             if !data.isEmpty {
@@ -1126,12 +1107,15 @@ public class Nexilis: NSObject {
         if !API.bInetConnAvailable() {
             return nil
         }
+        while Nexilis.isProcessWriteSync {
+            Thread.sleep(forTimeInterval: 0.5)
+        }
         isProcessWriteSync = true
         do {
-//            print(">> SENDING MESSAGE >> ", message.toLogString())
+//            print(">> SENDING MESSAGE >> ", message.getCode())
             if let data = try API.sGetResponse(sRequest: message.pack(), lTimeout: timeout, bKeepTOResp: true) {
                 let response = TMessage(data: data)
-//                print("<< RESPONSE MESSAGE << ", data)
+//                print("<< RESPONSE MESSAGE << ", response.getCode())
                 isProcessWriteSync = false
                 return response
             }
@@ -1294,8 +1278,12 @@ public class Nexilis: NSObject {
         if !isChangeProfile {
             let alert = LibAlertController(title: "Set Profile".localized(), message: "You must set your profile to use this feature".localized(), preferredStyle: .alert)
             alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {(_) in
-                let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
-                controller.forceLogin = true
+                guard let controller = APIS.getControllerSign() else { return }
+                if let controller = controller as? SignUpSignIn {
+                    controller.forceLogin = true
+                } else if let controller = controller as? SignInOption {
+                    controller.forceLogin = true
+                }
                 let navigationController = CustomNavigationController(rootViewController: controller)
                 navigationController.modalPresentationStyle = .fullScreen
                 navigationController.navigationBar.tintColor = .white
@@ -1364,8 +1352,12 @@ public class Nexilis: NSObject {
             alertChangeProfile = LibAlertController(title: "Set Profile".localized(), message: "You must set your profile to use this feature".localized(), preferredStyle: .alert)
             alertChangeProfile.addAction(UIAlertAction(title: "Cancel".localized(), style: .destructive, handler: nil))
             alertChangeProfile.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {(_) in
-                let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
-                controller.forceLogin = true
+                guard let controller = APIS.getControllerSign() else { return }
+                if let controller = controller as? SignUpSignIn {
+                    controller.forceLogin = true
+                } else if let controller = controller as? SignInOption {
+                    controller.forceLogin = true
+                }
                 let navigationController = CustomNavigationController(rootViewController: controller)
                 navigationController.modalPresentationStyle = .fullScreen
                 navigationController.navigationBar.tintColor = .white
@@ -1419,8 +1411,10 @@ public class Nexilis: NSObject {
         let groupWait = listDispatchGroups[message.getStatus()]
         groupWait?.enter()
         waitQueue[message.getStatus()] = message
+//        print("wandw req: \(message.getCode())")
         _ = write(message: message, timeout: timeout)
         if groupWait?.wait(timeout: .now() + 15) == .timedOut {
+//            print("wandw timedOut: \(message.getCode())")
             waitQueue.removeValue(forKey: message.getStatus())
             listDispatchGroups.removeValue(forKey: message.getStatus())
             groupWait?.leave()
@@ -1477,6 +1471,7 @@ public class Nexilis: NSObject {
         }
         message.mBodies[CoreMessage_TMessageKey.PACKET_ID] = packetId
         if let _ = waitQueue[message.getStatus()] {
+//            print("wandw resp: \(message.getCode())")
             //print("MESSAGE INCOMING DATA \(message.toLogString())")
             if message.mBodies.keys.contains(CoreMessage_TMessageKey.ERRCOD) {
                 waitQueue[message.getStatus()] = message
@@ -2925,16 +2920,10 @@ extension Nexilis: MessageDelegate {
                             if let dirPath = paths.first {
                                 let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(image)
                                 if FileManager.default.fileExists(atPath: imageURL.path) {
-                                    let image    = UIImage(contentsOfFile: imageURL.path)
-                                    let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
-                                    previewImageVC.image = image
-                                    previewImageVC.isHiddenTextField = true
-                                    previewImageVC.modalPresentationStyle = .overFullScreen
-                                    previewImageVC.modalTransitionStyle  = .crossDissolve
-                                    if UIApplication.shared.visibleViewController?.navigationController != nil {
-                                        UIApplication.shared.visibleViewController?.navigationController?.present(previewImageVC, animated: true, completion: nil)
-                                    } else {
-                                        UIApplication.shared.visibleViewController?.present(previewImageVC, animated: true, completion: nil)
+                                    do {
+                                        APIS.openImageNexilis(imageView: imageBroadcast, data: try Data(contentsOf: imageURL))
+                                    } catch {
+                                        
                                     }
                                 } else if FileEncryption.shared.isSecureExists(filename: image) {
                                     do {
@@ -2943,17 +2932,7 @@ extension Nexilis: MessageDelegate {
                                             if dataDecrypt != nil {
                                                 data = dataDecrypt!
                                             }
-                                            let image = UIImage(data: data)
-                                            let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
-                                            previewImageVC.image = image
-                                            previewImageVC.isHiddenTextField = true
-                                            previewImageVC.modalPresentationStyle = .overFullScreen
-                                            previewImageVC.modalTransitionStyle  = .crossDissolve
-                                            if UIApplication.shared.visibleViewController?.navigationController != nil {
-                                                UIApplication.shared.visibleViewController?.navigationController?.present(previewImageVC, animated: true, completion: nil)
-                                            } else {
-                                                UIApplication.shared.visibleViewController?.present(previewImageVC, animated: true, completion: nil)
-                                            }
+                                            APIS.openImageNexilis(imageView: imageBroadcast, data: data)
                                         }
                                     } catch {
                                         
@@ -2965,35 +2944,30 @@ extension Nexilis: MessageDelegate {
                                         }
                 
                                         DispatchQueue.main.async {
-                                            var image : UIImage?
+                                            var data : Data?
                                             if FileManager.default.fileExists(atPath: imageURL.path) {
-                                                image    = UIImage(contentsOfFile: imageURL.path)
+                                                do {
+                                                    data = try Data(contentsOf: imageURL)
+                                                } catch {
+                                                    
+                                                }
                                             }
                                             else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
                                                 do {
                                                     if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
-                                                        let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: imageData)
-                                                        if dataDecrypt == nil {
-                                                            image = UIImage(data: imageData)
-                                                        } else {
-                                                            image = UIImage(data: dataDecrypt!)
+                                                        if let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: imageData) {
+                                                            if dataDecrypt == nil {
+                                                                data = imageData
+                                                            } else {
+                                                                data = dataDecrypt
+                                                            }
                                                         }
                                                     }
                                                 } catch {
                                                     
                                                 }
                                             }
-                                            
-                                            let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
-                                            previewImageVC.image = image
-                                            previewImageVC.isHiddenTextField = true
-                                            previewImageVC.modalPresentationStyle = .overFullScreen
-                                            previewImageVC.modalTransitionStyle  = .crossDissolve
-                                            if UIApplication.shared.visibleViewController?.navigationController != nil {
-                                                UIApplication.shared.visibleViewController?.navigationController?.present(previewImageVC, animated: true, completion: nil)
-                                            } else {
-                                                UIApplication.shared.visibleViewController?.present(previewImageVC, animated: true, completion: nil)
-                                            }
+                                            APIS.openImageNexilis(imageView: imageBroadcast, data: data)
                                         }
                                     }
                                 }
@@ -4415,8 +4389,12 @@ extension Nexilis: MessageDelegate {
                                     if (cursorData.string(forColumnIndex: 0)! + " " + cursorData.string(forColumnIndex: 1)!).trimmingCharacters(in: .whitespaces) == "USR\(User.getMyPin()!)" {
                                         let alert = LibAlertController(title: "Set Profile".localized(), message: "You must set your profile to use this feature".localized(), preferredStyle: .alert)
                                         alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {(_) in
-                                            let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
-                                            controller.forceLogin = true
+                                            guard let controller = APIS.getControllerSign() else { return }
+                                            if let controller = controller as? SignUpSignIn {
+                                                controller.forceLogin = true
+                                            } else if let controller = controller as? SignInOption {
+                                                controller.forceLogin = true
+                                            }
                                             let navigationController = CustomNavigationController(rootViewController: controller)
                                             navigationController.modalPresentationStyle = .fullScreen
                                             navigationController.navigationBar.tintColor = .white

+ 12 - 1
NexilisLite/NexilisLite/Source/Utils.swift

@@ -20,7 +20,7 @@ import AVFoundation
 
 public final class Utils {
     public static let M_USER_ANDROID_ID = "UNK"
-    public static let CPAAS_VERSION = "UCPaaS-Nexilis.3.2.8"
+    public static let CPAAS_VERSION = "UCPaaS-Nexilis.\(Nexilis.cpaasVersion)"
     
     public static func getCurrentTime()->Int64 {
         return Int64(Date().timeIntervalSince1970)
@@ -404,6 +404,17 @@ public final class Utils {
         return ""
     }
     
+    public static func setUserMSISDN(value: String) {
+        SecureUserDefaults.shared.set(value, forKey: "pb_user_msisdn")
+    }
+
+    public static func getUserMSISDN() -> String {
+        if let value: String = SecureUserDefaults.shared.value(forKey: "pb_user_msisdn") {
+            return value
+        }
+        return ""
+    }
+    
 //    public static func getMD5(string: String) -> Data {
 //        let length = Int(CC_MD5_DIGEST_LENGTH)
 //        let messageData = string.data(using:.utf8)!

+ 1 - 1
NexilisLite/NexilisLite/Source/View/Call/CreateConferenceCallController.swift

@@ -511,7 +511,7 @@ public class CreateConferenceCallController: UITableViewController {
                 cell.selectionStyle = .default
             } else {
                 let data = groups[indexPath.row - 1]
-                getImage(name: data.profile, placeholderImage: UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+                getImage(name: data.profile, placeholderImage: UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
                     content.image = image
                 }
                 content.text = data.name

+ 0 - 13
NexilisLite/NexilisLite/Source/View/Call/QmeraAudioViewController.swift

@@ -239,19 +239,6 @@ class QmeraAudioViewController: UIViewController {
         return label
     }()
     
-    let qmeraLogo: UIButton = {
-        let image = UIImage(named: "Q-Button-PNG", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
-        let button = UIButton()
-        button.setImage(image, for: .normal)
-        button.imageView?.contentMode = .scaleAspectFit
-        button.imageEdgeInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
-        button.contentVerticalAlignment = .fill
-        button.contentHorizontalAlignment = .fill
-//        button.frame.size.width = 30
-//        button.frame.size.height = 30
-        return button
-    }()
-    
     let nexilisLogo: UIButton = {
         let image = UIImage(named: "pb_powered_button", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
         let button = UIButton()

+ 0 - 13
NexilisLite/NexilisLite/Source/View/Call/QmeraVideoViewController.swift

@@ -120,19 +120,6 @@ class QmeraVideoViewController: UIViewController {
         return label
     }()
     
-    let qmeraLogo: UIButton = {
-        let image = UIImage(named: "Q-Button-PNG", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
-        let button = UIButton()
-        button.setImage(image, for: .normal)
-        button.imageView?.contentMode = .scaleAspectFit
-        button.imageEdgeInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
-        button.contentVerticalAlignment = .fill
-        button.contentHorizontalAlignment = .fill
-//        button.frame.size.width = 30
-//        button.frame.size.height = 30
-        return button
-    }()
-    
     let nexilisLogo: UIButton = {
         let image = UIImage(named: "pb_powered_button", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
         let button = UIButton()

+ 0 - 13
NexilisLite/NexilisLite/Source/View/Call/VideoConferenceViewController.swift

@@ -96,19 +96,6 @@ class VideoConferenceViewController: UIViewController {
         return label
     }()
     
-    let qmeraLogo: UIButton = {
-        let image = UIImage(named: "Q-Button-PNG", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
-        let button = UIButton()
-        button.setImage(image, for: .normal)
-        button.imageView?.contentMode = .scaleAspectFit
-        button.imageEdgeInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
-        button.contentVerticalAlignment = .fill
-        button.contentHorizontalAlignment = .fill
-//        button.frame.size.width = 30
-//        button.frame.size.height = 30
-        return button
-    }()
-    
     let nexilisLogo: UIButton = {
         let image = UIImage(named: "pb_powered_button", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
         let button = UIButton()

+ 2 - 2
NexilisLite/NexilisLite/Source/View/Chat/ArchivedChatView.swift

@@ -232,12 +232,12 @@ public class ArchivedChatView: UIViewController, UITableViewDataSource, UITableV
                 }
             } else {
                 if data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL || data.isParent || data.pin == "-999" {
-                    getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : (data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL) ? "Profile---Purple" : "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
+                    getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : (data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL) ? "Profile---Purple" : "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
                         imageView.image = image
                     })
                 } else {
                     leadingAnchor = imageView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 40.0)
-                    let image = UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+                    let image = UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
                     imageView.image = image
                 }
             }

+ 2 - 2
NexilisLite/NexilisLite/Source/View/Chat/ChatWALikeVC.swift

@@ -718,12 +718,12 @@ public class ChatWALikeVC: UIViewController, UITableViewDataSource, UITableViewD
                 }
             } else {
                 if data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL || data.isParent || data.pin == "-999" {
-                    getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : (data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL) ? "Profile---Purple" : "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
+                    getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : (data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL) ? "Profile---Purple" : "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
                         imageView.image = image
                     })
                 } else {
                     leadingAnchor = imageView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 40.0)
-                    let image = UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+                    let image = UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
                     imageView.image = image
                 }
             }

+ 1 - 1
NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift

@@ -6510,7 +6510,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
                 imageViewer.navigationItem.rightBarButtonItem = shareButton
             }
             
-            var name = (dataGroup["f_name"] as? String ?? "") + " (\(dataTopic["title"] as? String ?? ""))"
+            let name = (dataGroup["f_name"] as? String ?? "") + " (\(dataTopic["title"] as? String ?? ""))"
             imageViewer.title = name
             
             let transitionDelegate = ZoomTransitioningDelegate()

+ 49 - 26
NexilisLite/NexilisLite/Source/View/Chat/ListGroupImages.swift

@@ -358,37 +358,60 @@ class ListGroupImages: UIViewController, UITableViewDataSource, UITableViewDeleg
                         guard progress == 100 else {
                             return
                         }
-                        
-                        do {
-                            let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(self.listGroupingImages[indexPath.row].imageId)
-                            if FileManager.default.fileExists(atPath: imageURL.path){
-                                let image    = UIImage(contentsOfFile: imageURL.path)
-                                let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
-                                if save {
-                                    UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
-                                }
+                        DispatchQueue.main.async { [self] in
+                            tableView.reloadRows(at: [indexPath], with: .none)
+                            updateEditor!(listGroupingImages, [:], false)
+                        }
+                    }
+                } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
+                    do {
+                        if var data = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
+                            let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: data)
+                            if dataDecrypt != nil {
+                                data = dataDecrypt!
                             }
-                            else if FileEncryption.shared.isSecureExists(filename: self.listGroupingImages[indexPath.row].imageId){
-                                if var imageData = try FileEncryption.shared.readSecure(filename: self.listGroupingImages[indexPath.row].imageId) {
-                                    let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: imageData)
-                                    if dataDecrypt != nil {
-                                        imageData = dataDecrypt!
-                                    }
-                                    let image    = UIImage(data: imageData)
-                                    let save: Bool = SecureUserDefaults.shared.value(forKey: "saveToGallery") ?? false
-                                    if save {
-                                        UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
-                                    }
-                                }
+                            let image = UIImage(data: data ?? Data())
+                            let imageViewer = MediaViewerViewController()
+                            imageViewer.media = .image(image ?? UIImage())
+                            
+                            let navigationController = UINavigationController(rootViewController: imageViewer)
+                            navigationController.defaultStyle()
+                            navigationController.view.backgroundColor = .clear
+                            navigationController.modalPresentationCapturesStatusBarAppearance = true
+                            navigationController.modalPresentationStyle = .overFullScreen
+                            
+                            let backAction = UIAction { _ in
+                                navigationController.dismiss(animated: true)
                             }
-                        } catch {
+                            let backButton = UIBarButtonItem(title: nil, image: UIImage(systemName: "chevron.backward"), primaryAction: backAction, menu: nil)
+                            imageViewer.navigationItem.leftBarButtonItem = backButton
+//                            if Nexilis.checkingAccess(key: "secure_folder_share") || sender.specFile.contains("download") || sender.specFile.contains("share") {
+//                                let shareAction = UIAction { _ in
+//                                    var activityViewController = UIActivityViewController(activityItems: [image ?? UIImage()], applicationActivities: nil)
+//                                    if type == 1 {
+//                                        activityViewController = UIActivityViewController(activityItems: [url ?? URL(string: "")!], applicationActivities: nil)
+//                                    }
+//                                    activityViewController.popoverPresentationController?.sourceView = imageViewer.view
+//                                    imageViewer.present(activityViewController, animated: true, completion: nil)
+//                                }
+//                                let shareButton = UIBarButtonItem(title: nil, image: UIImage(systemName: "square.and.arrow.up"), primaryAction: shareAction, menu: nil)
+//                                imageViewer.navigationItem.rightBarButtonItem = shareButton
+//                            }
+//                            
+//                            let name = (dataGroup["f_name"] as? String ?? "") + " (\(dataTopic["title"] as? String ?? ""))"
+//                            imageViewer.title = name
                             
+                            let transitionDelegate = ZoomTransitioningDelegate()
+//                            transitionDelegate.originImageView = sender.imageView
+                            navigationController.transitioningDelegate = transitionDelegate
+//                            self.transitioningDelegateRef = transitionDelegate
+                            
+                            present(navigationController, animated: true) {
+                                imageViewer.animateBackgroundIn()
+                            }
                         }
+                    } catch{
                         
-                        DispatchQueue.main.async { [self] in
-                            tableView.reloadRows(at: [indexPath], with: .none)
-                            updateEditor!(listGroupingImages, [:], false)
-                        }
                     }
                 }
             }

+ 2 - 7
NexilisLite/NexilisLite/Source/View/Chat/SecureFolderView.swift

@@ -258,6 +258,7 @@ public class SecureFolderViewController: UIViewController, UISearchBarDelegate,
     }
     
     public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FileCell", for: indexPath) as! FileCell
         let fileItem = filteredFiles[indexPath.item]
         if fileItem.imageId != "" {
             print("this image")
@@ -267,13 +268,7 @@ public class SecureFolderViewController: UIViewController, UISearchBarDelegate,
                     if dataDecrypt != nil {
                         data = dataDecrypt!
                     }
-                    let image = UIImage(data: data)
-                    let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
-                    previewImageVC.image = image
-                    previewImageVC.isHiddenTextField = true
-                    previewImageVC.modalPresentationStyle = .custom
-                    previewImageVC.modalTransitionStyle  = .crossDissolve
-                    self.present(previewImageVC, animated: true, completion: nil)
+                    APIS.openImageNexilis(imageView: cell.imageView, data: data, isGIF: true)
                 }
             }
             catch {

+ 1 - 1
NexilisLite/NexilisLite/Source/View/Control/BroadcastMembersTableViewController.swift

@@ -297,7 +297,7 @@ class BroadcastMembersTableViewController: UITableViewController, UISearchContro
             }
             content.text = group.name
             content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
-            getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+            getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
                 content.image = image
             }
             cell.contentConfiguration = content

+ 1 - 1
NexilisLite/NexilisLite/Source/View/Control/BroadcastViewController.swift

@@ -675,7 +675,7 @@ class BroadcastViewController: UITableViewController, UITextFieldDelegate, UITex
                 let group: Group
                 group = groups[indexPath.row]
                 content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
-                getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+                getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
                     content.image = image
 //                    tableView.reloadRows(at: [indexPath], with: .none)
                 }

+ 3 - 25
NexilisLite/NexilisLite/Source/View/Control/ChangeDeviceViewController.swift

@@ -17,7 +17,6 @@ public class ChangeDeviceViewController: UIViewController {
     
     public var isDismiss: ((String) -> ())?
     public var forceLogin = false
-    public var fromChangeNamePass = false
     
     public override func viewDidLoad() {
         super.viewDidLoad()
@@ -163,14 +162,7 @@ public class ChangeDeviceViewController: UIViewController {
                                         Nexilis.floatingButton = FloatingButton()
                                         Nexilis.addFB()
                                     }
-                                    if self.fromChangeNamePass{
-                                        var vc = self.navigationController?.presentingViewController
-                                        while vc?.presentingViewController != nil {
-                                            vc = vc?.presentingViewController
-                                        }
-                                        vc?.dismiss(animated: true, completion: nil)
-                                    }
-                                    else if !self.forceLogin {
+                                    if !self.forceLogin {
                                         self.navigationController?.popViewController(animated: true)
                                     } else {
                                         self.navigationController?.dismiss(animated: true)
@@ -287,14 +279,7 @@ public class ChangeDeviceViewController: UIViewController {
                                     Nexilis.addFB()
                                 }
                                 NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onRefreshWebView"), object: nil, userInfo: nil)
-                                if self.fromChangeNamePass{
-                                    var vc = self.navigationController?.presentingViewController
-                                    while vc?.presentingViewController != nil {
-                                        vc = vc?.presentingViewController
-                                    }
-                                    vc?.dismiss(animated: true, completion: nil)
-                                }
-                                else if !self.forceLogin {
+                                if !self.forceLogin {
                                     self.navigationController?.popViewController(animated: true)
                                 } else {
                                     self.navigationController?.dismiss(animated: true)
@@ -328,14 +313,7 @@ public class ChangeDeviceViewController: UIViewController {
                                     Nexilis.addFB()
                                 }
                                 NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onRefreshWebView"), object: nil, userInfo: nil)
-                                if self.fromChangeNamePass{
-                                    var vc = self.navigationController?.presentingViewController
-                                    while vc?.presentingViewController != nil {
-                                        vc = vc?.presentingViewController
-                                    }
-                                    vc?.dismiss(animated: true, completion: nil)
-                                }
-                                else if !self.forceLogin {
+                                if !self.forceLogin {
                                     self.navigationController?.popViewController(animated: true)
                                 } else {
                                     self.navigationController?.dismiss(animated: true)

+ 3 - 3
NexilisLite/NexilisLite/Source/View/Control/ChangeNamePassswordViewController.swift

@@ -52,9 +52,9 @@ public class ChangeNamePassswordViewController: UIViewController {
     }
     
     @objc func showLogin() {
-        let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "changeDevice") as! ChangeDeviceViewController
-        controller.fromChangeNamePass = true
-        navigationController?.show(controller, sender: nil)
+//        let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "changeDevice") as! ChangeDeviceViewController
+//        controller.fromChangeNamePass = true
+//        navigationController?.show(controller, sender: nil)
     }
     
     @objc func dismissKeyboard() {

+ 4 - 7
NexilisLite/NexilisLite/Source/View/Control/ContactChatViewController.swift

@@ -649,9 +649,6 @@ class ContactChatViewController: UITableViewController {
     }
     
     private func getOpenGroups(listGroups: [Group], completion: @escaping ([Group]) -> ()) {
-        while Nexilis.isProcessWriteSync {
-            Thread.sleep(forTimeInterval: 0.5)
-        }
         if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getOpenGroups(p_account: "1,2,3,5,6,7", offset: "0", search: "")) {
             var dataGroups: [Group] = []
             if (response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "00") {
@@ -1448,12 +1445,12 @@ extension ContactChatViewController {
                         }
                     } else {
                         if data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL || data.isParent || data.pin == "-999" {
-                            getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : (data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL) ? "Profile---Purple" : "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
+                            getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : (data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL) ? "Profile---Purple" : "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
                                 imageView.image = image
                             })
                         } else {
                             leadingAnchor = imageView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 40.0)
-                            let image = UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+                            let image = UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
                             imageView.image = image
                         }
                     }
@@ -1739,7 +1736,7 @@ extension ContactChatViewController {
                     cell.accessoryType = .none
                 }
                 content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
-                getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+                getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
                     content.image = image
                 }
                 cell.contentConfiguration = content
@@ -1877,7 +1874,7 @@ extension ContactChatViewController {
                 cell.accessoryType = .none
             }
             content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
-            getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+            getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
                 content.image = image
             }
             cell.contentConfiguration = content

+ 2 - 2
NexilisLite/NexilisLite/Source/View/Control/HistoryBroadcastViewController.swift

@@ -146,7 +146,7 @@ class HistoryBroadcastViewController: UIViewController, UITableViewDelegate, UIT
                 if user != nil {
                     imageView.image = UIImage(named: "Profile---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
                 } else {
-                    imageView.image = UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+                    imageView.image = UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
                 }
             } else {
                 if !Utils.getIconDock().isEmpty {
@@ -169,7 +169,7 @@ class HistoryBroadcastViewController: UIViewController, UITableViewDelegate, UIT
                         }
                     }
                 } else {
-                    getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
+                    getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
                         imageView.image = image
                         if !result {
                             imageView.tintColor = .mainColor

+ 6 - 14
NexilisLite/NexilisLite/Source/View/Control/ProfileViewController.swift

@@ -671,13 +671,11 @@ public class ProfileViewController: UITableViewController, UITextFieldDelegate {
             if let dirPath = paths.first {
                 let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imageUser)
                 if FileManager.default.fileExists(atPath: imageURL.path) {
-                    let image    = UIImage(contentsOfFile: imageURL.path)
-                    let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
-                    previewImageVC.image = image
-                    previewImageVC.isHiddenTextField = true
-                    previewImageVC.modalPresentationStyle = .custom
-                    previewImageVC.modalTransitionStyle  = .crossDissolve
-                    self.present(previewImageVC, animated: true, completion: nil)
+                    do {
+                        APIS.openImageNexilis(imageView: self.profile, data: try Data(contentsOf: imageURL))
+                    } catch {
+                        
+                    }
                 } else if FileEncryption.shared.isSecureExists(filename: imageUser) {
                     do {
                         if var data = try FileEncryption.shared.readSecure(filename: imageUser) {
@@ -685,13 +683,7 @@ public class ProfileViewController: UITableViewController, UITextFieldDelegate {
                             if dataDecrypt != nil {
                                 data = dataDecrypt!
                             }
-                            let image = UIImage(data: data)
-                            let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
-                            previewImageVC.image = image
-                            previewImageVC.isHiddenTextField = true
-                            previewImageVC.modalPresentationStyle = .custom
-                            previewImageVC.modalTransitionStyle  = .crossDissolve
-                            self.present(previewImageVC, animated: true, completion: nil)
+                            APIS.openImageNexilis(imageView: self.profile, data: data)
                         }
                     }
                     catch {

+ 10 - 5
NexilisLite/NexilisLite/Source/View/Control/SettingTableViewController.swift

@@ -65,6 +65,15 @@ public class SettingTableViewController: UITableViewController, UIGestureRecogni
         switchVibrateMode.addTarget(self, action: #selector(vibrateModeSwitch), for: .valueChanged)
         switchSaveToGallery.addTarget(self, action: #selector(saveToGallerySwitch), for: .valueChanged)
         switchAutoDownload.addTarget(self, action: #selector(autoDownloadSwitch), for: .valueChanged)
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(onRefresh(notification:)), name: NSNotification.Name(rawValue: "onRefreshWebView"), object: nil)
+    }
+    
+    @objc func onRefresh(notification: NSNotification) {
+        DispatchQueue.main.async {
+            self.makeMenu()
+            self.tableView.reloadData()
+        }
     }
     
     @objc func didTapExit() {
@@ -621,11 +630,7 @@ public class SettingTableViewController: UITableViewController, UIGestureRecogni
             }
             navigationController?.show(controller, sender: nil)
         } else if item.title == "Sign-Up/Sign-In".localized() {
-            let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
-            controller.isDismiss = { newThumb in
-                self.makeMenu(imageSignIn: newThumb)
-                self.tableView.reloadData()
-            }
+            guard let controller = APIS.getControllerSign() else { return }
             navigationController?.show(controller, sender: nil)
         } else if item.title == "Sign-Out".localized() {
             let alert = LibAlertController(title: "Sign-Out".localized(), message: "Are you sure want to logout?".localized(), preferredStyle: .alert)

+ 120 - 0
NexilisLite/NexilisLite/Source/View/Control/SignInOption.swift

@@ -0,0 +1,120 @@
+//
+//  SignInOption.swift
+//  Pods
+//
+//  Created by Qindi on 16/06/25.
+//
+
+import UIKit
+import Foundation
+
+public class SignInOption: UIViewController {
+    public var forceLogin = false
+    
+    private let containerView = UIStackView()
+    
+    public override func viewDidLoad() {
+        self.title = "Sign-Up/Sign-In Method".localized()
+        if forceLogin {
+            navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(didTapCancel(sender:)))
+        }
+        self.navigationController?.navigationBar.topItem?.backButtonTitle = ""
+        createBody()
+        if Nexilis.checkingAccess(key: "sign_in_up_msisdn") {
+            addButton(type: 0)
+        }
+        if Nexilis.checkingAccess(key: "sign_in_up_email") {
+            addButton(type: 1)
+        }
+        if Nexilis.checkingAccess(key: "sign_in_up_username") {
+            addButton(type: 2)
+        }
+    }
+    
+    public override func viewDidAppear(_ animated: Bool) {
+        let attributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
+        let navBarAppearance = UINavigationBarAppearance()
+        navBarAppearance.configureWithOpaqueBackground()
+        navBarAppearance.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : UIColor.mainColor
+        navBarAppearance.titleTextAttributes = attributes
+        navigationController?.navigationBar.standardAppearance = navBarAppearance
+        navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
+        navigationController?.navigationBar.tintColor = .white
+    }
+    
+    private func createBody() {
+        let iconIV = UIImageView(image: UIImage(named: "pb_user", in: Bundle.resourceBundle(for: Nexilis.self), with: nil))
+        self.view.addSubview(iconIV)
+        iconIV.anchor(top: self.view.safeAreaLayoutGuide.topAnchor, paddingTop: 20, centerX: self.view.centerXAnchor, width: 250, height: 200)
+        
+        let title = UILabel()
+        self.view.addSubview(title)
+        title.anchor(top: iconIV.bottomAnchor, paddingTop: 10, centerX: iconIV.centerXAnchor)
+        title.text = "Choose your Sign-Up or Sign-In method :".localized()
+        title.font = .systemFont(ofSize: 14)
+        
+        let bottomTitle = UILabel()
+        self.view.addSubview(bottomTitle)
+        bottomTitle.anchor(bottom: self.view.bottomAnchor, paddingBottom: 10, centerX: iconIV.centerXAnchor)
+        bottomTitle.text = "Powered by Nexilis".localized()
+        bottomTitle.font = .systemFont(ofSize: 13)
+        
+        self.view.addSubview(containerView)
+        let countData = APIS.checkSignMethod().0
+        let heightButton = 60 * CGFloat(countData)
+        containerView.anchor(top: title.bottomAnchor, left: self.view.leftAnchor, right: self.view.rightAnchor, paddingTop: 10, paddingLeft: 20, paddingRight: 20, height: heightButton + 30)
+        containerView.spacing = 10
+        containerView.axis = .vertical
+        containerView.distribution = .fillEqually
+    }
+    
+    private func addButton(type: Int) {
+        let button = UIButton(type: .custom)
+        button.frame = CGRect(x: 0, y: 0, width: containerView.bounds.size.width, height: 60)
+        button.layer.cornerRadius = 10
+        button.clipsToBounds = true
+        button.backgroundColor = .whiteBubbleColor
+        button.tag = type
+        button.addTarget(self, action: #selector(didTapSignIn), for: .touchUpInside)
+        
+        var image = UIImage(named: "pb_ic_msisdn", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+        var title = "Phone Number (MSISDN)".localized()
+        if type == 1 {
+            image = UIImage(named: "pb_stg_email", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+            title = "Email".localized()
+        } else if type == 2 {
+            image = UIImage(named: "pb_ic_name", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+            title = "Username".localized()
+        }
+        
+        let imageView = UIImageView(image: image)
+        button.addSubview(imageView)
+        imageView.anchor(left: button.leftAnchor, paddingLeft: 20, centerY: button.centerYAnchor, width: 40, height: 40)
+        imageView.layer.cornerRadius = 20
+        imageView.backgroundColor = .white
+        
+        let titleLabel = UILabel()
+        titleLabel.text = title
+        titleLabel.textColor = .label
+        titleLabel.font = .boldSystemFont(ofSize: 14)
+        button.addSubview(titleLabel)
+        titleLabel.anchor(left: imageView.rightAnchor, right: button.rightAnchor, paddingLeft: 10, paddingRight: 20, centerY: button.centerYAnchor)
+        
+        containerView.addArrangedSubview(button)
+    }
+    
+    @objc func didTapSignIn(sender: UIButton) {
+        let vc = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
+        if sender.tag == 0 {
+            vc.isMSISDN = true
+        } else if sender.tag == 1 {
+            vc.isEmail = true
+        }
+        vc.forceLogin = self.forceLogin
+        self.show(vc, sender: nil)
+    }
+    
+    @objc func didTapCancel(sender: Any) {
+        self.navigationController?.dismiss(animated: true)
+    }
+}

+ 442 - 282
NexilisLite/NexilisLite/Source/View/Control/SignUpSignIn.swift

@@ -8,6 +8,7 @@
 import UIKit
 import NotificationBannerSwift
 import nuSDKService
+import FirebaseAuth
 
 public class SignUpSignIn: UIViewController {
     @IBOutlet weak var descSignUpSignIn: UILabel!
@@ -15,38 +16,36 @@ public class SignUpSignIn: UIViewController {
     @IBOutlet weak var passwordField: PasswordTextField!
     @IBOutlet weak var showPasswordButton: UIButton!
     @IBOutlet weak var descDisclaimer: UILabel!
+    @IBOutlet weak var topConstDesc: NSLayoutConstraint!
     
-    public var isDismiss: ((String) -> ())?
     public var forceLogin = false
-    public var fromChangeNamePass = false
+    public var isEmail = false
+    public var isMSISDN = false
     
     public override func viewDidLoad() {
         super.viewDidLoad()
 
 //        self.view.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .black : .white
-        let attributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
-        let navBarAppearance = UINavigationBarAppearance()
-        navBarAppearance.configureWithOpaqueBackground()
-        navBarAppearance.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : UIColor.mainColor
-        navBarAppearance.titleTextAttributes = attributes
-        navigationController?.navigationBar.standardAppearance = navBarAppearance
-        navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
-        navigationController?.navigationBar.tintColor = .white
-
-        self.title = "Sign-Up/Sign-In".localized()
-        descSignUpSignIn.text = "Please enter your nickname and your password".localized()
-        descDisclaimer.text = "Disclaimer : Signing up with a nickname provides full privacy since".localized() + " \(Bundle.main.infoDictionary?["CFBundleName"] as! String) " + "does not know your identity, which is usually linked to your email account or mobile number. However, if you use a nickname, we will not be able to reset your password if you lose or forget it, so please keep your password secure.".localized()
-        descDisclaimer.font = UIFont.italicSystemFont(ofSize: 14)
-        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Submit".localized(), style: .plain, target: self, action: #selector(didTapSubmit(sender:)))
-        
-        if forceLogin {
-            navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(didTapCancel(sender:)))
+        var textTitle = "Please enter your nickname and your password".localized()
+        var subTitle = "Disclaimer : Signing up with a nickname provides full privacy since".localized() + " \(Bundle.main.infoDictionary?["CFBundleName"] as! String) " + "does not know your identity, which is usually linked to your email account or mobile number. However, if you use a nickname, we will not be able to reset your password if you lose or forget it, so please keep your password secure.".localized()
+        var textPlaceHolder = "Your Nickname".localized()
+        if isEmail {
+            textTitle = "Please enter your registered email address.".localized()
+            subTitle = ""
+            textPlaceHolder = "Your Email".localized()
+        } else if isMSISDN {
+            textTitle = "Please enter your registered phone number.".localized()
+            subTitle = ""
+            textPlaceHolder = "Your Phone Number (082...)".localized()
         }
+        descSignUpSignIn.text = textTitle
+        descDisclaimer.text = subTitle
+        descDisclaimer.font = UIFont.italicSystemFont(ofSize: 14)
         
         passwordField.addPadding(.right(40))
         passwordField.isSecureTextEntry = true
         showPasswordButton.setImage(UIImage(systemName: "eye.slash.fill"), for: .normal)
-        usernameField.placeholder = "Your Nickname".localized()
+        usernameField.placeholder = textPlaceHolder
         passwordField.placeholder = "Password".localized()
         usernameField.tintColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .mainColor
         passwordField.tintColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .mainColor
@@ -57,6 +56,34 @@ public class SignUpSignIn: UIViewController {
         let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
         tapGesture.cancelsTouchesInView = false
         view.addGestureRecognizer(tapGesture)
+        if isEmail || isMSISDN {
+            self.title = "Sign-In".localized()
+            passwordField.isHidden = true
+            showPasswordButton.isHidden = true
+            if isMSISDN{
+                usernameField.keyboardType = .numberPad
+            }
+        } else {
+            self.title = "Sign-Up/Sign-In".localized()
+        }
+        let controllers = self.navigationController?.viewControllers
+        if forceLogin && (controllers!.count < 2 || !(controllers![controllers!.count - 2] is SignInOption)) {
+            navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(didTapCancel(sender:)))
+        }
+        self.navigationController?.navigationBar.topItem?.backButtonTitle = ""
+    }
+    
+    public override func viewDidAppear(_ animated: Bool) {
+        let attributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
+        let navBarAppearance = UINavigationBarAppearance()
+        navBarAppearance.configureWithOpaqueBackground()
+        navBarAppearance.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : UIColor.mainColor
+        navBarAppearance.titleTextAttributes = attributes
+        navigationController?.navigationBar.standardAppearance = navBarAppearance
+        navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
+        navigationController?.navigationBar.tintColor = .white
+        
+        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Submit".localized(), style: .plain, target: self, action: #selector(didTapSubmit(sender:)))
     }
     
     @objc func didTapCancel(sender: Any) {
@@ -64,14 +91,14 @@ public class SignUpSignIn: UIViewController {
     }
     
     @objc func checkUsername(_ textField: UITextField) {
-        let text : String! = usernameField.text
-        if isValidEmail(text) {
-            passwordField.isHidden = true
-            showPasswordButton.isHidden = true
-        } else if passwordField.isHidden {
-            passwordField.isHidden = false
-            showPasswordButton.isHidden = false
-        }
+//        let text : String! = usernameField.text
+//        if isValidEmail(text) {
+//            passwordField.isHidden = true
+//            showPasswordButton.isHidden = true
+//        } else if passwordField.isHidden {
+//            passwordField.isHidden = false
+//            showPasswordButton.isHidden = false
+//        }
     }
     
     func isValidEmail(_ email: String) -> Bool {
@@ -98,10 +125,7 @@ public class SignUpSignIn: UIViewController {
     
     func checkEmail(email: String) {
         if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
-            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-            imageView.tintColor = .white
-            let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-            banner.show()
+            self.showFailedSignUpIn(title: "Check your connection".localized(), withLoader: false)
             return
         }
         Nexilis.showLoader()
@@ -109,12 +133,7 @@ public class SignUpSignIn: UIViewController {
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSendOTPLogin(p_email: email), timeout: 30 * 1000) {
                 if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") != "00" {
                     DispatchQueue.main.async {
-                        Nexilis.hideLoader(completion: {
-                            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                            imageView.tintColor = .white
-                            let banner = FloatingNotificationBanner(title: "Unregistered email account".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                            banner.show()
-                        })
+                        self.showFailedSignUpIn(title: "Unregistered email account".localized())
                     }
                 } else {
                     DispatchQueue.main.async {
@@ -125,76 +144,176 @@ public class SignUpSignIn: UIViewController {
                 }
             } else {
                 DispatchQueue.main.async {
-                    Nexilis.hideLoader(completion: {
-                        let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                        imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Unable to access servers. Try again later".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                        banner.show()
-                    })
+                    self.showFailedSignUpIn(title: "Unable to access servers. Try again later".localized())
+                }
+            }
+        }
+    }
+    
+    func checkNumber(number: String) {
+        var number = number
+        if number.hasPrefix("0") {
+            number = number.replacingCharacters(in: number.startIndex...number.startIndex, with: "+62")
+        }
+        if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
+            self.showFailedSignUpIn(title: "Check your connection".localized(), withLoader: false)
+            return
+        }
+        self.sendOTP(to: number)
+    }
+    
+    func sendOTP(to phoneNumber: String) {
+        PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate: nil) { verificationID, error in
+            if let error = error {
+                UIApplication.shared.visibleViewController?.view.makeToast("Error sending OTP: \(error)".localized(), duration: 3, position: .center)
+                return
+            }
+
+            Utils.setUserMSISDN(value: verificationID ?? "")
+            self.showPageOTP(phone: phoneNumber)
+        }
+    }
+    
+    func verifyOTP(_ code: String, number: String, privateKey: SecKey) {
+        let verificationID = Utils.getUserMSISDN()
+        let credential = PhoneAuthProvider.provider().credential(
+            withVerificationID: verificationID,
+            verificationCode: code
+        )
+
+        Auth.auth().signIn(with: credential) { authResult, error in
+            if error != nil {
+                self.showFailedSignUpIn(title: "Invalid OTP".localized())
+                self.showPageOTP(phone: number)
+                return
+            }
+
+            DispatchQueue.global().async {
+                if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getChalanger()) {
+                    if response.isOk() {
+                        let data = response.getBody(key: CoreMessage_TMessageKey.DATA, default_value: "")
+                        if data.isEmpty {
+                            DispatchQueue.main.async {
+                                self.showFailedSignUpIn(title: "Failed to get auth, please try again".localized())
+                            }
+                            return
+                        }
+                        var pk = ""
+                        var sign = ""
+                        let df = HMACDeviceFingerprintNexilis.generate()
+                        if let dataSign = "\(data)!\(df)".data(using: .utf8) {
+                            if let signature = KeyManagerNexilis.sign(data: dataSign, privateKey: privateKey) {
+                                sign = signature.base64EncodedString()
+                            }
+                        }
+                        if let publicKey = KeyManagerNexilis.getRSAX509PublicKeyBase64(privateKey: privateKey) {
+                            pk = publicKey
+                        }
+                        if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSendVerifyChangeDevice(p_email: "", p_vercode: "", number: number, deviceFingerprint: df, publicKey: pk, signature: sign), timeout: 30 * 1000) {
+                            if !response.isOk() {
+                                DispatchQueue.main.async {
+                                    DispatchQueue.main.async {
+                                        self.showFailedSignUpIn(title: "Failed".localized())
+                                    }
+                                }
+                            } else {
+                                self.successSubmit(response: response, first: "", last: "", number: number)
+                            }
+                        } else {
+                            DispatchQueue.main.async {
+                                self.showFailedSignUpIn(title: "Unable to access servers. Try again later".localized())
+                            }
+                        }
+                    } else {
+                        DispatchQueue.main.async {
+                            self.showFailedSignUpIn(title: "Failed to get auth, please try again".localized())
+                        }
+                    }
+                } else {
+                    DispatchQueue.main.async {
+                        self.showFailedSignUpIn(title: "Unable to access servers. Try again later".localized())
+                    }
                 }
             }
         }
     }
     
-    func showPageOTP(email: String, errCode:String = "") {
+    func showPageOTP(email: String = "", phone: String = "", errCode:String = "") {
         let showOTPVC = VerifyEmail()
         showOTPVC.email = email
+        showOTPVC.msisdn = phone
+        showOTPVC.isMSISDN = !phone.isEmpty
         showOTPVC.showWrongOTP = errCode
         showOTPVC.isDismiss = { code in
+            if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
+                self.showFailedSignUpIn(title: "Check your connection".localized(), withLoader: false)
+                return
+            }
+            if KeyManagerNexilis.hasGeneratedKey() {
+                KeyManagerNexilis.deleteKey()
+                KeyManagerNexilis.deleteMarker()
+            }
+            KeyManagerNexilis.generateKey()
+            KeyManagerNexilis.saveMarker()
+            guard let privateKey = KeyManagerNexilis.getPrivateKey() else {
+                KeyManagerNexilis.deleteKey()
+                KeyManagerNexilis.deleteMarker()
+                UIApplication.shared.visibleViewController?.view.makeToast("Biometric or passcode authentication required".localized(), duration: 3, position: .center)
+                return
+            }
+            if Database.shared.openDatabase() == 0 {
+                APIS.showRestartApp()
+                KeyManagerNexilis.deleteKey()
+                KeyManagerNexilis.deleteMarker()
+                return
+            }
             Nexilis.showLoader()
+            if !phone.isEmpty {
+                self.verifyOTP(code, number: phone, privateKey: privateKey)
+                return
+            }
             DispatchQueue.global().async {
-                if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSendVerifyChangeDevice(p_email: email, p_vercode: code), timeout: 30 * 1000) {
-                    if !response.isOk() {
-                        DispatchQueue.main.async {
-                            Nexilis.hideLoader(completion: {
-                                self.showPageOTP(email: email, errCode: response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99"))
-                            })
+                if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getChalanger()) {
+                    if response.isOk() {
+                        let data = response.getBody(key: CoreMessage_TMessageKey.DATA, default_value: "")
+                        if data.isEmpty {
+                            DispatchQueue.main.async {
+                                self.showFailedSignUpIn(title: "Failed to get auth, please try again".localized())
+                            }
+                            return
+                        }
+                        var pk = ""
+                        var sign = ""
+                        let df = HMACDeviceFingerprintNexilis.generate()
+                        if let dataSign = "\(data)!\(df)".data(using: .utf8) {
+                            if let signature = KeyManagerNexilis.sign(data: dataSign, privateKey: privateKey) {
+                                sign = signature.base64EncodedString()
+                            }
+                        }
+                        if let publicKey = KeyManagerNexilis.getRSAX509PublicKeyBase64(privateKey: privateKey) {
+                            pk = publicKey
+                        }
+                        if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSendVerifyChangeDevice(p_email: email, p_vercode: code, deviceFingerprint: df, publicKey: pk, signature: sign), timeout: 30 * 1000) {
+                            if !response.isOk() {
+                                DispatchQueue.main.async {
+                                    self.showPageOTP(email: email, errCode: response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99"))
+                                }
+                            } else {
+                                self.successSubmit(response: response, first: "", last: "", email: email)
+                            }
+                        } else {
+                            DispatchQueue.main.async {
+                                self.showFailedSignUpIn(title: "Unable to access servers. Try again later".localized())
+                            }
                         }
                     } else {
-                        self.deleteAllRecordDatabase()
-                        let id = response.getBody(key: CoreMessage_TMessageKey.F_PIN, default_value: "")
-                        let thumb = response.getBody(key: CoreMessage_TMessageKey.THUMB_ID, default_value: "")
-                        if(!id.isEmpty) {
-//                            Nexilis.changeUser(f_pin: id)
-                            Utils.setProfile(value: true)
-                            // pos registration
-                            _ = Nexilis.write(message: CoreMessage_TMessageBank.getPostRegistration(p_pin: id))
-                            DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
-                                Nexilis.hideLoader(completion: {
-                                    let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
-                                    imageView.tintColor = .white
-                                    let banner = FloatingNotificationBanner(title: "Successfully Sign-In".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
-                                    banner.show()
-                                    if Nexilis.showFB {
-                                        Nexilis.floatingButton.removeFromSuperview()
-                                        Nexilis.floatingButton = FloatingButton()
-                                        Nexilis.addFB()
-                                    }
-                                    if self.fromChangeNamePass{
-                                        var vc = self.navigationController?.presentingViewController
-                                        while vc?.presentingViewController != nil {
-                                            vc = vc?.presentingViewController
-                                        }
-                                        vc?.dismiss(animated: true, completion: nil)
-                                    }
-                                    else if !self.forceLogin {
-                                        self.navigationController?.popViewController(animated: true)
-                                    } else {
-                                        self.navigationController?.dismiss(animated: true)
-                                    }
-                                    self.isDismiss?(thumb)
-                                })
-                            })
+                        DispatchQueue.main.async {
+                            self.showFailedSignUpIn(title: "Failed to get auth, please try again".localized())
                         }
                     }
                 } else {
                     DispatchQueue.main.async {
-                        Nexilis.hideLoader(completion: {
-                            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                            imageView.tintColor = .white
-                            let banner = FloatingNotificationBanner(title: "Unable to access servers. Try again later".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                            banner.show()
-                        })
+                        self.showFailedSignUpIn(title: "Unable to access servers. Try again later".localized())
                     }
                 }
             }
@@ -204,247 +323,288 @@ public class SignUpSignIn: UIViewController {
         })
     }
     
-    @objc func didTapSubmit(sender: Any) {
-        guard let name = usernameField.text, !name.isEmpty else {
+    private func showFailedSignUpIn(title: String, withLoader: Bool = true) {
+        KeyManagerNexilis.deleteKey()
+        KeyManagerNexilis.deleteMarker()
+        if withLoader {
+            Nexilis.hideLoader(completion: {
+                let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
+                imageView.tintColor = .white
+                let banner = FloatingNotificationBanner(title: title, subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                banner.show()
+            })
+        } else {
             let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
             imageView.tintColor = .white
-            let banner = FloatingNotificationBanner(title: "Username can't be empty".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+            let banner = FloatingNotificationBanner(title: title, subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
             banner.show()
+        }
+    }
+    
+    @objc func didTapSubmit(sender: Any) {
+        guard let name = usernameField.text, !name.isEmpty else {
+            var text = "Username"
+            if isEmail {
+                text = "Email"
+            } else if isMSISDN {
+                text = "Phone Number"
+            }
+            self.showFailedSignUpIn(title: "\(text) can't be empty".localized(), withLoader: false)
+            return
+        }
+        
+        if isEmail {
+            if !isValidEmail(name) {
+                self.showFailedSignUpIn(title: "Invalid email format. Please enter a valid email address".localized(), withLoader: false)
+            } else {
+                checkEmail(email: name)
+            }
             return
         }
+        
+        if isMSISDN {
+            checkNumber(number: name)
+            return
+        }
+        
         let a = name.split(separator: " ", maxSplits: 1)
         let first = String(a[0])
         let last = a.count == 2 ? String(a[1]) : ""
         
         if first.count > 24 {
-            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-            imageView.tintColor = .white
-            let banner = FloatingNotificationBanner(title: "First name is too long".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-            banner.show()
+            self.showFailedSignUpIn(title: "First name is too long".localized(), withLoader: false)
             return
         }
         
         if last.count > 24 {
-            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-            imageView.tintColor = .white
-            let banner = FloatingNotificationBanner(title: "Last name is too long".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-            banner.show()
+            self.showFailedSignUpIn(title: "Last name is too long".localized(), withLoader: false)
             return
         }
         
         let idMe = User.getMyPin()!
-        
-        if isValidEmail(name) {
-            checkEmail(email: name)
+
+        if !name.matches("^[a-zA-Z0-9 ]*$") {
+            self.showFailedSignUpIn(title: "Contains prohibited characters. Only alphabetic characters are allowed.".localized(), withLoader: false)
             return
         }
-        if !name.matches("^[a-zA-Z0-9 ]*$") { 
-            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-            imageView.tintColor = .white
-            let banner = FloatingNotificationBanner(title: "Contains prohibited characters. Only alphabetic characters are allowed.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-            banner.show()
-            return
+        let password = passwordField.text ?? ""
+        if !passwordField.isHidden {
+            if password.isEmpty {
+                self.showFailedSignUpIn(title: "Password can't be empty".localized(), withLoader: false)
+                return
+            }
+            if password.count < 6 {
+                self.showFailedSignUpIn(title: "Password min 6 character".localized(), withLoader: false)
+                return
+            }
         }
-        guard let password = passwordField.text, !password.isEmpty else {
-            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-            imageView.tintColor = .white
-            let banner = FloatingNotificationBanner(title: "Password can't be empty".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-            banner.show()
+        if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
+            self.showFailedSignUpIn(title: "Check your connection".localized(), withLoader: false)
             return
         }
-        if password.count < 6 {
-            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-            imageView.tintColor = .white
-            let banner = FloatingNotificationBanner(title: "Password min 6 character".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-            banner.show()
-            return
+        if KeyManagerNexilis.hasGeneratedKey() {
+            KeyManagerNexilis.deleteKey()
+            KeyManagerNexilis.deleteMarker()
         }
-        if !CheckConnection.isConnectedToNetwork() || API.nGetCLXConnState() == 0 {
-            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-            imageView.tintColor = .white
-            let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-            banner.show()
+        KeyManagerNexilis.generateKey()
+        KeyManagerNexilis.saveMarker()
+        guard let privateKey = KeyManagerNexilis.getPrivateKey() else {
+            KeyManagerNexilis.deleteKey()
+            KeyManagerNexilis.deleteMarker()
+            UIApplication.shared.visibleViewController?.view.makeToast("Biometric or passcode authentication required".localized(), duration: 3, position: .center)
             return
         }
         if Database.shared.openDatabase() == 0 {
             APIS.showRestartApp()
+            KeyManagerNexilis.deleteKey()
+            KeyManagerNexilis.deleteMarker()
             return
         }
         Nexilis.showLoader()
         DispatchQueue.global().async {
-            let md5Hex = password
-            if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSignUpSignInAPI(p_name: name, p_password: md5Hex), timeout: 30 * 1000) {
-                if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "20" {
-                    DispatchQueue.main.async {
-                        Nexilis.hideLoader(completion: {
-                            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                            imageView.tintColor = .white
-                            let banner = FloatingNotificationBanner(title: "Invalid user / Username and password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                            banner.show()
-                        })
-                    }
-                } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "11" {
-                    DispatchQueue.main.async {
-                        Nexilis.hideLoader(completion: {
-                            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                            imageView.tintColor = .white
-                            let banner = FloatingNotificationBanner(title: "Failed, unknown user".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                            banner.show()
-                        })
+            if let response = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getChalanger()) {
+                if response.isOk() {
+                    let data = response.getBody(key: CoreMessage_TMessageKey.DATA, default_value: "")
+                    if data.isEmpty {
+                        DispatchQueue.main.async {
+                            self.showFailedSignUpIn(title: "Failed to get auth, please try again".localized())
+                        }
+                        return
                     }
-                } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "4u" {
-                    DispatchQueue.main.async {
-                        Nexilis.hideLoader(completion: {
-                            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                            imageView.tintColor = .white
-                            let banner = FloatingNotificationBanner(title: "Failed, blocked user".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                            banner.show()
-                        })
+                    let md5Hex = password
+                    var pk = ""
+                    var sign = ""
+                    let df = HMACDeviceFingerprintNexilis.generate()
+                    if let dataSign = "\(data)!\(df)".data(using: .utf8) {
+                        if let signature = KeyManagerNexilis.sign(data: dataSign, privateKey: privateKey) {
+                            sign = signature.base64EncodedString()
+                        }
                     }
-                } else if !response.isOk() {
-                    DispatchQueue.main.async {
-                        Nexilis.hideLoader(completion: {
-                            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                            imageView.tintColor = .white
-                            let banner = FloatingNotificationBanner(title: "Failed".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                            banner.show()
-                        })
+                    if let publicKey = KeyManagerNexilis.getRSAX509PublicKeyBase64(privateKey: privateKey) {
+                        pk = publicKey
                     }
-                } else {
-                    let sign = response.getBody(key: CoreMessage_TMessageKey.SIGN, default_value: "")
-                    if sign == "1" {
-                        let id = response.getBody(key: CoreMessage_TMessageKey.F_PIN, default_value: "")
-                        let f_pin = response.getBody(key: CoreMessage_TMessageKey.F_PIN_REAL, default_value: "")
-                        let thumb = response.getBody(key: CoreMessage_TMessageKey.THUMB_ID, default_value: "")
-                        let device_id = response.getBody(key: CoreMessage_TMessageKey.IMEI, default_value: id)
-                        let last_sign = response.getBody(key: CoreMessage_TMessageKey.LAST_SIGN, default_value: "0")
-                        //print("last sign: \(last_sign)")
-                        if last_sign != "0" {
-                            Utils.setLoginMultipleFPin(value: f_pin)
+                    if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSignUpSignInAPI(p_name: name, p_password: md5Hex, deviceFingerprint: df, publicKey: pk, signature: sign), timeout: 30 * 1000) {
+                        if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "20" {
                             DispatchQueue.main.async {
-                                let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
-                                imageView.tintColor = .white
-                                let banner = FloatingNotificationBanner(title: "Multiple Login Detected...".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .info, colors: nil, iconPosition: .center)
-                                banner.show()
+                                self.showFailedSignUpIn(title: "Invalid user / Username and password does not match".localized())
                             }
-                            DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
-                                Nexilis.hideLoader(completion: {
-                                    if Nexilis.showFB {
-                                        Nexilis.floatingButton.removeFromSuperview()
-                                        Nexilis.floatingButton = FloatingButton()
-                                        Nexilis.addFB()
-                                    }
-                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onRefreshWebView"), object: nil, userInfo: nil)
-                                    if self.fromChangeNamePass{
-                                        var vc = self.navigationController?.presentingViewController
-                                        while vc?.presentingViewController != nil {
-                                            vc = vc?.presentingViewController
-                                        }
-                                        vc?.dismiss(animated: true, completion: nil)
-                                    }
-                                    else if !self.forceLogin {
-                                        self.navigationController?.popViewController(animated: true)
-                                    } else {
-                                        self.navigationController?.dismiss(animated: true)
-                                    }
-                                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
-                                        let dialog = DialogUnableAccess()
-                                        dialog.modalTransitionStyle = .crossDissolve
-                                        dialog.modalPresentationStyle = .overCurrentContext
-                                        UIApplication.shared.visibleViewController?.present(dialog, animated: true)
-                                    })
-                                })
-                            })
-                            return
-                        }
-                        self.deleteAllRecordDatabase()
-                        if(!id.isEmpty) {
-//                            Nexilis.changeUser(f_pin: device_id)
-                            SecureUserDefaults.shared.set(device_id, forKey: "device_id")
-                            Utils.setProfile(value: true)
-                            // pos registration
-                            _ = Nexilis.write(message: CoreMessage_TMessageBank.getPostRegistration(p_pin: id))
-                            DispatchQueue.global().asyncAfter(deadline: .now() + 2, execute: {
-                                Nexilis.getWhitelistFileExt()
-                            })
-                            DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
-                                Nexilis.hideLoader(completion: {
-                                    let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
-                                    imageView.tintColor = .white
-                                    let banner = FloatingNotificationBanner(title: "Successfully Sign-In".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
-                                    banner.show()
-                                    if Nexilis.showFB {
-                                        Nexilis.floatingButton.removeFromSuperview()
-                                        Nexilis.floatingButton = FloatingButton()
-                                        Nexilis.addFB()
-                                    }
-                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onRefreshWebView"), object: nil, userInfo: nil)
-                                    if self.fromChangeNamePass{
-                                        var vc = self.navigationController?.presentingViewController
-                                        while vc?.presentingViewController != nil {
-                                            vc = vc?.presentingViewController
-                                        }
-                                        vc?.dismiss(animated: true, completion: nil)
-                                    }
-                                    else if !self.forceLogin {
-                                        self.navigationController?.popViewController(animated: true)
-                                    } else {
-                                        self.navigationController?.dismiss(animated: true)
-                                    }
-                                    self.isDismiss?(thumb)
-                                })
-                            })
+                        } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "11" {
+                            DispatchQueue.main.async {
+                                self.showFailedSignUpIn(title: "Failed, unknown user".localized())
+                            }
+                        } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "4u" {
+                            DispatchQueue.main.async {
+                                self.showFailedSignUpIn(title: "Failed, blocked user".localized())
+                            }
+                        } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "13" {
+                            DispatchQueue.main.async {
+                                self.showFailedSignUpIn(title: "Failed, This user is not registered on this device".localized())
+                            }
+                        } else if !response.isOk() {
+                            DispatchQueue.main.async {
+                                self.showFailedSignUpIn(title: "Failed".localized())
+                            }
+                        } else {
+                            self.successSubmit(response: response, first: first, last: last)
                         }
                     } else {
-                        Database.shared.database?.inTransaction({ (fmdb, rollback) in
-                            do {
-                                if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: "SELECT * FROM BUDDY where f_pin = '\(idMe)' ") {
-                                    if !cursorData.next() {
-                                        _ = Nexilis.write(message: CoreMessage_TMessageBank.getPostRegistration(p_pin: idMe))
-                                    } else {
-                                        _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: ["first_name": first , "last_name": last], _where: "f_pin = '\(idMe)'")
-                                    }
-                                    cursorData.close()
-                                }
-                            } catch {
-                                rollback.pointee = true
-                                print("Access database error: \(error.localizedDescription)")
-                            }
-                        })
-                        Utils.setProfile(value: true)
-                        Nexilis.getWhitelistFileExt()
-    //                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateFifthTab"), object: nil, userInfo: nil)
                         DispatchQueue.main.async {
-                            Nexilis.hideLoader(completion: {
-                                let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
-                                imageView.tintColor = .white
-                                let banner = FloatingNotificationBanner(title: "Successfully Sign-Up".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
-                                banner.show()
-                                if self.fromChangeNamePass{
-                                    var vc = self.navigationController?.presentingViewController
-                                    while vc?.presentingViewController != nil {
-                                        vc = vc?.presentingViewController
-                                    }
-                                    vc?.dismiss(animated: true, completion: nil)
-                                }
-                                else if !self.forceLogin {
-                                    self.navigationController?.popViewController(animated: true)
-                                } else {
-                                    self.navigationController?.dismiss(animated: true)
-                                }
-                            })
+                            self.showFailedSignUpIn(title: "Unable to access servers. Try again later".localized())
                         }
                     }
+                } else {
+                    DispatchQueue.main.async {
+                        self.showFailedSignUpIn(title: "Failed to get auth, please try again".localized())
+                    }
                 }
             } else {
                 DispatchQueue.main.async {
+                    self.showFailedSignUpIn(title: "Unable to access servers. Try again later".localized())
+                }
+            }
+        }
+    }
+    
+    private func successSubmit(response: TMessage, first: String, last: String, email: String = "", number: String = "") {
+        let sign = response.getBody(key: CoreMessage_TMessageKey.SIGN, default_value: "")
+        if sign == "1" || !email.isEmpty || !number.isEmpty {
+            let id = response.getBody(key: CoreMessage_TMessageKey.F_PIN, default_value: "")
+            let f_pin = response.getBody(key: CoreMessage_TMessageKey.F_PIN_REAL, default_value: "")
+            let device_id = response.getBody(key: CoreMessage_TMessageKey.IMEI, default_value: id)
+            let last_sign = response.getBody(key: CoreMessage_TMessageKey.LAST_SIGN, default_value: "0")
+            //print("last sign: \(last_sign)")
+            if last_sign != "0" {
+                Utils.setLoginMultipleFPin(value: f_pin)
+                DispatchQueue.main.async {
+                    let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
+                    imageView.tintColor = .white
+                    let banner = FloatingNotificationBanner(title: "Multiple Login Detected...".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .info, colors: nil, iconPosition: .center)
+                    banner.show()
+                }
+                DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
+                    Nexilis.hideLoader(completion: {
+                        if Nexilis.showFB {
+                            Nexilis.floatingButton.removeFromSuperview()
+                            Nexilis.floatingButton = FloatingButton()
+                            Nexilis.addFB()
+                        }
+                        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onRefreshWebView"), object: nil, userInfo: nil)
+                        if self.forceLogin {
+                            self.navigationController?.dismiss(animated: true)
+                        } else {
+                            let controllers = self.navigationController?.viewControllers
+                            if controllers![controllers!.count - 2] is SignInOption {
+                                self.navigationController?.popToViewController(controllers![0], animated: true)
+                            } else {
+                                self.navigationController?.popViewController(animated: true)
+                            }
+                        }
+                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
+                            let dialog = DialogUnableAccess()
+                            dialog.modalTransitionStyle = .crossDissolve
+                            dialog.modalPresentationStyle = .overCurrentContext
+                            UIApplication.shared.visibleViewController?.present(dialog, animated: true)
+                        })
+                    })
+                })
+                return
+            }
+            self.deleteAllRecordDatabase()
+            if(!id.isEmpty) {
+//                            Nexilis.changeUser(f_pin: device_id)
+                SecureUserDefaults.shared.set(device_id, forKey: "device_id")
+                Utils.setProfile(value: true)
+                // pos registration
+                _ = Nexilis.write(message: CoreMessage_TMessageBank.getPostRegistration(p_pin: id))
+                DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
                     Nexilis.hideLoader(completion: {
-                        let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
+                        let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
                         imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Unable to access servers. Try again later".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                        let banner = FloatingNotificationBanner(title: "Successfully Sign-In".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
                         banner.show()
+                        if Nexilis.showFB {
+                            Nexilis.floatingButton.removeFromSuperview()
+                            Nexilis.floatingButton = FloatingButton()
+                            Nexilis.addFB()
+                        }
+                        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onRefreshWebView"), object: nil, userInfo: nil)
+                        if self.forceLogin {
+                            self.navigationController?.dismiss(animated: true)
+                        } else {
+                            let controllers = self.navigationController?.viewControllers
+                            if controllers![controllers!.count - 2] is SignInOption {
+                                self.navigationController?.popToViewController(controllers![controllers!.count - 3], animated: true)
+                            } else {
+                                self.navigationController?.popViewController(animated: true)
+                            }
+                        }
                     })
+                })
+            }
+        } else {
+            let idMe = User.getMyPin()!
+            Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                var firstN = first
+                if firstN.isEmpty {
+                    if !email.isEmpty {
+                        firstN = email
+                    } else {
+                        firstN = number
+                    }
+                }
+                do {
+                    if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: "SELECT * FROM BUDDY where f_pin = '\(idMe)' ") {
+                        if !cursorData.next() {
+                            _ = Nexilis.write(message: CoreMessage_TMessageBank.getPostRegistration(p_pin: idMe))
+                        } else {
+                            _ = Database.shared.updateRecord(fmdb: fmdb, table: "BUDDY", cvalues: ["first_name": first , "last_name": last], _where: "f_pin = '\(idMe)'")
+                        }
+                        cursorData.close()
+                    }
+                } catch {
+                    rollback.pointee = true
+                    print("Access database error: \(error.localizedDescription)")
                 }
+            })
+            Utils.setProfile(value: true)
+            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onRefreshWebView"), object: nil, userInfo: nil)
+            DispatchQueue.main.async {
+                Nexilis.hideLoader(completion: {
+                    let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
+                    imageView.tintColor = .white
+                    let banner = FloatingNotificationBanner(title: "Successfully Sign-Up".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
+                    banner.show()
+                    if self.forceLogin {
+                        self.navigationController?.dismiss(animated: true)
+                    } else {
+                        let controllers = self.navigationController?.viewControllers
+                        if controllers![controllers!.count - 2] is SignInOption {
+                            self.navigationController?.popToViewController(controllers![controllers!.count - 3], animated: true)
+                        } else {
+                            self.navigationController?.popViewController(animated: true)
+                        }
+                    }
+                })
             }
         }
     }

+ 6 - 1
NexilisLite/NexilisLite/Source/View/Control/VerifyEmail.swift

@@ -11,6 +11,7 @@ import NotificationBannerSwift
 class VerifyEmail: UIViewController, UITextFieldDelegate, OTPTextFieldDelegate {
     var initialBounds: CGRect?
     var email = ""
+    var msisdn = ""
     let txtField1 = OTPTextField()
     let txtField2 = OTPTextField()
     let txtField3 = OTPTextField()
@@ -20,6 +21,7 @@ class VerifyEmail: UIViewController, UITextFieldDelegate, OTPTextFieldDelegate {
     var firstLoad = true
     var isDismiss: ((String) -> ())?
     var showWrongOTP = ""
+    var isMSISDN = false
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -44,6 +46,9 @@ class VerifyEmail: UIViewController, UITextFieldDelegate, OTPTextFieldDelegate {
         descVerify.numberOfLines = 0
         descVerify.textAlignment = .center
         descVerify.text = "Your verification code has been sent to".localized() + "\n" + email + "\n" + "Please check your email and enter the code sent".localized()
+        if isMSISDN {
+            descVerify.text = "Your verification code has been sent to".localized() + "\n" + msisdn + "\n" + "Please check in your message app and enter the code sent".localized()
+        }
         view.addSubview(descVerify)
         descVerify.anchor(top: titleVerify.bottomAnchor, paddingTop: 8, centerX: view.centerXAnchor)
         
@@ -126,7 +131,7 @@ class VerifyEmail: UIViewController, UITextFieldDelegate, OTPTextFieldDelegate {
                 imageView.tintColor = .white
                 let banner = FloatingNotificationBanner(title: "Expired OTP".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
                 banner.show()
-            } else if showWrongOTP == "4t" {
+            } else {
                 let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                 imageView.tintColor = .white
                 let banner = FloatingNotificationBanner(title: "Invalid OTP".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)

+ 1 - 1
NexilisLite/NexilisLite/Source/View/Streaming/QmeraCreateStreamingViewController.swift

@@ -559,7 +559,7 @@ public class QmeraCreateStreamingViewController: UITableViewController {
                 cell.selectionStyle = .default
             } else {
                 let data = groups[indexPath.row - 1]
-                getImage(name: data.profile, placeholderImage: UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+                getImage(name: data.profile, placeholderImage: UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
                     content.image = image
                 }
                 content.text = data.name

+ 1 - 1
NexilisLite/NexilisLite/Source/View/Streaming/QmeraGroupChooserViewController.swift

@@ -176,7 +176,7 @@ class QmeraGroupStreamingViewController: UITableViewController {
             group = available[indexPath.row]
         }
         content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
-        getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+        getImage(name: group.profile, placeholderImage: UIImage(named: "Conversation---Black", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
             content.image = image
         }
         content.text = group.name

BIN
NexilisUC/.DS_Store