Ver Fonte

update fix bugs and add share from external app to internal app

alqindiirsyam há 6 meses atrás
pai
commit
ee9078021f
33 ficheiros alterados com 1709 adições e 198 exclusões
  1. 187 7
      AppBuilder/AppBuilder.xcodeproj/project.pbxproj
  2. 4 2
      AppBuilder/AppBuilder/AppBuilder.entitlements
  3. 4 0
      AppBuilder/AppBuilder/FirstTabViewController.swift
  4. 2 2
      AppBuilder/AppBuilder/SecondTabViewController.swift
  5. 4 0
      AppBuilder/AppBuilder/ThirdTabViewController.swift
  6. 8 2
      AppBuilder/AppBuilder/ViewController.swift
  7. 10 0
      AppBuilder/AppBuilderShare/AppBuilderShare.entitlements
  8. 44 0
      AppBuilder/AppBuilderShare/Base.lproj/MainInterface.storyboard
  9. 27 0
      AppBuilder/AppBuilderShare/Info.plist
  10. 755 0
      AppBuilder/AppBuilderShare/ShareViewController.swift
  11. BIN
      NexilisLite/.DS_Store
  12. 123 0
      NexilisLite/NexilisLite.xcodeproj/project.pbxproj
  13. 257 1
      NexilisLite/NexilisLite/Source/APIS.swift
  14. 8 8
      NexilisLite/NexilisLite/Source/Callback.swift
  15. 1 10
      NexilisLite/NexilisLite/Source/Extension.swift
  16. 8 10
      NexilisLite/NexilisLite/Source/FloatingButton/FloatingButton.swift
  17. 2 2
      NexilisLite/NexilisLite/Source/IncomingThread.swift
  18. 1 1
      NexilisLite/NexilisLite/Source/Model/Chat.swift
  19. 68 13
      NexilisLite/NexilisLite/Source/Nexilis.swift
  20. 31 10
      NexilisLite/NexilisLite/Source/Utils.swift
  21. 1 0
      NexilisLite/NexilisLite/Source/View/Call/QmeraAudioViewController.swift
  22. 1 0
      NexilisLite/NexilisLite/Source/View/Call/QmeraVideoViewController.swift
  23. 14 11
      NexilisLite/NexilisLite/Source/View/Chat/ChatGPTBotView.swift
  24. 34 21
      NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift
  25. 43 24
      NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift
  26. 5 48
      NexilisLite/NexilisLite/Source/View/Chat/EditorStarMessages.swift
  27. 10 10
      NexilisLite/NexilisLite/Source/View/Chat/PreviewAttachmentImageVideo.swift
  28. 13 0
      NexilisLite/NexilisLite/Source/View/Chat/SecureFolderView.swift
  29. 1 1
      NexilisLite/NexilisLite/Source/View/Control/ConfigureFloatingButton.swift
  30. 31 1
      NexilisLite/NexilisLite/Source/View/Control/ContactChatViewController.swift
  31. 4 3
      NexilisLite/NexilisLite/Source/View/Control/HistoryBroadcastViewController.swift
  32. 7 7
      NexilisLite/NexilisLite/Source/View/Control/HistoryCCViewController.swift
  33. 1 4
      NexilisLite/NexilisLite/Source/View/Control/SettingTableViewController.swift

+ 187 - 7
AppBuilder/AppBuilder.xcodeproj/project.pbxproj

@@ -3,7 +3,7 @@
 	archiveVersion = 1;
 	classes = {
 	};
-	objectVersion = 54;
+	objectVersion = 70;
 	objects = {
 
 /* Begin PBXBuildFile section */
@@ -19,6 +19,7 @@
 		A42ED92227F30BA200B0FAB7 /* FirstTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A42ED92127F30BA200B0FAB7 /* FirstTabViewController.swift */; };
 		A42ED92427F3FC2F00B0FAB7 /* SecondTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A42ED92327F3FC2F00B0FAB7 /* SecondTabViewController.swift */; };
 		A42ED92627F439A200B0FAB7 /* ThirdTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A42ED92527F439A200B0FAB7 /* ThirdTabViewController.swift */; };
+		CD865A8A2D5B17B2007F741E /* AppBuilderShare.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = CD865A802D5B17B2007F741E /* AppBuilderShare.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 		CD9D59D92BEE1D30008014B4 /* digisales_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = CD9D59CE2BEE1D2F008014B4 /* digisales_icon.png */; };
 		CD9D59DA2BEE1D30008014B4 /* ikn_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = CD9D59CF2BEE1D2F008014B4 /* ikn_icon.png */; };
 		CD9D59DB2BEE1D30008014B4 /* gudeg_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = CD9D59D02BEE1D2F008014B4 /* gudeg_icon.png */; };
@@ -32,6 +33,16 @@
 		CD9D59E32BEE1D30008014B4 /* nu_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = CD9D59D82BEE1D30008014B4 /* nu_icon.png */; };
 /* End PBXBuildFile section */
 
+/* Begin PBXContainerItemProxy section */
+		CD865A882D5B17B2007F741E /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 2401CE8E275490DB00B323BB /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = CD865A7F2D5B17B2007F741E;
+			remoteInfo = AppBuilderShare;
+		};
+/* End PBXContainerItemProxy section */
+
 /* Begin PBXCopyFilesBuildPhase section */
 		247E0A722796969200430E5F /* Embed Frameworks */ = {
 			isa = PBXCopyFilesBuildPhase;
@@ -43,12 +54,23 @@
 			name = "Embed Frameworks";
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		CD865AA62D5B2325007F741E /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		CDEE3DD129B06E1E00B420E5 /* Embed Foundation Extensions */ = {
 			isa = PBXCopyFilesBuildPhase;
 			buildActionMask = 2147483647;
 			dstPath = "";
 			dstSubfolderSpec = 13;
 			files = (
+				CD865A8A2D5B17B2007F741E /* AppBuilderShare.appex in Embed Foundation Extensions */,
 			);
 			name = "Embed Foundation Extensions";
 			runOnlyForDeploymentPostprocessing = 0;
@@ -71,6 +93,7 @@
 		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>"; };
 		A42ED92527F439A200B0FAB7 /* ThirdTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdTabViewController.swift; sourceTree = "<group>"; };
+		CD865A802D5B17B2007F741E /* AppBuilderShare.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AppBuilderShare.appex; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD9D59CE2BEE1D2F008014B4 /* digisales_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = digisales_icon.png; sourceTree = "<group>"; };
 		CD9D59CF2BEE1D2F008014B4 /* ikn_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ikn_icon.png; sourceTree = "<group>"; };
 		CD9D59D02BEE1D2F008014B4 /* gudeg_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = gudeg_icon.png; sourceTree = "<group>"; };
@@ -86,6 +109,20 @@
 		D246950EF11B497A8C2CE4B7 /* Pods_AppBuilder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppBuilder.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
 
+/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
+		CD865A8B2D5B17B2007F741E /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
+			isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
+			membershipExceptions = (
+				Info.plist,
+			);
+			target = CD865A7F2D5B17B2007F741E /* AppBuilderShare */;
+		};
+/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
+
+/* Begin PBXFileSystemSynchronizedRootGroup section */
+		CD865A812D5B17B2007F741E /* AppBuilderShare */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (CD865A8B2D5B17B2007F741E /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = AppBuilderShare; sourceTree = "<group>"; };
+/* End PBXFileSystemSynchronizedRootGroup section */
+
 /* Begin PBXFrameworksBuildPhase section */
 		2401CE93275490DB00B323BB /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
@@ -95,6 +132,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		CD865A7D2D5B17B2007F741E /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -102,6 +146,7 @@
 			isa = PBXGroup;
 			children = (
 				2401CE98275490DB00B323BB /* AppBuilder */,
+				CD865A812D5B17B2007F741E /* AppBuilderShare */,
 				76BBCA5783FCBB787B29FC82 /* Frameworks */,
 				6E32BCCF4DE50EE1A90E8AAE /* Pods */,
 				2401CE97275490DB00B323BB /* Products */,
@@ -112,6 +157,7 @@
 			isa = PBXGroup;
 			children = (
 				2401CE96275490DB00B323BB /* AppBuilder.app */,
+				CD865A802D5B17B2007F741E /* AppBuilderShare.appex */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -182,12 +228,36 @@
 			buildRules = (
 			);
 			dependencies = (
+				CD865A892D5B17B2007F741E /* PBXTargetDependency */,
 			);
 			name = AppBuilder;
 			productName = TestQmeraLite;
 			productReference = 2401CE96275490DB00B323BB /* AppBuilder.app */;
 			productType = "com.apple.product-type.application";
 		};
+		CD865A7F2D5B17B2007F741E /* AppBuilderShare */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CD865A8C2D5B17B2007F741E /* Build configuration list for PBXNativeTarget "AppBuilderShare" */;
+			buildPhases = (
+				CD865A7C2D5B17B2007F741E /* Sources */,
+				CD865A7D2D5B17B2007F741E /* Frameworks */,
+				CD865A7E2D5B17B2007F741E /* Resources */,
+				CD865AA62D5B2325007F741E /* Embed Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			fileSystemSynchronizedGroups = (
+				CD865A812D5B17B2007F741E /* AppBuilderShare */,
+			);
+			name = AppBuilderShare;
+			packageProductDependencies = (
+			);
+			productName = AppBuilderShare;
+			productReference = CD865A802D5B17B2007F741E /* AppBuilderShare.appex */;
+			productType = "com.apple.product-type.app-extension";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -200,6 +270,9 @@
 					2401CE95275490DB00B323BB = {
 						CreatedOnToolsVersion = 12.5.1;
 					};
+					CD865A7F2D5B17B2007F741E = {
+						CreatedOnToolsVersion = 16.2;
+					};
 				};
 			};
 			buildConfigurationList = 2401CE91275490DB00B323BB /* Build configuration list for PBXProject "AppBuilder" */;
@@ -216,6 +289,7 @@
 			projectRoot = "";
 			targets = (
 				2401CE95275490DB00B323BB /* AppBuilder */,
+				CD865A7F2D5B17B2007F741E /* AppBuilderShare */,
 			);
 		};
 /* End PBXProject section */
@@ -242,6 +316,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		CD865A7E2D5B17B2007F741E /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
@@ -302,8 +383,23 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		CD865A7C2D5B17B2007F741E /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
+/* Begin PBXTargetDependency section */
+		CD865A892D5B17B2007F741E /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = CD865A7F2D5B17B2007F741E /* AppBuilderShare */;
+			targetProxy = CD865A882D5B17B2007F741E /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
 /* Begin PBXVariantGroup section */
 		2401CE9F275490DB00B323BB /* Main.storyboard */ = {
 			isa = PBXVariantGroup;
@@ -451,7 +547,7 @@
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 5;
-				DEVELOPMENT_TEAM = FR2C2CZUYZ;
+				DEVELOPMENT_TEAM = 6RLKHZJ57M;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Frameworks",
@@ -463,8 +559,8 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 5.0.4;
-				PRODUCT_BUNDLE_IDENTIFIER = io.newuniverse.AppBuilder;
+				MARKETING_VERSION = 5.0.5;
+				PRODUCT_BUNDLE_IDENTIFIER = io.nexilis.appbuilder;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@@ -487,7 +583,7 @@
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 5;
-				DEVELOPMENT_TEAM = FR2C2CZUYZ;
+				DEVELOPMENT_TEAM = 6RLKHZJ57M;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Frameworks",
@@ -499,8 +595,8 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 5.0.4;
-				PRODUCT_BUNDLE_IDENTIFIER = io.newuniverse.AppBuilder;
+				MARKETING_VERSION = 5.0.5;
+				PRODUCT_BUNDLE_IDENTIFIER = io.nexilis.appbuilder;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@@ -512,6 +608,81 @@
 			};
 			name = Release;
 		};
+		CD865A8D2D5B17B2007F741E /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CODE_SIGN_ENTITLEMENTS = AppBuilderShare/AppBuilderShare.entitlements;
+				CODE_SIGN_IDENTITY = "Apple Development";
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 5;
+				DEVELOPMENT_TEAM = 6RLKHZJ57M;
+				ENABLE_USER_SCRIPT_SANDBOXING = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu17;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = AppBuilderShare/Info.plist;
+				INFOPLIST_KEY_CFBundleDisplayName = AppBuilderShare;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "";
+				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@executable_path/../../Frameworks",
+				);
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+				MARKETING_VERSION = 5.0.5;
+				PRODUCT_BUNDLE_IDENTIFIER = io.nexilis.appbuilder.AppBuilderShare;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+				SUPPORTS_MACCATALYST = NO;
+				SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+				SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = 1;
+			};
+			name = Debug;
+		};
+		CD865A8E2D5B17B2007F741E /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CODE_SIGN_ENTITLEMENTS = AppBuilderShare/AppBuilderShare.entitlements;
+				CODE_SIGN_IDENTITY = "Apple Development";
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 5;
+				DEVELOPMENT_TEAM = 6RLKHZJ57M;
+				ENABLE_USER_SCRIPT_SANDBOXING = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu17;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = AppBuilderShare/Info.plist;
+				INFOPLIST_KEY_CFBundleDisplayName = AppBuilderShare;
+				INFOPLIST_KEY_NSHumanReadableCopyright = "";
+				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@executable_path/../../Frameworks",
+				);
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+				MARKETING_VERSION = 5.0.5;
+				PRODUCT_BUNDLE_IDENTIFIER = io.nexilis.appbuilder.AppBuilderShare;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+				SUPPORTS_MACCATALYST = NO;
+				SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+				SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = 1;
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -533,6 +704,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		CD865A8C2D5B17B2007F741E /* Build configuration list for PBXNativeTarget "AppBuilderShare" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CD865A8D2D5B17B2007F741E /* Debug */,
+				CD865A8E2D5B17B2007F741E /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 2401CE8E275490DB00B323BB /* Project object */;

+ 4 - 2
AppBuilder/AppBuilder/AppBuilder.entitlements

@@ -4,7 +4,9 @@
 <dict>
 	<key>aps-environment</key>
 	<string>development</string>
-	<key>com.apple.developer.calling-app</key>
-	<true/>
+	<key>com.apple.security.application-groups</key>
+	<array>
+		<string>group.nexilis.share</string>
+	</array>
 </dict>
 </plist>

+ 4 - 0
AppBuilder/AppBuilder/FirstTabViewController.swift

@@ -692,6 +692,10 @@ extension FirstTabViewController: WKUIDelegate, WKNavigationDelegate {
             return
         }
         if allowedURLs.contains(url.absoluteString) {
+            if ViewController.alwaysHideButton {
+                ViewController.alwaysHideButton = false
+                showTabBar()
+            }
             print("✅ URL already allowed: \(url)")
             decisionHandler(.allow)
             return

+ 2 - 2
AppBuilder/AppBuilder/SecondTabViewController.swift

@@ -525,8 +525,8 @@ class SecondTabViewController: UIViewController, UIScrollViewDelegate, UIGesture
     }
     
     override func viewDidAppear(_ animated: Bool) {
-        self.navigationController?.navigationBar.topItem?.title = "Chats".localized() + " & " + "Forums".localized()
-        self.navigationController?.navigationBar.setNeedsLayout()
+//        self.navigationController?.navigationBar.topItem?.title = "Chats".localized() + " & " + "Forums".localized()
+//        self.navigationController?.navigationBar.setNeedsLayout()
         DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
             var viewController = UIApplication.shared.windows.first!.rootViewController
             if !(viewController is ViewController) {

+ 4 - 0
AppBuilder/AppBuilder/ThirdTabViewController.swift

@@ -712,6 +712,10 @@ extension ThirdTabViewController: WKUIDelegate {
             return
         }
         if allowedURLs.contains(url.absoluteString) {
+            if ViewController.alwaysHideButton {
+                ViewController.alwaysHideButton = false
+                showTabBar()
+            }
             print("✅ URL already allowed: \(url)")
             decisionHandler(.allow)
             return

+ 8 - 2
AppBuilder/AppBuilder/ViewController.swift

@@ -63,7 +63,7 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
         super.viewDidLoad()
         DispatchQueue.main.async { [self] in
             while !Utils.getFinishInitPrefsr() || HTTPCookieStorage.shared.cookies(for: URL(string: Utils.getDomainOpr())!)!.count == 0 {
-               
+                Thread.sleep(forTimeInterval: 1)
             }
             startView()
         }
@@ -230,6 +230,7 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
 //            navigationController?.setNavigationBarHidden(true, animated: false)
 //        }
         
+        self.selectedIndex = Utils.getLastTabSelected()
         self.setViewControllers(tabs, animated: false)
         if(cpaasMode == PrefsUtil.CPAAS_MODE_DOCKED || cpaasMode == PrefsUtil.CPAAS_MODE_MIX){
             if(customTab.count == 3){
@@ -240,7 +241,6 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
         let center: NotificationCenter = NotificationCenter.default
         center.addObserver(self, selector: #selector(checkCounter), name: NSNotification.Name(rawValue: Nexilis.listenerReceiveChat), object: nil)
         center.addObserver(self, selector: #selector(checkCounter), name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil)
-        self.selectedIndex = Utils.getLastTabSelected()
         if !Utils.getIndicatorTabImage().isEmpty {
             let indicatorImage = Utils.getIndicatorTabImage()
             let fullUrl = "\(PrefsUtil.getURLBase())get_file_from_path?img=\(indicatorImage)"
@@ -378,6 +378,10 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
 //        tabBar.frame.origin.y = view.frame.height - 65
 //    }
     
+    override func viewDidAppear(_ animated: Bool) {
+        Nexilis.floatingButton.isHidden = false
+    }
+    
     func settingDelegate() {
         if self.navigationController?.visibleViewController == fourthTab {
             return
@@ -385,6 +389,7 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
         if self.viewControllers?.firstIndex(of: fourthTab!) == nil {
             let vc = fourthTab!
             vc.notInTab = true
+            Nexilis.floatingButton.isHidden = true
             self.navigationController?.show(vc, sender: nil)
         } else {
             self.selectedIndex = (self.viewControllers?.firstIndex(of: fourthTab!))!
@@ -645,6 +650,7 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
             }
         }
         self.selectedViewController?.viewWillAppear(false)
+        self.selectedViewController?.viewDidAppear(false)
     }
     
     func showPrivacyPolicyView() {

+ 10 - 0
AppBuilder/AppBuilderShare/AppBuilderShare.entitlements

@@ -0,0 +1,10 @@
+<?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>com.apple.security.application-groups</key>
+	<array>
+		<string>group.nexilis.share</string>
+	</array>
+</dict>
+</plist>

+ 44 - 0
AppBuilder/AppBuilderShare/Base.lproj/MainInterface.storyboard

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="wOh-SD-URu">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--Share View Controller-->
+        <scene sceneID="ceB-am-kn3">
+            <objects>
+                <viewController id="j1y-V4-xli" customClass="ShareViewController" customModule="AppBuilderShare" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
+                        <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
+                        <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
+                    </view>
+                    <navigationItem key="navigationItem" id="vRr-2L-dxO"/>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="126" y="-2"/>
+        </scene>
+        <!--Navigation Controller-->
+        <scene sceneID="YaN-2c-Gqh">
+            <objects>
+                <navigationController id="wOh-SD-URu" sceneMemberID="viewController">
+                    <navigationBar key="navigationBar" contentMode="scaleToFill" id="qdF-yN-XCL">
+                        <rect key="frame" x="0.0" y="59" width="393" height="44"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </navigationBar>
+                    <connections>
+                        <segue destination="j1y-V4-xli" kind="relationship" relationship="rootViewController" id="6mN-95-KNx"/>
+                    </connections>
+                </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="tbq-fx-PBf" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-961" y="-2"/>
+        </scene>
+    </scenes>
+</document>

+ 27 - 0
AppBuilder/AppBuilderShare/Info.plist

@@ -0,0 +1,27 @@
+<?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>NSExtension</key>
+	<dict>
+		<key>NSExtensionAttributes</key>
+		<dict>
+			<key>NSExtensionActivationRule</key>
+            <dict>
+                <key>NSExtensionActivationSupportsImageWithMaxCount</key>
+                <integer>1</integer>
+                <key>NSExtensionActivationSupportsMovieWithMaxCount</key>
+                <integer>1</integer>
+                <key>NSExtensionActivationSupportsText</key>
+                <true/>
+                <key>NSExtensionActivationSupportsFileWithMaxCount</key>
+                <integer>1</integer>
+            </dict>
+		</dict>
+		<key>NSExtensionMainStoryboard</key>
+		<string>MainInterface</string>
+		<key>NSExtensionPointIdentifier</key>
+		<string>com.apple.share-services</string>
+	</dict>
+</dict>
+</plist>

+ 755 - 0
AppBuilder/AppBuilderShare/ShareViewController.swift

@@ -0,0 +1,755 @@
+//
+//  ShareViewController.swift
+//  AppBuilderShare
+//
+//  Created by Qindi on 11/02/25.
+//
+
+import UIKit
+import Social
+import UniformTypeIdentifiers
+import AVFoundation
+import QuickLook
+
+class ShareViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UITextViewDelegate, QLPreviewControllerDataSource {
+    let tableView = UITableView()
+    let searchBar = UISearchBar()
+    var contacts: [Contact] = []
+    var filteredContacts: [Contact] = []
+    var selectedContact: Contact!
+    private var textViewBottomConstraint: NSLayoutConstraint?
+    private var containerBottomConstraint: NSLayoutConstraint?
+    private var heightTextView: NSLayoutConstraint?
+    let vcHandleText = UIViewController()
+    let vcHandleImage = UIViewController()
+    let vcHandleVideo = UIViewController()
+    let vcHandleFile = UIViewController()
+    var textView = UITextView()
+    var typeShareNum = 0
+    var selectedImage: URL!
+    var selectedVideo: URL!
+    var selectedFile: URL!
+    private var previewView: VideoPreviewView?
+    let previewController = QLPreviewController()
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        loadCustomContacts()
+        registerKeyboardNotifications()
+    }
+    
+    deinit {
+        NotificationCenter.default.removeObserver(self) // Remove observers when view controller deallocates
+    }
+    
+    override func viewDidAppear(_ animated: Bool) {
+        setupUI()
+    }
+    
+    func setupUI() {
+        let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancelAction))
+        cancelButton.tintColor = .label
+        self.navigationItem.leftBarButtonItem = cancelButton
+
+        // Search Bar (Right)
+        searchBar.placeholder = "Search"
+        searchBar.delegate = self
+        searchBar.sizeToFit()
+        self.navigationItem.titleView = searchBar
+        
+        self.navigationController?.navigationBar.backgroundColor = .systemBackground
+        self.navigationController?.navigationBar.tintColor = .label
+
+        // TableView Setup
+        tableView.translatesAutoresizingMaskIntoConstraints = false
+        tableView.delegate = self
+        tableView.dataSource = self
+        tableView.register(ContactCell.self, forCellReuseIdentifier: "ContactCell")
+        tableView.separatorStyle = .singleLine
+        view.addSubview(tableView)
+
+        // Auto Layout Constraints
+        NSLayoutConstraint.activate([
+            tableView.topAnchor.constraint(equalTo: view.topAnchor),
+            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
+            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
+        ])
+    }
+
+    func loadCustomContacts() {
+        if let userDefaults = UserDefaults(suiteName: "group.nexilis.share"),
+           let value = userDefaults.string(forKey: "shareContacts") {
+            if let jsonData = value.data(using: .utf8) {
+                do {
+                    if let jsonArray = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [[String: Any]] {
+                        for json in jsonArray {
+                            let id = json["id"] as? String ?? ""
+                            let name = json["name"] as? String ?? ""
+                            let imageId = json["image"] as? String ?? ""
+                            let type = json["type"] as? Int ?? 0
+                            var profileImage = type == 0 ? UIImage(systemName: "person.fill") : UIImage(systemName: "bubble.right.fill")
+                            if !imageId.isEmpty {
+                                if let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.nexilis.share") {
+                                    let sharedFileURL = appGroupURL.appendingPathComponent(imageId)
+                                    if FileManager.default.fileExists(atPath: sharedFileURL.path) {
+                                        profileImage = UIImage(contentsOfFile: sharedFileURL.path)
+                                    }
+                                }
+                            }
+                            contacts.append(Contact(id: id, name: name, profileImage: profileImage, imageId: imageId, typeContact: "\(type)"))
+                        }
+                        filteredContacts = contacts
+                        tableView.reloadData()
+                    }
+                } catch {
+                    print("Error parsing JSON: \(error)")
+                }
+            }
+        }
+    }
+
+    // TableView DataSource Methods
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return filteredContacts.count
+    }
+    
+    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return 60
+    }
+
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell", for: indexPath) as! ContactCell
+        cell.separatorInset = UIEdgeInsets(top: 0, left: 65, bottom: 0, right: 25)
+        let contact = filteredContacts[indexPath.row]
+        cell.configure(with: contact)
+        return cell
+    }
+
+    // Handle Contact Selection
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        tableView.deselectRow(at: indexPath, animated: true)
+        selectedContact = filteredContacts[indexPath.row]
+        handleSharedContent(selectedContact)
+    }
+
+    // Cancel Button Action
+    @objc func cancelAction() {
+        self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
+    }
+    
+    @objc func sendAction() {
+        if let userDefaults = UserDefaults(suiteName: "group.nexilis.share") {
+            do {
+                var dataShared: [String: Any] = [:]
+                dataShared["typeShare"] = typeShareNum
+                dataShared["typeContact"] = selectedContact.typeContact
+                dataShared["idContact"] = selectedContact.id
+                dataShared["data"] = textView.text
+                if typeShareNum == TypeShare.image {
+                    let compressedImageName = "Nexilis_image_\(Int(Date().timeIntervalSince1970 * 1000))_\(selectedImage.lastPathComponent)"
+                    let thumbName = "THUMB_Nexilis_image_\(Int(Date().timeIntervalSince1970 * 1000))_\(selectedImage.lastPathComponent)"
+                    if let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.nexilis.share") {
+                        let sharedImageURL = appGroupURL.appendingPathComponent(compressedImageName)
+                        let sharedThumbURL = appGroupURL.appendingPathComponent(thumbName)
+                        try? UIImage(contentsOfFile: selectedImage.path)?.jpegData(compressionQuality: 0.25)?.write(to: sharedThumbURL)
+                        try? UIImage(contentsOfFile: selectedImage.path)?.jpegData(compressionQuality: 0.5)?.write(to: sharedImageURL)
+                    }
+                    dataShared["thumb"] = thumbName
+                    dataShared["image"] = compressedImageName
+                    let jsonData = try JSONSerialization.data(withJSONObject: dataShared, options: .prettyPrinted)
+                    if let jsonString = String(data: jsonData, encoding: .utf8) {
+                        userDefaults.set(jsonString, forKey: "sharedItem")
+                        userDefaults.synchronize()
+                    }
+                } else if typeShareNum == TypeShare.video {
+                    let dataVideo = try? Data(contentsOf: selectedVideo)
+                    if let dataVideotoCompress = dataVideo {
+                        let sizeInKB = Double(dataVideotoCompress.count) / 1024.0
+                        let sizeOfVideo = sizeInKB / 1024.0
+                        if (sizeOfVideo > 10.0) {
+                            let compressedURL = NSURL.fileURL(withPath: NSTemporaryDirectory() + UUID().uuidString + ".mp4")
+                            compressVideo(inputURL: selectedVideo,
+                                          outputURL: compressedURL) { exportSession in
+                                guard let session = exportSession else {
+                                    return
+                                }
+                                
+                                switch session.status {
+                                case .unknown:
+                                    break
+                                case .waiting:
+                                    break
+                                case .exporting:
+                                    break
+                                case .completed:
+                                    guard let compressedData = try? Data(contentsOf: compressedURL) else {
+                                        return
+                                    }
+                                    self.sendVideoToMainApp(compressedData, dataShared)
+                                case .failed:
+                                    break
+                                case .cancelled:
+                                    break
+                                @unknown default:
+                                    break
+                                }
+                            }
+                            return
+                        } else {
+                            self.sendVideoToMainApp(dataVideotoCompress, dataShared)
+                        }
+                    }
+                } else if typeShareNum == TypeShare.file {
+                    let fileName = selectedFile.lastPathComponent
+                    if let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.nexilis.share") {
+                        let sharedFileURL = appGroupURL.appendingPathComponent(fileName)
+                        try? Data(contentsOf: selectedFile).write(to: sharedFileURL)
+                    }
+                    dataShared["file"] = fileName
+                    let jsonData = try JSONSerialization.data(withJSONObject: dataShared, options: .prettyPrinted)
+                    if let jsonString = String(data: jsonData, encoding: .utf8) {
+                        userDefaults.set(jsonString, forKey: "sharedItem")
+                        userDefaults.synchronize()
+                    }
+                } else {
+                    let jsonData = try JSONSerialization.data(withJSONObject: dataShared, options: .prettyPrinted)
+                    if let jsonString = String(data: jsonData, encoding: .utf8) {
+                        userDefaults.set(jsonString, forKey: "sharedItem")
+                        userDefaults.synchronize()
+                    }
+                }
+            } catch {
+                
+            }
+        }
+        self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
+    }
+    
+    private func sendVideoToMainApp(_ data: Data, _ dataShared: [String: Any]) {
+        do {
+            var dataShared = dataShared
+            let originalVideoName = self.selectedVideo.lastPathComponent
+            let renamedVideoName = "Nexilis_video_\(Int(Date().timeIntervalSince1970 * 1000))_\(originalVideoName)"
+            let thumbName = "THUMB_Nexilis_video_\(Int(Date().timeIntervalSince1970 * 1000))_\(originalVideoName)"
+            if let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.nexilis.share") {
+                let sharedVideoURL = appGroupURL.appendingPathComponent(renamedVideoName)
+                let sharedThumbURL = appGroupURL.appendingPathComponent(thumbName)
+                try? data.write(to: sharedVideoURL)
+                let asset = AVURLAsset(url: self.selectedVideo, options: nil)
+                let imgGenerator = AVAssetImageGenerator(asset: asset)
+                imgGenerator.appliesPreferredTrackTransform = true
+                let cgImage = try imgGenerator.copyCGImage(at: CMTimeMake(value: 0, timescale: 1), actualTime: nil)
+                let thumbnail = UIImage(cgImage: cgImage)
+                try? thumbnail.jpegData(compressionQuality: 1.0)?.write(to: sharedThumbURL)
+                dataShared["thumb"] = thumbName
+                dataShared["video"] = renamedVideoName
+                let jsonData = try JSONSerialization.data(withJSONObject: dataShared, options: .prettyPrinted)
+                if let jsonString = String(data: jsonData, encoding: .utf8) {
+                    let userDefaults = UserDefaults(suiteName: "group.nexilis.share")
+                    userDefaults!.set(jsonString, forKey: "sharedItem")
+                    userDefaults!.synchronize()
+                }
+            }
+        } catch {
+            
+        }
+    }
+    
+    func compressVideo(inputURL: URL,
+                       outputURL: URL,
+                       handler:@escaping (_ exportSession: AVAssetExportSession?) -> Void) {
+        let urlAsset = AVURLAsset(url: inputURL, options: nil)
+        guard let exportSession = AVAssetExportSession(asset: urlAsset,
+                                                       presetName: AVAssetExportPresetMediumQuality) else {
+            handler(nil)
+            
+            return
+        }
+        
+        exportSession.outputURL = outputURL
+        exportSession.outputFileType = .mp4
+        exportSession.shouldOptimizeForNetworkUse = true
+        exportSession.exportAsynchronously {
+            handler(exportSession)
+        }
+    }
+    
+    @objc func backAction() {
+        if let previewView = previewView {
+            previewView.stopVideo()
+        }
+        self.dismiss(animated: false, completion: nil)
+    }
+
+    // SearchBar Delegate Methods
+    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
+        if searchText.isEmpty {
+            filteredContacts = contacts
+        } else {
+            filteredContacts = contacts.filter { $0.name.lowercased().contains(searchText.lowercased()) }
+        }
+        tableView.reloadData()
+    }
+    
+    private func registerKeyboardNotifications() {
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
+    }
+
+    @objc private func keyboardWillShow(_ notification: Notification) {
+        if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
+            let keyboardHeight = keyboardFrame.height
+            let info:NSDictionary = notification.userInfo! as NSDictionary
+            let duration: CGFloat = info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber as! CGFloat
+            updateTextViewBottomConstraint(-keyboardHeight - 20, duration)
+        }
+    }
+
+    @objc private func keyboardWillHide(_ notification: Notification) {
+        let info:NSDictionary = notification.userInfo! as NSDictionary
+        let duration: CGFloat = info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber as! CGFloat
+        updateTextViewBottomConstraint(-20, duration) // Reset bottom constraint
+    }
+
+    private func updateTextViewBottomConstraint(_ constant: CGFloat, _ duration: CGFloat) {
+        if typeShareNum == TypeShare.text {
+            textViewBottomConstraint?.constant = constant
+        } else {
+            containerBottomConstraint?.constant = constant + 20
+        }
+
+        UIView.animate(withDuration: TimeInterval(duration)) { // Smooth animation
+            self.view.layoutIfNeeded()
+        }
+    }
+    
+    func textViewDidChange(_ textView: UITextView) {
+        vcHandleText.navigationItem.rightBarButtonItem?.isEnabled = !textView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
+    }
+    
+    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
+        if text == "\n" {
+            textView.resignFirstResponder()
+            return false
+        }
+        return true
+    }
+    
+    func textViewDidChangeSelection(_ textView: UITextView) {
+        if typeShareNum == TypeShare.text {
+            return
+        }
+        let cursorPosition = textView.caretRect(for: textView.selectedTextRange!.start).origin
+        let doubleCurrentLine = cursorPosition.y / textView.font!.lineHeight
+        if doubleCurrentLine.isFinite {
+            let currentLine = Int(doubleCurrentLine)
+            UIView.animate(withDuration: 0.3) {
+                let numberOfLines = textView.textContainer.lineBreakMode == .byWordWrapping ? Int(textView.contentSize.height / textView.font!.lineHeight) - 1 : 1
+                if currentLine == 0 && numberOfLines == 1 {
+                    self.heightTextView?.constant = 45
+                } else if currentLine >= 4 {
+                    self.heightTextView?.constant = 95.0
+                } else if currentLine < 4 && numberOfLines < 5 {
+                    self.heightTextView?.constant = textView.contentSize.height
+                }
+            }
+        }
+    }
+    
+    func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
+        return 1
+    }
+    
+    func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
+        return selectedFile as QLPreviewItem
+    }
+    
+    private func buildAppearance(_ contact: Contact, _ viewVc: UIView) {
+        viewVc.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .black : .white
+        let buttonClose = UIButton(type: .system)
+        buttonClose.setImage(UIImage(systemName: "xmark.circle.fill")?.withConfiguration(UIImage.SymbolConfiguration(pointSize: 40)), for: .normal)
+        buttonClose.tintColor = .gray.withAlphaComponent(0.8)
+        buttonClose.imageView?.contentMode = .scaleAspectFit
+        buttonClose.clipsToBounds = true
+        buttonClose.addTarget(self, action: #selector(backAction), for: .touchUpInside)
+        viewVc.addSubview(buttonClose)
+        buttonClose.translatesAutoresizingMaskIntoConstraints = false
+        NSLayoutConstraint.activate([
+            buttonClose.leadingAnchor.constraint(equalTo: viewVc.leadingAnchor, constant: 20.0),
+            buttonClose.topAnchor.constraint(equalTo: viewVc.topAnchor, constant: 20.0),
+            buttonClose.widthAnchor.constraint(equalToConstant: 40.0),
+            buttonClose.heightAnchor.constraint(equalToConstant: 40.0),
+        ])
+        
+        let containerView = UIView()
+        containerView.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .black : .white
+        viewVc.addSubview(containerView)
+        containerView.translatesAutoresizingMaskIntoConstraints = false
+        NSLayoutConstraint.activate([
+            containerView.leadingAnchor.constraint(equalTo: viewVc.leadingAnchor),
+            containerView.trailingAnchor.constraint(equalTo: viewVc.trailingAnchor),
+            containerView.heightAnchor.constraint(equalToConstant: 55)
+        ])
+        containerBottomConstraint = containerView.bottomAnchor.constraint(equalTo: viewVc.bottomAnchor)
+        containerBottomConstraint?.isActive = true
+        
+        let containerTo = UIView()
+        containerView.addSubview(containerTo)
+        containerTo.translatesAutoresizingMaskIntoConstraints = false
+        containerTo.layer.cornerRadius = 8
+        containerTo.clipsToBounds = true
+        containerTo.backgroundColor = .darkGray
+        NSLayoutConstraint.activate([
+            containerTo.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 15),
+            containerTo.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -10),
+            containerTo.heightAnchor.constraint(equalToConstant: 30),
+            containerTo.widthAnchor.constraint(greaterThanOrEqualToConstant: 50)
+        ])
+        
+        let textTo = UILabel()
+        containerTo.addSubview(textTo)
+        textTo.text = contact.name
+        textTo.textColor = .white
+        textTo.font = .systemFont(ofSize: 15)
+        textTo.textAlignment = .center
+        textTo.translatesAutoresizingMaskIntoConstraints = false
+        NSLayoutConstraint.activate([
+            textTo.leadingAnchor.constraint(equalTo: containerTo.leadingAnchor, constant: 10),
+            textTo.trailingAnchor.constraint(equalTo: containerTo.trailingAnchor, constant: -10),
+            textTo.centerYAnchor.constraint(equalTo: containerTo.centerYAnchor)
+        ])
+        
+        let buttonTo = UIButton(type: .system)
+        buttonTo.setImage(UIImage(systemName: "paperplane.circle.fill")?.withConfiguration(UIImage.SymbolConfiguration(pointSize: 35)), for: .normal)
+        buttonTo.tintColor = .systemBlue
+        buttonTo.addTarget(self, action: #selector(sendAction), for: .touchUpInside)
+        containerView.addSubview(buttonTo)
+        buttonTo.translatesAutoresizingMaskIntoConstraints = false
+        NSLayoutConstraint.activate([
+            buttonTo.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -15),
+            buttonTo.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -10),
+            buttonTo.widthAnchor.constraint(equalToConstant: 35),
+            buttonTo.heightAnchor.constraint(equalToConstant: 35),
+        ])
+        
+        textView = UITextView()
+        viewVc.addSubview(textView)
+        textView.textColor = .white
+        textView.font = .systemFont(ofSize: 17)
+        textView.textContainerInset = UIEdgeInsets(top: 10.5, left: 15, bottom: 10.5, right: 15)
+        textView.translatesAutoresizingMaskIntoConstraints = false
+        textView.layer.cornerRadius = 22.5
+        textView.clipsToBounds = true
+        textView.layer.borderColor = UIColor.gray.cgColor
+        textView.layer.borderWidth = 1
+        textView.delegate = self
+        NSLayoutConstraint.activate([
+            textView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -15),
+            textView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 15),
+            textView.bottomAnchor.constraint(equalTo: containerView.topAnchor, constant: -20),
+        ])
+        heightTextView = textView.heightAnchor.constraint(equalToConstant: 45)
+        heightTextView?.isActive = true
+    }
+    
+    func handleSharedContent(_ contact: Contact) {
+        guard let extensionItem = extensionContext?.inputItems.first as? NSExtensionItem else { return }
+
+        for attachment in extensionItem.attachments ?? [] {
+            if attachment.hasItemConformingToTypeIdentifier(UTType.text.identifier) {
+                // Handle Text
+                attachment.loadItem(forTypeIdentifier: UTType.text.identifier, options: nil) { (textItem, error) in
+                    if let sharedText = textItem as? String {
+                        DispatchQueue.main.async { [self] in
+                            typeShareNum = TypeShare.text
+                            if let viewVc = vcHandleText.view {
+                                viewVc.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .black : .white
+                                self.navigationItem.backButtonTitle = ""
+                                let sendButton = UIBarButtonItem(title: "Send", style: .plain, target: self, action: #selector(sendAction))
+                                sendButton.tintColor = .label
+                                vcHandleText.navigationItem.rightBarButtonItem = sendButton
+                                
+                                let containerTo = UIView()
+                                viewVc.addSubview(containerTo)
+                                containerTo.translatesAutoresizingMaskIntoConstraints = false
+                                NSLayoutConstraint.activate([
+                                    containerTo.topAnchor.constraint(equalTo: viewVc.safeAreaLayoutGuide.topAnchor, constant: 8.0),
+                                    containerTo.leadingAnchor.constraint(equalTo: viewVc.leadingAnchor),
+                                    containerTo.trailingAnchor.constraint(equalTo: viewVc.trailingAnchor),
+                                    containerTo.heightAnchor.constraint(equalToConstant: 44)
+                                ])
+                                containerTo.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .systemBackground : .gray
+                                
+                                let textTo = UILabel()
+                                containerTo.addSubview(textTo)
+                                textTo.translatesAutoresizingMaskIntoConstraints = false
+                                NSLayoutConstraint.activate([
+                                    textTo.leadingAnchor.constraint(equalTo: viewVc.leadingAnchor, constant: 10.0),
+                                    textTo.centerYAnchor.constraint(equalTo: containerTo.centerYAnchor)
+                                ])
+                                textTo.text = "To: \(contact.name)"
+                                textTo.font = .systemFont(ofSize: 13)
+                                textTo.textColor = .label
+                                
+                                textView = UITextView()
+                                textView.translatesAutoresizingMaskIntoConstraints = false
+                                textView.isScrollEnabled = true
+                                textView.text = sharedText
+                                textView.textColor = .label
+                                textView.font = .systemFont(ofSize: 16)
+                                textView.backgroundColor = .clear
+                                textView.delegate = self
+                                viewVc.addSubview(textView)
+                                NSLayoutConstraint.activate([
+                                    textView.leadingAnchor.constraint(equalTo: viewVc.leadingAnchor),
+                                    textView.trailingAnchor.constraint(equalTo: viewVc.trailingAnchor),
+                                    textView.topAnchor.constraint(equalTo: containerTo.bottomAnchor, constant: 5)
+                                ])
+                                textViewBottomConstraint = textView.bottomAnchor.constraint(equalTo: viewVc.bottomAnchor, constant: -20)
+                                textViewBottomConstraint?.isActive = true
+                                
+                                self.navigationController?.pushViewController(vcHandleText, animated: true)
+                            }
+                        }
+                    }
+                }
+                return
+            } else if attachment.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
+                // Handle Image
+                attachment.loadItem(forTypeIdentifier: UTType.image.identifier, options: nil) { (imageItem, error) in
+                    if let imageURL = imageItem as? URL {
+                        DispatchQueue.main.async { [self] in
+                            typeShareNum = TypeShare.image
+                            selectedImage = imageURL
+                            if let viewVc = vcHandleImage.view {
+                                let imageView = UIImageView()
+                                imageView.image = UIImage(contentsOfFile: imageURL.path)
+                                imageView.contentMode = .scaleAspectFit
+                                imageView.clipsToBounds = true
+                                viewVc.addSubview(imageView)
+                                imageView.frame = CGRect(x: 0, y: 70, width: viewVc.bounds.size.width, height: self.view.bounds.height - 150)
+                                
+                                buildAppearance(contact, viewVc)
+                                
+                                vcHandleImage.modalPresentationStyle = .fullScreen
+                                self.navigationController?.present(vcHandleImage, animated: true)
+                            }
+                        }
+                    }
+                }
+                return
+            } else if attachment.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
+                // Handle Video
+                attachment.loadItem(forTypeIdentifier: UTType.movie.identifier, options: nil) { (videoItem, error) in
+                    if let videoURL = videoItem as? URL {
+                        DispatchQueue.main.async { [self] in
+                            typeShareNum = TypeShare.video
+                            selectedVideo = videoURL
+                            if let viewVc = vcHandleVideo.view {
+                                previewView = VideoPreviewView(frame: CGRect(x: 0, y: 70, width: viewVc.bounds.size.width, height: self.view.bounds.height - 190))
+                                viewVc.addSubview(previewView!)
+                                previewView!.configure(with: videoURL)
+                                
+                                buildAppearance(contact, viewVc)
+                                
+                                vcHandleVideo.modalPresentationStyle = .fullScreen
+                                self.navigationController?.present(vcHandleVideo, animated: true)
+                            }
+                        }
+                    }
+                }
+                return
+            } else if attachment.hasItemConformingToTypeIdentifier(UTType.data.identifier) {
+                // Handle Other Files
+                attachment.loadItem(forTypeIdentifier: UTType.data.identifier, options: nil) { (fileItem, error) in
+                    if let fileURL = fileItem as? URL {
+                        DispatchQueue.main.async { [self] in
+                            typeShareNum = TypeShare.file
+                            selectedFile = fileURL
+                            if let viewVc = vcHandleFile.view {
+                                vcHandleFile.addChild(previewController)
+                                previewController.dataSource = self
+                                previewController.view.frame = CGRect(x: 0, y: 70, width: viewVc.bounds.size.width, height: viewVc.bounds.size.height - 190)
+                                previewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+                                viewVc.addSubview(previewController.view)
+                                previewController.didMove(toParent: vcHandleFile)
+                                
+                                buildAppearance(contact, viewVc)
+                                
+                                vcHandleFile.modalPresentationStyle = .fullScreen
+                                self.navigationController?.present(vcHandleFile, animated: true)
+                            }
+                        }
+                    } else {
+                        attachment.loadItem(forTypeIdentifier: "public.file-url", options: nil) { (urlData, error) in
+                            if let url = urlData as? URL {
+                                DispatchQueue.main.async { [self] in
+                                    typeShareNum = TypeShare.file
+                                    selectedFile = url
+                                    if let viewVc = vcHandleFile.view {
+                                        vcHandleFile.addChild(previewController)
+                                        previewController.dataSource = self
+                                        previewController.view.frame = CGRect(x: 0, y: 70, width: viewVc.bounds.size.width, height: viewVc.bounds.size.height - 190)
+                                        previewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+                                        viewVc.addSubview(previewController.view)
+                                        previewController.didMove(toParent: vcHandleFile)
+                                        
+                                        buildAppearance(contact, viewVc)
+                                        
+                                        vcHandleFile.modalPresentationStyle = .fullScreen
+                                        self.navigationController?.present(vcHandleFile, animated: true)
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                return
+            }
+        }
+    }
+}
+
+struct Contact {
+    let id: String
+    let name: String
+    let profileImage: UIImage?
+    let imageId: String
+    let typeContact: String
+}
+
+class TypeShare {
+    static let text = 1
+    static let image = 2
+    static let video = 3
+    static let file = 4
+}
+
+class VideoPreviewView: UIView {
+    
+    private var player: AVPlayer?
+    private var playerLayer: AVPlayerLayer?
+    private var isPlaying = false
+    
+    private let playPauseButton: UIButton = {
+        let button = UIButton(type: .system)
+        button.setImage(UIImage(systemName: "play.fill"), for: .normal)
+        button.tintColor = .white
+        button.backgroundColor = UIColor.black.withAlphaComponent(0.5)
+        button.layer.cornerRadius = 25
+        button.clipsToBounds = true
+        return button
+    }()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        setupUI()
+    }
+    
+    required init?(coder: NSCoder) {
+        super.init(coder: coder)
+        setupUI()
+    }
+    
+    private func setupUI() {
+        playPauseButton.frame = CGRect(x: (bounds.width - 50) / 2, y: (bounds.height - 50) / 2, width: 50, height: 50)
+        playPauseButton.addTarget(self, action: #selector(playPauseTapped), for: .touchUpInside)
+        addSubview(playPauseButton)
+    }
+    
+    func configure(with url: URL) {
+        // Setup AVPlayer
+        player = AVPlayer(url: url)
+        playerLayer = AVPlayerLayer(player: player)
+        playerLayer?.frame = bounds
+        playerLayer?.videoGravity = .resizeAspect
+        if let playerLayer = playerLayer {
+            layer.insertSublayer(playerLayer, below: playPauseButton.layer)
+        }
+        
+        // Observe when video ends
+        NotificationCenter.default.addObserver(self, selector: #selector(videoDidEnd), name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem)
+    }
+    
+    @objc private func playPauseTapped() {
+        guard let player = player else { return }
+        
+        if isPlaying {
+            player.pause()
+            playPauseButton.setImage(UIImage(systemName: "play.fill"), for: .normal)
+        } else {
+            player.play()
+            playPauseButton.setImage(UIImage(systemName: "pause.fill"), for: .normal)
+        }
+        
+        isPlaying.toggle()
+    }
+    
+    @objc private func videoDidEnd() {
+        guard let player = player else { return }
+        
+        // Replay video from start
+        player.seek(to: .zero)
+        player.play()
+        playPauseButton.setImage(UIImage(systemName: "pause.fill"), for: .normal)
+        isPlaying = true
+    }
+    
+    func stopVideo() {
+        player?.pause()
+        player?.seek(to: .zero)
+        playPauseButton.setImage(UIImage(systemName: "play.fill"), for: .normal)
+        isPlaying = false
+    }
+    
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+    }
+}
+
+class ContactCell: UITableViewCell {
+    
+    let profileImageView = UIImageView()
+    let nameLabel = UILabel()
+
+    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+        setupUI()
+    }
+
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    func setupUI() {
+        profileImageView.translatesAutoresizingMaskIntoConstraints = false
+        profileImageView.layer.cornerRadius = 25
+        profileImageView.clipsToBounds = true
+        profileImageView.contentMode = .center
+
+        nameLabel.translatesAutoresizingMaskIntoConstraints = false
+        nameLabel.font = UIFont.systemFont(ofSize: 16, weight: .medium)
+
+        contentView.addSubview(profileImageView)
+        contentView.addSubview(nameLabel)
+
+        NSLayoutConstraint.activate([
+            profileImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15),
+            profileImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
+            profileImageView.widthAnchor.constraint(equalToConstant: 50),
+            profileImageView.heightAnchor.constraint(equalToConstant: 50),
+
+            nameLabel.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 15),
+            nameLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
+        ])
+    }
+
+    func configure(with contact: Contact) {
+        nameLabel.text = contact.name
+        profileImageView.image = contact.profileImage ?? UIImage(systemName: "person.circle")
+        if !contact.imageId.isEmpty {
+            profileImageView.contentMode = .scaleAspectFill
+        }
+    }
+}

BIN
NexilisLite/.DS_Store


+ 123 - 0
NexilisLite/NexilisLite.xcodeproj/project.pbxproj

@@ -10,6 +10,7 @@
 		1241AEBC2D017E8C0088175A /* MasterKeyUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1241AEBB2D017E8C0088175A /* MasterKeyUtil.swift */; };
 		1241AEBD2D017E8C0088175A /* FileEncryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1241AEBA2D017E8C0088175A /* FileEncryption.swift */; };
 		12C36CEB2D0299630095BEC1 /* SecureFolderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C36CEA2D02995F0095BEC1 /* SecureFolderView.swift */; };
+		38479781BBA877EE9BF102E9 /* Pods_NexilisLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E83536BC68F49BB9625ED65B /* Pods_NexilisLite.framework */; };
 		CD1E6E6D2A0B7C3600BF871F /* NexilisLite.docc in Sources */ = {isa = PBXBuildFile; fileRef = CD1E6E6C2A0B7C3600BF871F /* NexilisLite.docc */; };
 		CD1E6E732A0B7C3600BF871F /* NexilisLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD1E6E682A0B7C3600BF871F /* NexilisLite.framework */; };
 		CD1E6E782A0B7C3600BF871F /* NexilisLiteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD1E6E772A0B7C3600BF871F /* NexilisLiteTests.swift */; };
@@ -235,6 +236,7 @@
 		CDDF46752A2DD81300049A19 /* CreateSeminarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDF46732A2DD81300049A19 /* CreateSeminarViewController.swift */; };
 		CDDF46762A2DD81300049A19 /* SeminarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDF46742A2DD81300049A19 /* SeminarViewController.swift */; };
 		CDDF467A2A2EF0A700049A19 /* ScreenSharingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDDF46792A2EF0A700049A19 /* ScreenSharingViewController.swift */; };
+		E8C1D146142A83CB350DD0DE /* Pods_NexilisLite_NexilisLiteTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BAB8A0AC826DB74FE2D6EF6D /* Pods_NexilisLite_NexilisLiteTests.framework */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -271,6 +273,10 @@
 		1241AEBA2D017E8C0088175A /* FileEncryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileEncryption.swift; sourceTree = "<group>"; };
 		1241AEBB2D017E8C0088175A /* MasterKeyUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterKeyUtil.swift; sourceTree = "<group>"; };
 		12C36CEA2D02995F0095BEC1 /* SecureFolderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureFolderView.swift; sourceTree = "<group>"; };
+		8926F5C23ECB2DC20ECCEC00 /* Pods-NexilisLite.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NexilisLite.debug.xcconfig"; path = "Target Support Files/Pods-NexilisLite/Pods-NexilisLite.debug.xcconfig"; sourceTree = "<group>"; };
+		ACB20591E989D377944ACA38 /* Pods-NexilisLite-NexilisLiteTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NexilisLite-NexilisLiteTests.debug.xcconfig"; path = "Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests.debug.xcconfig"; sourceTree = "<group>"; };
+		BAB8A0AC826DB74FE2D6EF6D /* Pods_NexilisLite_NexilisLiteTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NexilisLite_NexilisLiteTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		C62A0C31706C73970E7CD995 /* Pods-NexilisLite-NexilisLiteTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NexilisLite-NexilisLiteTests.release.xcconfig"; path = "Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests.release.xcconfig"; sourceTree = "<group>"; };
 		CD1E6E682A0B7C3600BF871F /* NexilisLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NexilisLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD1E6E6B2A0B7C3600BF871F /* NexilisLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NexilisLite.h; sourceTree = "<group>"; };
 		CD1E6E6C2A0B7C3600BF871F /* NexilisLite.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = NexilisLite.docc; sourceTree = "<group>"; };
@@ -507,6 +513,8 @@
 		CDDF46792A2EF0A700049A19 /* ScreenSharingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenSharingViewController.swift; sourceTree = "<group>"; };
 		CDE3BED62C86E3A000B0BF36 /* pb_call_in.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = pb_call_in.mp3; sourceTree = "<group>"; };
 		CDE3BED72C86E3A000B0BF36 /* pb_call_out.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = pb_call_out.mp3; sourceTree = "<group>"; };
+		DF552B05B00BB7A9BC95487E /* Pods-NexilisLite.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NexilisLite.release.xcconfig"; path = "Target Support Files/Pods-NexilisLite/Pods-NexilisLite.release.xcconfig"; sourceTree = "<group>"; };
+		E83536BC68F49BB9625ED65B /* Pods_NexilisLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NexilisLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -514,6 +522,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				38479781BBA877EE9BF102E9 /* Pods_NexilisLite.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -522,6 +531,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				CD1E6E732A0B7C3600BF871F /* NexilisLite.framework in Frameworks */,
+				E8C1D146142A83CB350DD0DE /* Pods_NexilisLite_NexilisLiteTests.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -545,6 +555,8 @@
 				CD1E79142A0CA43600BF871F /* SDWebImage.framework */,
 				CD1E79152A0CA43600BF871F /* SnapKit.framework */,
 				CD1E79162A0CA43600BF871F /* Toast_Swift.framework */,
+				E83536BC68F49BB9625ED65B /* Pods_NexilisLite.framework */,
+				BAB8A0AC826DB74FE2D6EF6D /* Pods_NexilisLite_NexilisLiteTests.framework */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -553,6 +565,10 @@
 			isa = PBXGroup;
 			children = (
 				CD46A0BE2A0CE4FD009E4C87 /* NexilisLite.podspec */,
+				8926F5C23ECB2DC20ECCEC00 /* Pods-NexilisLite.debug.xcconfig */,
+				DF552B05B00BB7A9BC95487E /* Pods-NexilisLite.release.xcconfig */,
+				ACB20591E989D377944ACA38 /* Pods-NexilisLite-NexilisLiteTests.debug.xcconfig */,
+				C62A0C31706C73970E7CD995 /* Pods-NexilisLite-NexilisLiteTests.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -957,11 +973,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = CD1E6E7C2A0B7C3600BF871F /* Build configuration list for PBXNativeTarget "NexilisLite" */;
 			buildPhases = (
+				46DA4C08B89C2BE8DA10BB14 /* [CP] Check Pods Manifest.lock */,
 				CD1E6E632A0B7C3600BF871F /* Headers */,
 				CD1E6E642A0B7C3600BF871F /* Sources */,
 				CD1E6E652A0B7C3600BF871F /* Frameworks */,
 				CD1E6E662A0B7C3600BF871F /* Resources */,
 				CD469E162A0CA6C9009E4C87 /* Embed Frameworks */,
+				96E6D2068F10B9382682F4B6 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -977,9 +995,12 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = CD1E6E7F2A0B7C3600BF871F /* Build configuration list for PBXNativeTarget "NexilisLiteTests" */;
 			buildPhases = (
+				179505924863B40267A8DD7F /* [CP] Check Pods Manifest.lock */,
 				CD1E6E6E2A0B7C3600BF871F /* Sources */,
 				CD1E6E6F2A0B7C3600BF871F /* Frameworks */,
 				CD1E6E702A0B7C3600BF871F /* Resources */,
+				B210A69E33C74CC4354B3678 /* [CP] Embed Pods Frameworks */,
+				4F31F00597D2D3F2ABCA0E89 /* [CP] Copy Pods Resources */,
 			);
 			buildRules = (
 			);
@@ -1193,6 +1214,104 @@
 		};
 /* End PBXResourcesBuildPhase section */
 
+/* Begin PBXShellScriptBuildPhase section */
+		179505924863B40267A8DD7F /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-NexilisLite-NexilisLiteTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			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;
+		};
+		46DA4C08B89C2BE8DA10BB14 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-NexilisLite-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			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;
+		};
+		4F31F00597D2D3F2ABCA0E89 /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-resources-${CONFIGURATION}-input-files.xcfilelist",
+			);
+			name = "[CP] Copy Pods Resources";
+			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-resources-${CONFIGURATION}-output-files.xcfilelist",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		96E6D2068F10B9382682F4B6 /* [CP] Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite/Pods-NexilisLite-resources-${CONFIGURATION}-input-files.xcfilelist",
+			);
+			name = "[CP] Copy Pods Resources";
+			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite/Pods-NexilisLite-resources-${CONFIGURATION}-output-files.xcfilelist",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NexilisLite/Pods-NexilisLite-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		B210A69E33C74CC4354B3678 /* [CP] Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+			);
+			name = "[CP] Embed Pods Frameworks";
+			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
 /* Begin PBXSourcesBuildPhase section */
 		CD1E6E642A0B7C3600BF871F /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
@@ -1474,6 +1593,7 @@
 		};
 		CD1E6E7D2A0B7C3600BF871F /* Debug */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = 8926F5C23ECB2DC20ECCEC00 /* Pods-NexilisLite.debug.xcconfig */;
 			buildSettings = {
 				BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
 				CODE_SIGN_STYLE = Automatic;
@@ -1513,6 +1633,7 @@
 		};
 		CD1E6E7E2A0B7C3600BF871F /* Release */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = DF552B05B00BB7A9BC95487E /* Pods-NexilisLite.release.xcconfig */;
 			buildSettings = {
 				BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
 				CODE_SIGN_STYLE = Automatic;
@@ -1552,6 +1673,7 @@
 		};
 		CD1E6E802A0B7C3600BF871F /* Debug */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = ACB20591E989D377944ACA38 /* Pods-NexilisLite-NexilisLiteTests.debug.xcconfig */;
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
 				CODE_SIGN_STYLE = Automatic;
@@ -1569,6 +1691,7 @@
 		};
 		CD1E6E812A0B7C3600BF871F /* Release */ = {
 			isa = XCBuildConfiguration;
+			baseConfigurationReference = C62A0C31706C73970E7CD995 /* Pods-NexilisLite-NexilisLiteTests.release.xcconfig */;
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
 				CODE_SIGN_STYLE = Automatic;

+ 257 - 1
NexilisLite/NexilisLite/Source/APIS.swift

@@ -411,6 +411,22 @@ public class APIS: NSObject {
         }
     }
     
+    public static func openSecureFolder() {
+        let isChangeProfile = Utils.getSetProfile()
+        if !isChangeProfile {
+            APIS.showChangeProfile()
+            return
+        }
+        let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "secureFolderView") as! SecureFolderViewController
+        let navigationController = CustomNavigationController(rootViewController: controller)
+        navigationController.defaultStyle()
+        if UIApplication.shared.visibleViewController?.navigationController != nil {
+            UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+        } else {
+            UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+        }
+    }
+    
     public static func openCreateGroup() {
         let isChangeProfile = Utils.getSetProfile()
         if !isChangeProfile {
@@ -984,7 +1000,7 @@ public class APIS: NSObject {
                     }
                     if let cursorGroup = Database.shared.getRecords(fmdb: fmdb, query: "SELECT f_name, image_id FROM GROUPZ WHERE group_id='\(idGroup)'"), cursorGroup.next() {
                         let nameGroup = cursorGroup.string(forColumnIndex: 0) ?? ""
-                        nameSubtitle = "\(nameGroup)(\(nameTopic))"
+                        nameSubtitle = "\(nameGroup) (\(nameTopic))"
                         cursorGroup.close()
                     }
                 } catch {
@@ -1219,10 +1235,250 @@ public class APIS: NSObject {
 //            try API.switchCBI(cbiI: Callback(), bLight: false)
 //        } catch {
 //        }
+//        setDataForShareExtension()
+        checkDataForShareExtension()
         UIApplication.shared.applicationIconBadgeNumber = 0
         UNUserNotificationCenter.current().removeAllDeliveredNotifications()
     }
     
+    private static func checkDataForShareExtension() {
+        DispatchQueue.global().async {
+            if let userDefaults = UserDefaults(suiteName: "group.nexilis.share") {
+                if let value = userDefaults.string(forKey: "sharedItem") {
+                    if !value.isEmpty {
+                        if let jsonData = value.data(using: .utf8) {
+                            do {
+                                if let json = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
+                                    let typeImage = 2
+                                    let typeVideo = 3
+                                    let typeFile = 4
+                                    let typeContact = json["typeContact"] as? String ?? "0"
+                                    var data = json["data"] as? String ?? ""
+                                    let idContact = json["idContact"] as? String ?? ""
+                                    let typeShare = json["typeShare"] as? Int ?? 1
+                                    var message = TMessage()
+                                    var groupId = ""
+                                    var chatId = ""
+                                    let scopeId = typeContact == "1" ? "4" : "3"
+                                    let thumb = json["thumb"] as? String ?? ""
+                                    let imageId = json["image"] as? String ?? ""
+                                    let videoId = json["video"] as? String ?? ""
+                                    let fileId = json["file"] as? String ?? ""
+                                    var renamedFileId = ""
+                                    var attachmentFlag = ""
+                                    if scopeId == "4" {
+                                        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                                            if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "SELECT group_id id FROM DISCUSSION_FORUM WHERE chat_id = '\(idContact)'"), cursor.next() {
+                                                groupId = cursor.string(forColumnIndex: 0) ?? ""
+                                                chatId = idContact
+                                                cursor.close()
+                                            } else {
+                                                groupId = idContact
+                                            }
+                                        })
+                                    }
+                                    if typeShare == typeImage {
+                                        if let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.nexilis.share") {
+                                            let sharedImageURL = appGroupURL.appendingPathComponent(imageId)
+                                            let sharedThumbURL = appGroupURL.appendingPathComponent(thumb)
+                                            let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
+                                            if FileManager.default.fileExists(atPath: sharedImageURL.path) {
+                                                let file = documentDir.appendingPathComponent(imageId)
+                                                if !FileManager().fileExists(atPath: file.path) {
+                                                    try? FileManager.default.copyItem(at: sharedImageURL, to: file)
+                                                }
+                                            }
+                                            if FileManager.default.fileExists(atPath: sharedThumbURL.path) {
+                                                let file = documentDir.appendingPathComponent(thumb)
+                                                if !FileManager().fileExists(atPath: file.path) {
+                                                    try? FileManager.default.copyItem(at: sharedThumbURL, to: file)
+                                                }
+                                            }
+                                        }
+                                        attachmentFlag = "1"
+                                    } else if typeShare == typeVideo {
+                                        if let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.nexilis.share") {
+                                            let sharedVideoURL = appGroupURL.appendingPathComponent(videoId)
+                                            let sharedThumbURL = appGroupURL.appendingPathComponent(thumb)
+                                            let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
+                                            if FileManager.default.fileExists(atPath: sharedVideoURL.path) {
+                                                let file = documentDir.appendingPathComponent(videoId)
+                                                if !FileManager().fileExists(atPath: file.path) {
+                                                    try? FileManager.default.copyItem(at: sharedVideoURL, to: file)
+                                                }
+                                            }
+                                            if FileManager.default.fileExists(atPath: sharedThumbURL.path) {
+                                                let file = documentDir.appendingPathComponent(thumb)
+                                                if !FileManager().fileExists(atPath: file.path) {
+                                                    try? FileManager.default.copyItem(at: sharedThumbURL, to: file)
+                                                }
+                                            }
+                                        }
+                                        attachmentFlag = "2"
+                                    } else if typeShare == typeFile {
+                                        if let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.nexilis.share") {
+                                            renamedFileId = "Nexilis_\(Date().currentTimeMillis())_" + fileId
+                                            let sharedFileURL = appGroupURL.appendingPathComponent(fileId)
+                                            let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
+                                            if FileManager.default.fileExists(atPath: sharedFileURL.path) {
+                                                let file = documentDir.appendingPathComponent(renamedFileId)
+                                                if !FileManager().fileExists(atPath: file.path) {
+                                                    try? FileManager.default.copyItem(at: sharedFileURL, to: file)
+                                                }
+                                            }
+                                            data = "\(fileId)|\(data)"
+                                        }
+                                        attachmentFlag = "6"
+                                    }
+                                    message = CoreMessage_TMessageBank.sendMessage(l_pin: groupId.isEmpty ? idContact : groupId, message_scope_id: scopeId, status: scopeId == "3" ? "1" : "2", message_text: data, credential: "0", attachment_flag: attachmentFlag, ex_blog_id: "", message_large_text: "", ex_format: "", image_id: imageId, audio_id: "", video_id: videoId, file_id: renamedFileId, thumb_id: thumb, reff_id: "", read_receipts: "4", chat_id: chatId, is_call_center: "0", call_center_id: "", opposite_pin: scopeId == "3" ? (User.getMyPin() ?? "") : idContact, gif_id: "", isForwarded: "0", isSecret: "0")
+                                    Nexilis.addQueueMessage(message: message)
+                                    DispatchQueue.global().asyncAfter(deadline: .now() + 0.5, execute: {
+                                        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
+                                    })
+                                    userDefaults.set("", forKey: "sharedItem")
+                                    userDefaults.synchronize()
+                                }
+                            } catch {
+                                print("Error parsing JSON: \(error)")
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    public static func setDataForShareExtension() {
+        DispatchQueue.global().async {
+            if let userDefaults = UserDefaults(suiteName: "group.nexilis.share") {
+                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                    var dataShared: [[String: Any]] = []
+                    if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "SELECT f_pin id, image_id image, first_name || ' ' || ifnull(last_name, '') name FROM BUDDY WHERE f_pin != '\(User.getMyPin() ?? "")' AND f_pin != '-997' AND official_account != '1'") {
+                        var dataTemp: [String: Any] = [:]
+                        while cursor.next() {
+                            for columnIndex in 0..<cursor.columnCount {
+                                if let columnName = cursor.columnName(for: columnIndex) {
+                                    if let value = cursor.object(forColumn: columnName) {
+                                        if columnName == "image" {
+                                           dataTemp[columnName] = value
+                                           if let imageString = dataTemp[columnName] as? String, !imageString.isEmpty {
+                                               do {
+                                                   let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
+                                                   let file = documentDir.appendingPathComponent(imageString)
+                                                   if FileManager().fileExists(atPath: file.path) {
+                                                       if let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.nexilis.share") {
+                                                           let sharedFileURL = appGroupURL.appendingPathComponent(imageString)
+                                                           if !FileManager.default.fileExists(atPath: sharedFileURL.path) {
+                                                               try? FileManager.default.copyItem(at: file, to: sharedFileURL)
+                                                           }
+                                                       }
+                                                   }
+                                               } catch {
+                                                   
+                                               }
+                                           }
+                                        } else {
+                                            dataTemp[columnName] = value
+                                        }
+                                    }
+                                }
+                                dataTemp["type"] = 0
+                            }
+                        }
+                        dataShared.append(dataTemp)
+                        cursor.close()
+                    }
+                    if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "SELECT group_id id, image_id image, f_name name FROM GROUPZ WHERE official != 1") {
+                        var dataTemp: [String: Any] = [:]
+                        var dataTempTopic: [String: Any] = [:]
+                        while cursor.next() {
+                            for columnIndex in 0..<cursor.columnCount {
+                                if let columnName = cursor.columnName(for: columnIndex) {
+                                    if let value = cursor.object(forColumn: columnName) {
+                                        if columnName == "name" {
+                                            dataTemp[columnName] = "\(value) (Lounge)"
+                                        } else if columnName == "image" {
+                                            dataTemp[columnName] = value
+                                            if let imageString = dataTemp[columnName] as? String, !imageString.isEmpty {
+                                                do {
+                                                    let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
+                                                    let file = documentDir.appendingPathComponent(imageString)
+                                                    if FileManager().fileExists(atPath: file.path) {
+                                                        if let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.nexilis.share") {
+                                                            let sharedFileURL = appGroupURL.appendingPathComponent(imageString)
+                                                            if !FileManager.default.fileExists(atPath: sharedFileURL.path) {
+                                                                try? FileManager.default.copyItem(at: file, to: sharedFileURL)
+                                                            }
+                                                        }
+                                                    }
+                                                } catch {
+                                                    
+                                                }
+                                            }
+                                        } else {
+                                            dataTemp[columnName] = value
+                                        }
+                                    }
+                                }
+                                dataTemp["type"] = 1
+                            }
+                            dataShared.append(dataTemp)
+                            let group_id = cursor.string(forColumnIndex: 0) ?? ""
+                            let image_group = cursor.string(forColumnIndex: 1) ?? ""
+                            let name_group = cursor.string(forColumnIndex: 2) ?? ""
+                            if let cursorTopic = Database.shared.getRecords(fmdb: fmdb, query: "SELECT chat_id id, thumb image, title name FROM DISCUSSION_FORUM WHERE group_id = '\(group_id)'") {
+                                while cursorTopic.next() {
+                                    for columnIndex in 0..<cursorTopic.columnCount {
+                                        if let columnName = cursorTopic.columnName(for: columnIndex) {
+                                            if let value = cursorTopic.object(forColumn: columnName) {
+                                                if columnName == "name" {
+                                                    dataTempTopic[columnName] = "\(name_group) (\(value))"
+                                                } else if columnName == "image" {
+                                                    dataTempTopic[columnName] = "\(value)".isEmpty ? image_group : "\(value)"
+                                                    if let imageString = dataTempTopic[columnName] as? String, !imageString.isEmpty {
+                                                        do {
+                                                            let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
+                                                            let file = documentDir.appendingPathComponent(imageString)
+                                                            if FileManager().fileExists(atPath: file.path) {
+                                                                if let appGroupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.nexilis.share") {
+                                                                    let sharedFileURL = appGroupURL.appendingPathComponent(imageString)
+                                                                    if !FileManager.default.fileExists(atPath: sharedFileURL.path) {
+                                                                        try? FileManager.default.copyItem(at: file, to: sharedFileURL)
+                                                                    }
+                                                                }
+                                                            }
+                                                        } catch {
+                                                            
+                                                        }
+                                                    }
+                                                } else {
+                                                    dataTempTopic[columnName] = value
+                                                }
+                                            }
+                                        }
+                                        dataTempTopic["type"] = 1
+                                    }
+                                    dataShared.append(dataTempTopic)
+                                    cursorTopic.close()
+                                }
+                            }
+                        }
+                        cursor.close()
+                    }
+                    do {
+                        let jsonData = try JSONSerialization.data(withJSONObject: dataShared, options: .prettyPrinted)
+                        if let jsonString = String(data: jsonData, encoding: .utf8) {
+                            userDefaults.set(jsonString, forKey: "shareContacts")
+                            userDefaults.synchronize()
+                        }
+                    } catch {
+                        print("Error converting to JSON: \(error)")
+                    }
+                })
+            }
+        }
+    }
+    
     public static func setCheckEmulator(isActive: Bool) {
 //        Utils.bCheckEmulator = isActive
     }

+ 8 - 8
NexilisLite/NexilisLite/Source/Callback.swift

@@ -153,18 +153,18 @@ class NetworkMonitor {
         guard !isMonitoring else { return }
         
         monitor.pathUpdateHandler = { path in
-            self.isConnected = path.status == .satisfied
             self.canAccessGoogle(completion: { [self] connected in
+                self.isConnected = connected
                 InquiryThread.default.set(wait: !connected)
                 OutgoingThread.default.set(wait: !connected)
                 if !connected {
                     fromDisconnect = true
-                    DispatchQueue.main.async {
-                        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()
-                    }
+//                    DispatchQueue.main.async {
+//                        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()
+//                    }
                 }
                 if connected && fromDisconnect {
                     fromDisconnect = false
@@ -190,7 +190,7 @@ class NetworkMonitor {
     }
     
     func canAccessGoogle(completion: @escaping (Bool) -> Void) {
-        guard isConnected, let url = URL(string: "https://www.google.com") else {
+        guard let url = URL(string: "https://www.google.com") else {
             completion(false)
             return
         }

+ 1 - 10
NexilisLite/NexilisLite/Source/Extension.swift

@@ -764,7 +764,7 @@ extension String {
         let italicSign: Character = "_"
         let underlineSign: Character = "^"
         let strikethroughSign: Character = "~"
-        let italicGreySign: Character = "%"
+        let italicGreySign: Character = "$"
         var locationBold: [NSRange] = []
         
         //Bold
@@ -1034,17 +1034,8 @@ extension String {
         return false
     }
     
-    func checkCharBefore(char: String) -> Bool {
-        return char == " " || char == "\n"
-    }
-    
-    func checkCharRich(char: String) -> Bool {
-        return char == "*" || char == "_" || char == "^" || char == "~"
-    }
-    
     func checkStartWithLink() -> Bool {
         return self.starts(with: "https://") || self.starts(with: "http://") || self.starts(with: "www.")
-        //|| self.starts(with: "*https://") || self.starts(with: "*http://") || self.starts(with: "*www.") || self.starts(with: "_https://") || self.starts(with: "_http://") || self.starts(with: "_www.") || self.starts(with: "^https://") || self.starts(with: "^http://") || self.starts(with: "^www.") || self.starts(with: "~https://") || self.starts(with: "~http://") || self.starts(with: "~www.")
     }
     
     func getRangeOfWordWithSign(sentence: String, sign: Character) -> [Range<Int>] {

+ 8 - 10
NexilisLite/NexilisLite/Source/FloatingButton/FloatingButton.swift

@@ -336,13 +336,6 @@ public class FloatingButton: UIView, UIGestureRecognizerDelegate {
         } else if Int(Date().timeIntervalSince(datePull!)) < 60 {
             return
         }
-        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()
-            return
-        }
         if groupView.subviews.count == 0 {
             getDefaultButton()
         }
@@ -432,9 +425,10 @@ public class FloatingButton: UIView, UIGestureRecognizerDelegate {
                                     newButton.widthAnchor.constraint(equalToConstant: defaultWidthHeightMenuFB).isActive = true
                                     newButton.heightAnchor.constraint(equalToConstant: defaultWidthHeightMenuFB).isActive = true
                                 } else if mode == MODE_VERTICAL_SIDE_TAB {
-                                    newButton.widthAnchor.constraint(equalToConstant: 30).isActive = true
+                                    newButton.frame = CGRect(x: 0, y: 0, width: 25, height: 25)
+                                    newButton.widthAnchor.constraint(equalToConstant: 25).isActive = true
                                     newButton.heightAnchor.constraint(equalToConstant: 25).isActive = true
-                                    newButton.contentMode = .scaleAspectFit
+                                    newButton.contentMode = .scaleAspectFill
                                     newButton.clipsToBounds = true
                                 } else {
                                     newButton.heightAnchor.constraint(equalToConstant: defaultWidthHeightMenuFB).isActive = true
@@ -526,7 +520,11 @@ public class FloatingButton: UIView, UIGestureRecognizerDelegate {
                 newButton.widthAnchor.constraint(equalToConstant: defaultWidthHeightMenuFB).isActive = true
                 newButton.heightAnchor.constraint(equalToConstant: defaultWidthHeightMenuFB).isActive = true
             } else if mode == MODE_VERTICAL_SIDE_TAB {
-                newButton.imageView?.contentMode = .scaleAspectFit
+                newButton.frame = CGRect(x: 0, y: 0, width: 25, height: 25)
+                newButton.widthAnchor.constraint(equalToConstant: 25).isActive = true
+                newButton.heightAnchor.constraint(equalToConstant: 25).isActive = true
+                newButton.contentMode = .scaleAspectFill
+                newButton.clipsToBounds = true
             } else {
                 newButton.heightAnchor.constraint(equalToConstant: defaultWidthHeightMenuFB).isActive = true
             }

+ 2 - 2
NexilisLite/NexilisLite/Source/IncomingThread.swift

@@ -379,9 +379,9 @@ class IncomingThread {
         DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
             let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
             let channelCC: String = SecureUserDefaults.shared.value(forKey: "channelCC") ?? ""
-            let complaintId = onGoingCC.components(separatedBy: ",")[2]
+            let complaintId = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[2]
             let fPinCC = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
-            if fPinCC == User.getMyPin() {
+            if fPinCC == User.getMyPin() || fPinCC.isEmpty || complaintId.isEmpty {
                 return
             }
             if channelCC == "1" {

+ 1 - 1
NexilisLite/NexilisLite/Source/Model/Chat.swift

@@ -182,7 +182,7 @@ public class Chat: Model {
                                         lock: !lastQuery.isEmpty ? cursorData.string(forColumnIndex: 12) ?? "" : cursorData.string(forColumnIndex: 16) ?? "",
                                         thumb: !lastQuery.isEmpty ? cursorData.string(forColumnIndex: 3) ?? "" : "",
                                         audio: !lastQuery.isEmpty ? cursorData.string(forColumnIndex: 13) ?? "" : cursorData.string(forColumnIndex: 17) ?? "",
-                                        gif: !lastQuery.isEmpty ? cursorData.string(forColumnIndex: 14) ?? "" : cursorData.string(forColumnIndex: 18) ?? "")
+                                        gif: !lastQuery.isEmpty ? cursorData.string(forColumnIndex: 17) ?? "" : cursorData.string(forColumnIndex: 18) ?? "")
                         chats.append(chat)
                     }
                     cursorData.close()

+ 68 - 13
NexilisLite/NexilisLite/Source/Nexilis.swift

@@ -115,6 +115,7 @@ public class Nexilis: NSObject {
     public static let IDX_CREATE_GROUP = 21
     public static let IDX_ADDFRIEND = 22
     public static let IDX_SIGNUP_OR_IN_PAGE = 23
+    public static let IDX_SECURE_FOLDER = 29
     public static let IDX_POST = 99
     public static let IDX_SELF_ACT = 100
     public static let IDX_SOCIAL_COMMERCE = 101
@@ -165,27 +166,26 @@ public class Nexilis: NSObject {
         
         DispatchQueue.global().async {
             do {
+                if Utils.getFinishInitPrefsr() {
+                    Utils.setFinishInitPrefs(value: false)
+                }
                 let address = Nexilis.getAddressNew(apiKey:apiKey)
                 if address.isEmpty {
                     return
                 }
-                Nexilis.dispatch = DispatchGroup()
-                Nexilis.dispatch?.enter()
+                var id = Utils.getConnectionID()
                 Nexilis.ADDRESS = address.components(separatedBy: ":")[0]
                 Nexilis.PORT = Int(address.components(separatedBy: ":")[1]) ?? 0
-                var id = Utils.getConnectionID()
                 if id.isEmpty {
                     let sDID = UIDevice.current.identifierForVendor?.uuidString ?? "UNK-DEVICE"
                     id = String(sDID[sDID.index(sDID.endIndex, offsetBy: -5)...])
                     Utils.setConnectionID(value: id)
                 }
                 try API.initConnection(bSwitchIP: false, sAPIK: apiKey, aAppMain: nil, cbiI: Callback(), sTCPAddr: Nexilis.ADDRESS, nTCPPort: Nexilis.PORT, sUserID: id, sStartWH: "09:00")
-                
-                // wait until connection true
-                Nexilis.dispatch?.wait()
-                Nexilis.dispatch = nil
-                
-                if(User.getMyPin() == nil){
+                while (API.nGetCLXConnState() == 0) {
+                    Thread.sleep(forTimeInterval: 0.5)
+                }
+                if(User.getMyPin() == nil) {
                     if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSignUpApi(api: apiKey, p_pin: id), timeout: 30 * 1000){
                         id = response.getBody(key: CoreMessage_TMessageKey.F_PIN, default_value: "")
                         let enable_signup = (response.getBody(key: CoreMessage_TMessageKey.IS_ENABLED_ANONYMOUS, default_value: "0")) == "1"
@@ -271,10 +271,10 @@ public class Nexilis: NSObject {
                             }
                         }
                     }
-                    Nexilis.destroyAll()
                     if (Utils.getSetProfile() && !Utils.getFinishInitPrefsr()) || (!Utils.getForceAnonymous() && !Utils.getFinishInitPrefsr()) {
                         Utils.setFinishInitPrefs(value: true)
                     }
+                    Nexilis.destroyAll()
                     if !Utils.getLoginMultipleFPin().isEmpty {
                         let dialog = DialogUnableAccess()
                         dialog.modalTransitionStyle = .crossDissolve
@@ -797,6 +797,7 @@ public class Nexilis: NSObject {
             
             let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields, for: url)
             //print("MASUK SINI1 \(cookies)")
+            Utils.setCookiesMobileForStorage(value: convertCookiesToJSONString(cookies: cookies) ?? "")
             HTTPCookieStorage.shared.setCookies(cookies, for: url, mainDocumentURL: nil)
             if let cookieHeader = HTTPCookie.requestHeaderFields(with: cookies)["Cookie"] {
              Utils.setCookiesMobile(value: cookieHeader.replacingOccurrences(of: "; ", with: ";"))
@@ -835,10 +836,62 @@ public class Nexilis: NSObject {
         task.resume()
         _ = semaphore.wait(timeout: .distantFuture)
         result = Utils.getIpOpr()
-        //print("[App] getAddress:", result)
+        if HTTPCookieStorage.shared.cookies(for: URL(string: Utils.getDomainOpr())!)!.count == 0 && !Utils.getCookiesMobileForStorage().isEmpty {
+            HTTPCookieStorage.shared.setCookies(convertJSONStringToCookies(jsonString: Utils.getCookiesMobileForStorage()), for: url, mainDocumentURL: nil)
+        }
+        print("[App] getAddress:", result)
         return result
     }
     
+    private static func convertCookiesToJSONString(cookies: [HTTPCookie]) -> String? {
+        let cookiesArray = cookies.map { cookie -> [String: Any] in
+            return [
+                "name": cookie.name,
+                "value": cookie.value,
+                "domain": cookie.domain,
+                "path": cookie.path,
+                "expiresDate": cookie.expiresDate?.timeIntervalSince1970 ?? NSNull(),
+                "isSecure": cookie.isSecure,
+                "isHTTPOnly": cookie.isHTTPOnly
+            ]
+        }
+        
+        if let jsonData = try? JSONSerialization.data(withJSONObject: cookiesArray, options: .prettyPrinted) {
+            return String(data: jsonData, encoding: .utf8)
+        }
+        
+        return nil
+    }
+    
+    private static func convertJSONStringToCookies(jsonString: String) -> [HTTPCookie] {
+        guard let jsonData = jsonString.data(using: .utf8),
+              let jsonArray = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [[String: Any]] else {
+            return []
+        }
+        
+        var cookies: [HTTPCookie] = []
+        
+        for cookieDict in jsonArray {
+            var properties: [HTTPCookiePropertyKey: Any] = [
+                .name: cookieDict["name"] as? String ?? "",
+                .value: cookieDict["value"] as? String ?? "",
+                .domain: cookieDict["domain"] as? String ?? "",
+                .path: cookieDict["path"] as? String ?? "/",
+                .secure: cookieDict["isSecure"] as? Bool ?? false
+            ]
+            
+            if let expiresTimeInterval = cookieDict["expiresDate"] as? TimeInterval {
+                properties[.expires] = Date(timeIntervalSince1970: expiresTimeInterval)
+            }
+            
+            if let cookie = HTTPCookie(properties: properties) {
+                cookies.append(cookie)
+            }
+        }
+        
+        return cookies
+    }
+    
     private static func checkNewDomain(_ newDomain: String) -> Bool {
         var result = false
         let url = URL(string: "\(newDomain)dipp/NuN1v3rs3/Qm3r4i0/get_ip_domain?account=\(Nexilis.sAPIKey)")!
@@ -859,6 +912,7 @@ public class Nexilis: NSObject {
                     
                     let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields, for: url)
                     HTTPCookieStorage.shared.setCookies(cookies, for: url, mainDocumentURL: nil)
+                    Utils.setCookiesMobileForStorage(value: convertCookiesToJSONString(cookies: cookies) ?? "")
                     if let cookieHeader = HTTPCookie.requestHeaderFields(with: cookies)["Cookie"] {
                      Utils.setCookiesMobile(value: cookieHeader.replacingOccurrences(of: "; ", with: ";"))
                     }
@@ -1041,6 +1095,8 @@ public class Nexilis: NSObject {
             APIS.openConversation()
         } else if index == IDX_FAVORITEMESSAGE {
             APIS.openFavoriteMessage()
+        } else if index == IDX_SECURE_FOLDER {
+            APIS.openSecureFolder()
         } else {
             openApp(id: id)
         }
@@ -1438,7 +1494,7 @@ public class Nexilis: NSObject {
                         //print(error)
                     }
                 }
-                if !withStatus && !fromAPNS {
+                if !withStatus && !fromAPNS && !messageExist {
                     DispatchQueue.main.async {
                         if let delegate = Nexilis.shared.messageDelegate, Utils.getSetProfile() {
                             message.mBodies[CoreMessage_TMessageKey.MESSAGE_TEXT] = message.getBody(key : CoreMessage_TMessageKey.MESSAGE_TEXT, default_value : "").toNormalString()
@@ -3378,7 +3434,6 @@ extension Nexilis: MessageDelegate {
                                                 }
                                             }
                                         }
-                                        print("HEHE0 \(members)")
                                         SecureUserDefaults.shared.set("\(members)", forKey: "membersCC")
                                         if message.getBody(key: CoreMessage_TMessageKey.CHANNEL) == "0" {
                                             let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal

+ 31 - 10
NexilisLite/NexilisLite/Source/Utils.swift

@@ -224,6 +224,17 @@ public final class Utils {
         return ""
     }
     
+    static func setCookiesMobileForStorage(value: String) {
+        SecureUserDefaults.shared.set(value, forKey: "cookies_mobile_storage")
+    }
+
+    public static func getCookiesMobileForStorage() -> String {
+        if let value: String = SecureUserDefaults.shared.value(forKey: "cookies_mobile_storage") {
+            return value
+        }
+        return ""
+    }
+    
     static func getBackground() -> String {
         if let value: String = SecureUserDefaults.shared.value(forKey: "app_builder_background") {
             return value
@@ -401,47 +412,57 @@ public final class Utils {
         if chat.credential == "1" && chat.lock == "2" {
             return ("🚫 _"+"Message has expired".localized()+"_").richText(group_id: chat.pin)
         } else if chat.credential == "1" {
-            return "Confidential Message".localized().richText(group_id: chat.pin)
+            return showNSMutableAttributedString("Confidential Message".localized())
         } else if chat.attachmentFlag == "27" {
-            return ("📄 " + "Live Streaming".localized()).richText(group_id: chat.pin)
+            return showNSMutableAttributedString(("📄 " + "Live Streaming".localized()))
         } else if chat.attachmentFlag == "26" {
-            return ("📄 " + "Seminar".localized()).richText(group_id: chat.pin)
+            return showNSMutableAttributedString(("📄 " + "Seminar".localized()))
         } else if !chat.audio.isEmpty {
-            return ("♫ " + "Audio".localized()).richText(group_id: chat.pin)
+            return showNSMutableAttributedString(("♫ " + "Audio".localized()))
         } else if !chat.image.isEmpty {
             if !chat.messageText.isEmpty {
                 return "📷 \(chat.messageText)".richText(group_id: chat.pin)
             } else {
-                return ("📷 " + "Photo".localized()).richText(group_id: chat.pin)
+                return showNSMutableAttributedString(("📷 " + "Photo".localized()))
             }
         }
         else if !chat.gif.isEmpty {
             if !chat.messageText.isEmpty {
                 return "🎬 \(chat.messageText)".richText(group_id: chat.pin)
             } else {
-                return ("🎬 " + "GIF".localized()).richText(group_id: chat.pin)
+                return showNSMutableAttributedString("🎬 GIF")
             }
         }
         else if !chat.video.isEmpty {
             if !chat.messageText.isEmpty {
                 return "📹 \(chat.messageText)".richText(group_id: chat.pin)
             } else {
-                return ("📹 " + "Video".localized()).richText(group_id: chat.pin)
+                return showNSMutableAttributedString(("📹 " + "Video".localized()))
             }
         }
         else if !chat.file.isEmpty {
             if chat.messageScope == "18" {
-                return ("📄 Form").richText(group_id: chat.pin)
+                return showNSMutableAttributedString(("📄 Form"))
+            }
+            let nameFile = chat.messageText.components(separatedBy: "|")[0]
+            let dataText = chat.messageText.components(separatedBy: "|")[1]
+            if !dataText.isEmpty {
+                return ("📄 " + dataText).richText(group_id: chat.pin)
             }
-            return ("📄 " + chat.messageText.components(separatedBy: "|")[0]).richText(group_id: chat.pin)
+            return showNSMutableAttributedString(("📄 \(nameFile)"))
         } else if chat.attachmentFlag == "11" {
-            return ("❤️ " + "Sticker".localized()).richText(group_id: chat.pin)
+            return showNSMutableAttributedString(("❤️ " + "Sticker".localized()))
         }
         else {
             return chat.messageText.richText(group_id: chat.pin)
         }
     }
     
+    private static func showNSMutableAttributedString(_ text: String) -> NSMutableAttributedString {
+        let font = UIFont.systemFont(ofSize: 12)
+        return NSMutableAttributedString(string: text, attributes: [NSAttributedString.Key.font: font])
+    }
+    
     static func getURLBase() -> String {
         if let value: String = SecureUserDefaults.shared.value(forKey: "app_builder_url_base") {
             return value

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

@@ -528,6 +528,7 @@ class QmeraAudioViewController: UIViewController {
         editorPersonalVC.complaintId = ticketId
         editorPersonalVC.onGoingCC = true
         editorPersonalVC.isRequestContactCenter = false
+        editorPersonalVC.channelContactCenter = "1"
         editorPersonalVC.users = users
         editorPersonalVC.fromVCAC = true
         let navigationController = CustomNavigationController(rootViewController: editorPersonalVC)

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

@@ -678,6 +678,7 @@ class QmeraVideoViewController: UIViewController {
         editorPersonalVC.complaintId = ticketId
         editorPersonalVC.onGoingCC = true
         editorPersonalVC.isRequestContactCenter = false
+        editorPersonalVC.channelContactCenter = "2"
         editorPersonalVC.users = users
         editorPersonalVC.fromVCAC = true
         let navigationController = CustomNavigationController(rootViewController: editorPersonalVC)

+ 14 - 11
NexilisLite/NexilisLite/Source/View/Chat/ChatGPTBotView.swift

@@ -152,6 +152,9 @@ public class ChatGPTBotView: UIViewController, UIGestureRecognizerDelegate {
         guard let me = User.getMyPin() else {
             return
         }
+        if dataMessages.count > 0 { // && (dataMessages[dataMessages.count - 1]["read_receipts"] as? String ?? "0") == "0"
+            return
+        }
         var user_id:String? = ""
         let message_id = me + CoreMessage_TMessageUtil.getTID()
         let server_date = String(Date().currentTimeMillis())
@@ -422,7 +425,7 @@ public class ChatGPTBotView: UIViewController, UIGestureRecognizerDelegate {
                                             "opposite_pin" : "",
                                             "format" : "",
                                             "blog_id" : "",
-                                            "read_receipts" : "0",
+                                            "read_receipts" : "4",
                                             "chat_id" : "",
                                             "account_type" : "1",
                                             "credential" :"",
@@ -480,7 +483,7 @@ public class ChatGPTBotView: UIViewController, UIGestureRecognizerDelegate {
                                 row["video_id"] = ""
                                 row["image_id"] = ""
                                 row["thumb_id"] = ""
-                                row["read_receipts"] = "0"
+                                row["read_receipts"] = "4"
                                 row["credential"] = ""
                                 row["file_id"] = ""
                                 row["reff_id"] = ""
@@ -661,7 +664,7 @@ public class ChatGPTBotView: UIViewController, UIGestureRecognizerDelegate {
     }
     
     private func getData() {
-        var query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared, blog_id, credential FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND message_scope_id = '31' order by server_date asc"
+        let query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared, blog_id, credential FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND message_scope_id = '31' order by server_date asc"
         Database.shared.database?.inTransaction({ (fmdb, rollback) in
             do {
                 if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
@@ -697,17 +700,17 @@ public class ChatGPTBotView: UIViewController, UIGestureRecognizerDelegate {
                         let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
                         let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
                         if let dirPath = paths.first {
-                            let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["video_id"] as! String)
-                            let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["file_id"] as! String)
+                            let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["video_id"] as? String ?? "")
+                            let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(row["file_id"] as? String ?? "")
                             do {
                                 if ((row["video_id"] as! String) != "") {
-                                    if FileManager.default.fileExists(atPath: videoURL.path) || FileEncryption.shared.isSecureExists(filename: row["video_id"] as! String){
+                                    if FileManager.default.fileExists(atPath: videoURL.path) || FileEncryption.shared.isSecureExists(filename: row["video_id"] as? String ?? ""){
                                         row["progress"] = 100.0
                                     } else {
                                         row["progress"] = 0.0
                                     }
                                 } else {
-                                    if FileManager.default.fileExists(atPath: fileURL.path) || FileEncryption.shared.isSecureExists(filename: row["file_id"] as! String){
+                                    if FileManager.default.fileExists(atPath: fileURL.path) || FileEncryption.shared.isSecureExists(filename: row["file_id"] as? String ?? ""){
                                         row["progress"] = 100.0
                                     } else {
                                         row["progress"] = 0.0
@@ -715,12 +718,12 @@ public class ChatGPTBotView: UIViewController, UIGestureRecognizerDelegate {
                                 }
                             }
                         }
-                        row["chat_date"] = chatDate(stringDate: row["server_date"] as! String)
+                        row["chat_date"] = chatDate(stringDate: row["server_date"] as? String ?? "")
                         row["isSelected"] = false
                         dataMessages.append(row)
                         var gptRow : [String: String] = [:]
                         gptRow["role"] = row["f_pin"] as! String == "-997" ? "assistant" : "user"
-                        gptRow["content"] = row["message_text"] as! String
+                        gptRow["content"] = row["message_text"] as? String ?? ""
                         chatGPTMessages.append(gptRow)
                     }
                     cursorData.close()
@@ -1342,9 +1345,9 @@ extension ChatGPTBotView: UIContextMenuInteractionDelegate {
             }
         })
         
-        var children: [UIMenuElement] = [copy, delete]
+        let children: [UIMenuElement] = [copy, delete]
 //        let copyOption = self.copyOption(indexPath: indexPath!)
-        let idMe = User.getMyPin() as String?
+//        let idMe = User.getMyPin() as String?
 //        if (dataMessages[indexPath!.row]["lock"] != nil && dataMessages[indexPath!.row]["lock"] as! String == "1") || dataMessages[indexPath!.row]["message_scope_id"] as! String == "18" || dataPerson["f_pin"] == "-999" || dataMessages[indexPath!.row]["credential"] as! String == "1" {
 //            children = [delete]
 //        } else {

+ 34 - 21
NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift

@@ -159,6 +159,9 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
         super.viewDidLoad()
 //        navigationController?.navigationBar.topItem?.title = ""
         Utils.addBackground(view: contactChatNav.view)
+        if Nexilis.fromMAB {
+            Nexilis.floatingButton.isHidden = true
+        }
         
         viewButton.layer.shadowColor = self.traitCollection.userInterfaceStyle == .dark ? UIColor.white.cgColor : UIColor.gray.cgColor
         viewButton.layer.shadowOpacity = 1
@@ -2395,7 +2398,7 @@ extension EditorGroup: UIDocumentPickerDelegate, DocumentPickerDelegate, QLPrevi
             let urlFile = self.previewItem?.absoluteString
             var originaFileName = (urlFile! as NSString).lastPathComponent
             originaFileName = NSString(string: originaFileName).removingPercentEncoding!
-            let renamedNameFile = "Nexilis_doc_" + "\(Date().currentTimeMillis())_" + originaFileName
+            let renamedNameFile = "Nexilis_\(Date().currentTimeMillis())_" + originaFileName
             let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
             let fileURL = documentsDirectory.appendingPathComponent(renamedNameFile)
             if !FileManager.default.fileExists(atPath: fileURL.path) {
@@ -2481,20 +2484,23 @@ extension EditorGroup: UITextViewDelegate {
             UIView.animate(withDuration: 0.3) {
                 let numberOfLines = textView.textContainer.lineBreakMode == .byWordWrapping ? Int(textView.contentSize.height / textView.font!.lineHeight) - 1 : 1
                 if currentLine == 0 && numberOfLines == 1 {
-                    self.heightTextFieldSend.constant = 40
                     if self.isEditingMessage {
                         self.constraintHeighteditTextView.constant = 40
+                    } else {
+                        self.heightTextFieldSend.constant = 40
                     }
                 } else if (self.heightTextFieldSend.constant < 95.0 || (self.constraintHeighteditTextView != nil && self.constraintHeighteditTextView.constant < 95.0)) && currentLine >= 4 {
-                    self.heightTextFieldSend.constant = 95.0
                     if self.isEditingMessage {
                         self.constraintHeighteditTextView.constant = 95.0
+                    } else {
+                        self.heightTextFieldSend.constant = 95.0
                     }
                 } else if currentLine < 4 && numberOfLines < 5 {
                     if (nowTextFieldSend!.text.count > 0 && self.heightTextFieldSend.constant != nowTextFieldSend!.contentSize.height) {
-                        self.heightTextFieldSend.constant = nowTextFieldSend!.contentSize.height
                         if self.isEditingMessage {
                             self.constraintHeighteditTextView.constant = nowTextFieldSend!.contentSize.height
+                        } else {
+                            self.heightTextFieldSend.constant = nowTextFieldSend!.contentSize.height
                         }
                     }
                 }
@@ -2547,12 +2553,10 @@ extension EditorGroup: UITextViewDelegate {
             }
         }
         
-        if textView.text.contains("*") || textView.text.contains("_") || textView.text.contains("^") || textView.text.contains("~") || textView.text.contains("@") {
-            textView.preserveCursorPosition(withChanges: { _ in
-                textView.attributedText = textView.text.richText(isEditing: true, group_id: self.dataGroup["group_id"]  as? String ?? "", listMentionInTextField: listMentionInTextField)
-                return .preserveCursor
-            })
-        }
+        textView.preserveCursorPosition(withChanges: { _ in
+            textView.attributedText = textView.text.richText(isEditing: true, group_id: self.dataGroup["group_id"]  as? String ?? "", listMentionInTextField: listMentionInTextField)
+            return .preserveCursor
+        })
     }
     
     private func showMention(text: String) {
@@ -2810,7 +2814,7 @@ extension EditorGroup: UITextViewDelegate {
     }
     
     public func textViewDidEndEditing(_ textView: UITextView) {
-        if textView.text.isEmpty {
+        if textView.text.isEmpty && textView != editTextView {
             textView.text = "Send message".localized()
             textView.textColor = UIColor.lightGray
         }
@@ -3053,7 +3057,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
                             let dataContent = json["content"]!
                             let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
                             if idx != nil{
-                                self.dataMessages[idx!][TypeDataMessage.message_text] = dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "" + "\n\n" + "%\(dataContent)%"
+                                self.dataMessages[idx!][TypeDataMessage.message_text] = (dataMessages[indexPath!.row][TypeDataMessage.message_text] as? String ?? "") + "\n\n" + "$\(dataContent)$"
                             }
                             DispatchQueue.main.async{
                                 self.tableChatView.reloadRows(at: [indexPath!], with: .none)
@@ -3238,7 +3242,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
                     children.insert(edit, at: children.count - 1)
                 }
                 if !(dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "").isEmpty {
-                    if (dataMessages[indexPath!.row]["f_pin"]  as? String ?? "") == idMe {
+                    if (dataMessages[indexPath!.row]["f_pin"]  as? String ?? "") == idMe && ((dataMessages[indexPath!.row][TypeDataMessage.is_forwarded] as? Int) ?? 0) == 0 {
                         children.insert(edit, at: children.count - 1)
                     }
                     isMore = true
@@ -3304,7 +3308,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             buttonSend.actionHandle(controlEvents: .touchUpInside,
              ForAction:{() -> Void in
                 let newText = self.editTextView.text ?? ""
-                if newText.trimmingCharacters(in: .whitespacesAndNewlines) != oldText {
+                if !newText.isEmpty && newText.trimmingCharacters(in: .whitespacesAndNewlines) != oldText {
                     let lastEdited = Int64(Date().currentTimeMillis())
                     let message = CoreMessage_TMessageBank.editMessage(message_id: dataMessages[indexPath.row][TypeDataMessage.message_id]  as? String ?? "", l_pin: dataMessages[indexPath.row][TypeDataMessage.l_pin]  as? String ?? "", message_scope_id: dataMessages[indexPath.row][TypeDataMessage.message_scope_id]  as? String ?? "", status: "1", message_text: newText, credential: dataMessages[indexPath.row][TypeDataMessage.credential]  as? String ?? "", attachment_flag: dataMessages[indexPath.row][TypeDataMessage.attachment_flag]  as? String ?? "", ex_blog_id: dataMessages[indexPath.row][TypeDataMessage.blog_id]  as? String ?? "", message_large_text: "", ex_format: "", image_id: dataMessages[indexPath.row][TypeDataMessage.image_id]  as? String ?? "", audio_id: dataMessages[indexPath.row][TypeDataMessage.audio_id]  as? String ?? "", video_id: dataMessages[indexPath.row][TypeDataMessage.video_id]  as? String ?? "", file_id: dataMessages[indexPath.row][TypeDataMessage.file_id]  as? String ?? "", thumb_id: dataMessages[indexPath.row][TypeDataMessage.thumb_id]  as? String ?? "", reff_id: dataMessages[indexPath.row][TypeDataMessage.reff_id]  as? String ?? "", read_receipts: dataMessages[indexPath.row][TypeDataMessage.read_receipts]  as? String ?? "", chat_id: dataMessages[indexPath.row][TypeDataMessage.chat_id]  as? String ?? "", is_call_center: dataMessages[indexPath.row][TypeDataMessage.is_call_center]  as? String ?? "", call_center_id: dataMessages[indexPath.row][TypeDataMessage.call_center_id]  as? String ?? "", opposite_pin: dataMessages[indexPath.row][TypeDataMessage.opposite_pin]  as? String ?? "", last_edit: lastEdited)
                     Nexilis.addQueueMessage(message: message, isEditMessage: true)
@@ -3371,7 +3375,12 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
         }
         editVC.modalTransitionStyle = .crossDissolve
         editVC.modalPresentationStyle = .overFullScreen
-        self.present(editVC, animated: true)
+        self.present(editVC, animated: true, completion: {
+            self.constraintHeighteditTextView.constant = self.editTextView.contentSize.height
+            if self.constraintHeighteditTextView.constant > 95 {
+                self.constraintHeighteditTextView.constant = 95.0
+            }
+        })
     }
     
     @objc func dismissEditVC() {
@@ -4552,6 +4561,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
         messageText.font = .systemFont(ofSize: 12)
         
         var textChat = (dataMessages[indexPath.row]["message_text"])! as? String
+        let originalMessageText = textChat
         if (dataMessages[indexPath.row]["lock"] != nil && (dataMessages[indexPath.row]["lock"])! as? String == "1") {
             if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
                 textChat = "🚫 _"+"You were deleted this message".localized()+"_"
@@ -4631,6 +4641,9 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
                     textChat = textChat!.components(separatedBy: "■")[0]
                     textChat = textChat!.trimmingCharacters(in: .whitespacesAndNewlines)
                 }
+                if !fileChat.isEmpty {
+                    textChat = textChat!.components(separatedBy: "|")[1]
+                }
                 let finalAtribute = textChat!.richText(group_id: self.dataGroup["group_id"]  as? String ?? "")
                 textChat = finalAtribute.string
                 let urlPattern = "(https?://|www\\.)\\S+"
@@ -4995,12 +5008,12 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
             let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
             let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
             let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
-            let arrExtFile = (textChat?.components(separatedBy: "|")[0])?.split(separator: ".")
-            let finalExtFile = arrExtFile![arrExtFile!.count - 1]
+            let arrExtFile = (originalMessageText!.components(separatedBy: "|")[0]).split(separator: ".")
+            let finalExtFile = arrExtFile[arrExtFile.count - 1]
             if let dirPath = paths.first {
                 let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(fileChat)
                 if FileManager.default.fileExists(atPath: fileURL.path) {
-                    if let dataFile = try? Data(contentsOf: fileURL) {
+                    if let dataFile = try? Data(contentsOf: fileURL), textChat!.isEmpty {
                         var sizeOfFile = Int(dataFile.count / 1000000)
                         if (sizeOfFile < 1) {
                             sizeOfFile = Int(dataFile.count / 1000)
@@ -5020,7 +5033,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
                 }
                 else if FileEncryption.shared.isSecureExists(filename: fileChat) {
                     do {
-                        if let dataFile = try FileEncryption.shared.readSecure(filename: fileChat) {
+                        if let dataFile = try FileEncryption.shared.readSecure(filename: fileChat), textChat!.isEmpty {
                             var sizeOfFile = Int(dataFile.count / 1000000)
                             if (sizeOfFile < 1) {
                                 sizeOfFile = Int(dataFile.count / 1000)
@@ -5075,7 +5088,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
             nameFile.widthAnchor.constraint(lessThanOrEqualToConstant: 200).isActive = true
             nameFile.font = UIFont.systemFont(ofSize: 12, weight: .medium)
             nameFile.textColor = .white
-            nameFile.text = textChat?.components(separatedBy: "|")[0]
+            nameFile.text = originalMessageText?.components(separatedBy: "|")[0]
             
             if (dataMessages[indexPath.row]["progress"] as! Double != 100.0) {
                 let containerLoading = UIView()
@@ -5500,7 +5513,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
             titleForwarded.anchor(top: containerForwarded.topAnchor, left: imageForwarded.rightAnchor, right: containerForwarded.rightAnchor, height: 15)
             titleForwarded.font = .systemFont(ofSize: 15)
             let textForwarded = "Forwarded".localized()
-            titleForwarded.attributedText = " %\(textForwarded)%".richText()
+            titleForwarded.attributedText = " $\(textForwarded)$".richText()
         }
         
         return cellMessage

+ 43 - 24
NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -163,6 +163,10 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
 //        navigationController?.navigationBar.topItem?.title = ""
         Utils.addBackground(view: contactChatNav.view)
         
+        if Nexilis.fromMAB {
+            Nexilis.floatingButton.isHidden = true
+        }
+        
         viewButton.layer.shadowColor = self.traitCollection.userInterfaceStyle == .dark ? UIColor.white.cgColor : UIColor.gray.cgColor
         viewButton.layer.shadowOpacity = 1
         viewButton.layer.shadowOffset = .zero
@@ -2126,8 +2130,16 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                 }
                 present(picker, animated: true, completion: nil)
             case "imageCamera":
+                if isContactCenter && channelContactCenter == "2" {
+                    self.view.makeToast("You can't take photo when Video Call".localized(), duration: 3)
+                    return
+                }
                 imageVideoPicker.present(source: .imageCamera)
             case "videoCamera":
+                if isContactCenter && channelContactCenter == "2" {
+                    self.view.makeToast("You can't take video when Video Call".localized(), duration: 3)
+                    return
+                }
                 imageVideoPicker.present(source: .videoCamera)
             default:
                 break
@@ -3594,7 +3606,7 @@ extension EditorPersonal: UIDocumentPickerDelegate, DocumentPickerDelegate, QLPr
             let urlFile = self.previewItem?.absoluteString
             var originaFileName = (urlFile! as NSString).lastPathComponent
             originaFileName = NSString(string: originaFileName).removingPercentEncoding!
-            let renamedNameFile = "Nexilis_doc_" + "\(Date().currentTimeMillis())_" + originaFileName
+            let renamedNameFile = "Nexilis_\(Date().currentTimeMillis())_" + originaFileName
             let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
             let fileURL = documentsDirectory.appendingPathComponent(renamedNameFile)
             if !FileManager.default.fileExists(atPath: fileURL.path) {
@@ -3634,20 +3646,23 @@ extension EditorPersonal: UITextViewDelegate {
             UIView.animate(withDuration: 0.3) {
                 let numberOfLines = textView.textContainer.lineBreakMode == .byWordWrapping ? Int(textView.contentSize.height / textView.font!.lineHeight) - 1 : 1
                 if currentLine == 0 && numberOfLines == 1 {
-                    self.heightTextFieldSend.constant = 40
                     if self.isEditingMessage {
                         self.constraintHeighteditTextView.constant = 40
+                    } else {
+                        self.heightTextFieldSend.constant = 40
                     }
                 } else if (self.heightTextFieldSend.constant < 95.0 || (self.constraintHeighteditTextView != nil && self.constraintHeighteditTextView.constant < 95.0)) && currentLine >= 4 {
-                    self.heightTextFieldSend.constant = 95.0
                     if self.isEditingMessage {
                         self.constraintHeighteditTextView.constant = 95.0
+                    } else {
+                        self.heightTextFieldSend.constant = 95.0
                     }
                 } else if currentLine < 4 && numberOfLines < 5 {
                     if (nowTextFieldSend!.text.count > 0 && self.heightTextFieldSend.constant != nowTextFieldSend!.contentSize.height) {
-                        self.heightTextFieldSend.constant = nowTextFieldSend!.contentSize.height
                         if self.isEditingMessage {
                             self.constraintHeighteditTextView.constant = nowTextFieldSend!.contentSize.height
+                        } else {
+                            self.heightTextFieldSend.constant = nowTextFieldSend!.contentSize.height
                         }
                     }
                 }
@@ -3674,12 +3689,10 @@ extension EditorPersonal: UITextViewDelegate {
         timerCheckLink = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: {_ in
             self.checkLink(fullText: textView.text)
         })
-        if textView.text.contains("*") || textView.text.contains("_") || textView.text.contains("^") || textView.text.contains("~") {
-            textView.preserveCursorPosition(withChanges: { _ in
-                textView.attributedText = textView.text.richText(isEditing: true)
-                return .preserveCursor
-            })
-        }
+        textView.preserveCursorPosition(withChanges: { _ in
+            textView.attributedText = textView.text.richText(isEditing: true)
+            return .preserveCursor
+        })
     }
     
     private func checkLink(fullText: String) {
@@ -3873,7 +3886,7 @@ extension EditorPersonal: UITextViewDelegate {
     }
     
     public func textViewDidEndEditing(_ textView: UITextView) {
-        if textView.text.isEmpty {
+        if textView.text.isEmpty && textView != editTextView {
             textView.text = "Send message".localized()
             textView.textColor = UIColor.lightGray
         }
@@ -4100,7 +4113,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
                             let dataContent = json["content"]!
                             let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[indexPath!.row]["message_id"] as? String})
                             if idx != nil{
-                                self.dataMessages[idx!][TypeDataMessage.message_text] = dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "" + "\n\n" + "%\(dataContent)%"
+                                self.dataMessages[idx!][TypeDataMessage.message_text] = (dataMessages[indexPath!.row][TypeDataMessage.message_text] as? String ?? "") + "\n\n" + "$\(dataContent)$"
                             }
                             DispatchQueue.main.async{
                                 self.tableChatView.reloadRows(at: [indexPath!], with: .none)
@@ -4292,7 +4305,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
                 children.insert(info, at: children.count - 1)
             }
             if !(dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "").isEmpty {
-                if (dataMessages[indexPath!.row]["f_pin"]  as? String ?? "") == idMe {
+                if (dataMessages[indexPath!.row]["f_pin"]  as? String ?? "") == idMe && ((dataMessages[indexPath!.row][TypeDataMessage.is_forwarded] as? Int) ?? 0) == 0 {
                     children.insert(edit, at: children.count - 1)
                 }
                 isMore = true
@@ -4356,7 +4369,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
             buttonSend.actionHandle(controlEvents: .touchUpInside,
              ForAction:{() -> Void in
                 let newText = self.editTextView.text ?? ""
-                if newText.trimmingCharacters(in: .whitespacesAndNewlines) != oldText {
+                if !newText.isEmpty && newText.trimmingCharacters(in: .whitespacesAndNewlines) != oldText {
                     let lastEdited = Int64(Date().currentTimeMillis())
                     let message = CoreMessage_TMessageBank.editMessage(message_id: dataMessages[indexPath.row][TypeDataMessage.message_id]  as? String ?? "", l_pin: dataMessages[indexPath.row][TypeDataMessage.l_pin]  as? String ?? "", message_scope_id: dataMessages[indexPath.row][TypeDataMessage.message_scope_id]  as? String ?? "", status: "1", message_text: newText, credential: dataMessages[indexPath.row][TypeDataMessage.credential]  as? String ?? "", attachment_flag: dataMessages[indexPath.row][TypeDataMessage.attachment_flag]  as? String ?? "", ex_blog_id: dataMessages[indexPath.row][TypeDataMessage.blog_id]  as? String ?? "", message_large_text: "", ex_format: "", image_id: dataMessages[indexPath.row][TypeDataMessage.image_id]  as? String ?? "", audio_id: dataMessages[indexPath.row][TypeDataMessage.audio_id]  as? String ?? "", video_id: dataMessages[indexPath.row][TypeDataMessage.video_id]  as? String ?? "", file_id: dataMessages[indexPath.row][TypeDataMessage.file_id]  as? String ?? "", thumb_id: dataMessages[indexPath.row][TypeDataMessage.thumb_id]  as? String ?? "", reff_id: dataMessages[indexPath.row][TypeDataMessage.reff_id]  as? String ?? "", read_receipts: dataMessages[indexPath.row][TypeDataMessage.read_receipts]  as? String ?? "", chat_id: dataMessages[indexPath.row][TypeDataMessage.chat_id]  as? String ?? "", is_call_center: dataMessages[indexPath.row][TypeDataMessage.is_call_center]  as? String ?? "", call_center_id: dataMessages[indexPath.row][TypeDataMessage.call_center_id]  as? String ?? "", opposite_pin: dataMessages[indexPath.row][TypeDataMessage.opposite_pin]  as? String ?? "", last_edit: lastEdited)
                     Nexilis.addQueueMessage(message: message, isEditMessage: true)
@@ -4423,7 +4436,12 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
         }
         editVC.modalTransitionStyle = .crossDissolve
         editVC.modalPresentationStyle = .overFullScreen
-        self.present(editVC, animated: true)
+        self.present(editVC, animated: true, completion: {
+            self.constraintHeighteditTextView.constant = self.editTextView.contentSize.height
+            if self.constraintHeighteditTextView.constant > 95 {
+                self.constraintHeighteditTextView.constant = 95.0
+            }
+        })
     }
     
     @objc func dismissEditVC() {
@@ -5753,6 +5771,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
         }
         messageText.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
         var textChat = (dataMessages[indexPath.row]["message_text"] as? String) ?? ""
+        let originalMessageText = textChat
         if (dataMessages[indexPath.row]["lock"] != nil && (dataMessages[indexPath.row]["lock"])! as? String == "1") {
             if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
                 textChat = "🚫 _"+"You were deleted this message".localized()+"_"
@@ -5842,6 +5861,9 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                     textChat = textChat.components(separatedBy: "■")[0]
                     textChat = textChat.trimmingCharacters(in: .whitespacesAndNewlines)
                 }
+                if !fileChat.isEmpty {
+                    textChat = textChat.components(separatedBy: "|")[1]
+                }
                 let finalAtribute = textChat.richText()
                 textChat = finalAtribute.string
                 let urlPattern = "(https?://|www\\.)\\S+"
@@ -6209,12 +6231,12 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
             let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
             let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
             let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
-            let arrExtFile = (textChat.components(separatedBy: "|")[0]).split(separator: ".")
+            let arrExtFile = (originalMessageText.components(separatedBy: "|")[0]).split(separator: ".")
             let finalExtFile = arrExtFile[arrExtFile.count - 1]
             if let dirPath = paths.first {
                 let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(fileChat)
                 if FileManager.default.fileExists(atPath: fileURL.path) {
-                    if let dataFile = try? Data(contentsOf: fileURL) {
+                    if let dataFile = try? Data(contentsOf: fileURL), textChat.isEmpty {
                         var sizeOfFile = Int(dataFile.count / 1000000)
                         if (sizeOfFile < 1) {
                             sizeOfFile = Int(dataFile.count / 1000)
@@ -6230,12 +6252,10 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                                 messageText.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
                             }
                         }
-                    } else {
-                        messageText.text = ""
                     }
                 }
                 else if FileEncryption.shared.isSecureExists(filename: fileChat) {
-                    if let dataFile = try? FileEncryption.shared.readSecure(filename: fileChat) {
+                    if let dataFile = try? FileEncryption.shared.readSecure(filename: fileChat), textChat.isEmpty {
                         var sizeOfFile = Int(dataFile.count / 1000000)
                         if (sizeOfFile < 1) {
                             sizeOfFile = Int(dataFile.count / 1000)
@@ -6251,8 +6271,6 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                                 messageText.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
                             }
                         }
-                    } else {
-                        messageText.text = ""
                     }
                 }
             }
@@ -6289,7 +6307,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
             nameFile.widthAnchor.constraint(lessThanOrEqualToConstant: 200).isActive = true
             nameFile.font = UIFont.systemFont(ofSize: 12, weight: .medium)
             nameFile.textColor = .white
-            nameFile.text = textChat.components(separatedBy: "|")[0]
+            nameFile.text = originalMessageText.components(separatedBy: "|")[0]
             
             if (dataMessages[indexPath.row]["progress"] as! Double != 100.0) {
                 let containerLoading = UIView()
@@ -6703,7 +6721,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
             titleForwarded.anchor(top: containerForwarded.topAnchor, left: imageForwarded.rightAnchor, right: containerForwarded.rightAnchor, height: 15)
             titleForwarded.font = .systemFont(ofSize: 15)
             let textForwarded = "Forwarded".localized()
-            titleForwarded.attributedText = " %\(textForwarded)%".richText()
+            titleForwarded.attributedText = " $\(textForwarded)$".richText()
         }
         topMarginText.isActive = true
 //        let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureCellAction))
@@ -7116,6 +7134,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
         } else if (sender.file_id != "") {
             if let dirPath = paths.first {
                 let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
+                print("MASUK SINI KAH? \(fileURL)")
                 if FileManager.default.fileExists(atPath: fileURL.path) {
                     self.previewItem = fileURL as NSURL
                     let previewController = QLPreviewController()

+ 5 - 48
NexilisLite/NexilisLite/Source/View/Chat/EditorStarMessages.swift

@@ -73,65 +73,22 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
         tableChatView.reloadData()
         
         let center: NotificationCenter = NotificationCenter.default
-        center.addObserver(self, selector: #selector(onStatusChat(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerStatusChat), object: nil)
+        center.addObserver(self, selector: #selector(onRefreshData(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerStatusChat), object: nil)
         center.addObserver(self, selector: #selector(onRefreshData(notification:)), name: NSNotification.Name(rawValue: "listenerStarMessage"), object: nil)
 
     }
     
     @objc func onRefreshData(notification: NSNotification) {
-        getData()
-        tableChatView.reloadData()
+        DispatchQueue.main.async { [self] in
+            getData()
+            tableChatView.reloadData()
+        }
     }
     
     @objc func didTapExit() {
         self.dismiss(animated: true, completion: nil)
     }
     
-    @objc func onStatusChat(notification: NSNotification) {
-        DispatchQueue.main.async {
-            let data:[AnyHashable : Any] = notification.userInfo!
-            if let dataMessage = data["message"] as? TMessage {
-                let chatData = dataMessage.mBodies
-                if (chatData.keys.contains(CoreMessage_TMessageKey.MESSAGE_ID) && !(chatData[CoreMessage_TMessageKey.MESSAGE_ID]!).contains("-2,")) {
-                    let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == chatData[CoreMessage_TMessageKey.MESSAGE_ID]! })
-                    if (idx != nil) {
-                        if (chatData[CoreMessage_TMessageKey.DELETE_MESSAGE_FLAG] == "1") {
-                            let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
-                            let row = self.dataMessages.filter({ $0["chat_date"] as! String == self.dataMessages[idx!]["chat_date"] as! String}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String })
-                            self.dataMessages.remove(at: idx!)
-                            if row != nil && section != nil  {
-                                let indexPath = IndexPath(row: row!, section: section!)
-                                self.tableChatView.deleteRows(at: [indexPath], with: .fade)
-                                if self.dataMessages.filter({ $0["chat_date"] as! String == self.dataMessages[indexPath.row]["chat_date"] as! String }).count == 0 {
-                                    self.dataDates.remove(at: indexPath.section)
-                                    self.tableChatView.deleteSections(IndexSet(integer: indexPath.section), with: .fade)
-                                }
-                            }
-                        }
-                    }
-                }
-                else if (chatData.keys.contains("message_id")) {
-                    let idx = self.dataMessages.firstIndex(where: { "'\(String(describing: $0["message_id"] as? String))'" == chatData["message_id"]! })
-                    if (idx != nil) {
-                        if (chatData[CoreMessage_TMessageKey.DELETE_MESSAGE_FLAG] == "1") {
-                            let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
-                            let row = self.dataMessages.filter({ $0["chat_date"] as! String == self.dataMessages[idx!]["chat_date"] as! String}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String })
-                            self.dataMessages.remove(at: idx!)
-                            if row != nil && section != nil  {
-                                let indexPath = IndexPath(row: row!, section: section!)
-                                self.tableChatView.deleteRows(at: [indexPath], with: .fade)
-                                if self.dataMessages.filter({ $0["chat_date"] as! String == self.dataMessages[indexPath.row]["chat_date"] as! String }).count == 0 {
-                                    self.dataDates.remove(at: indexPath.section)
-                                    self.tableChatView.deleteSections(IndexSet(integer: indexPath.section), with: .fade)
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-    
     public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
         let containerView = UIView()
         containerView.backgroundColor = .clear

+ 10 - 10
NexilisLite/NexilisLite/Source/View/Chat/PreviewAttachmentImageVideo.swift

@@ -321,14 +321,14 @@ class PreviewAttachmentImageVideo: UIViewController, UIScrollViewDelegate, UITex
                 originalImageName = (urlImage! as NSString).lastPathComponent
             }
             let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
-            let compressedImageName = "Nexilis_image_\(originalImageName)"
-            let thumbName = "THUMB_Nexilis_image_\(originalImageName)"
+            let compressedImageName = "Nexilis_image_\(Date().currentTimeMillis())_\(originalImageName)"
+            let thumbName = "THUMB_Nexilis_image_\(Date().currentTimeMillis())_\(originalImageName)"
             let fileURL = documentsDirectory.appendingPathComponent(compressedImageName)
             var compressedImage:Data?
             if (image != nil) {
-                compressedImage = image!.jpegData(compressionQuality:  1.0)
+                compressedImage = image!.jpegData(compressionQuality:  0.5)
             } else {
-                compressedImage = (imageVideoData![.originalImage] as! UIImage).jpegData(compressionQuality:  1.0)
+                compressedImage = (imageVideoData![.originalImage] as! UIImage).jpegData(compressionQuality:  0.5)
             }
             if let data = compressedImage,
                !FileManager.default.fileExists(atPath: fileURL.path) {
@@ -419,8 +419,8 @@ class PreviewAttachmentImageVideo: UIViewController, UIScrollViewDelegate, UITex
                         var thumbName = ""
                         if (fromCopy && dataGIF != nil) {
                             originalVideoName = "\(Date().currentTimeMillis())_copyGif"
-                            renamedVideoName = "Nexilis_gif_\(originalVideoName)"
-                            thumbName = "THUMB_Nexilis_gif_\(originalVideoName)"
+                            renamedVideoName = "Nexilis_gif_\(Date().currentTimeMillis())_\(originalVideoName)"
+                            thumbName = "THUMB_Nexilis_gif_\(Date().currentTimeMillis())_\(originalVideoName)"
                         } else {
                             if imageVideoData != nil {
                                 urlVideo = (imageVideoData![.mediaURL] as! NSURL).absoluteString!
@@ -428,8 +428,8 @@ class PreviewAttachmentImageVideo: UIViewController, UIScrollViewDelegate, UITex
                                 urlVideo = (urlVideoPhpPicker! as NSURL).absoluteString!
                             }
                             originalVideoName = (urlVideo as NSString).lastPathComponent
-                            renamedVideoName = "Nexilis_video_\(originalVideoName)"
-                            thumbName = "THUMB_Nexilis_video_\(originalVideoName)"
+                            renamedVideoName = "Nexilis_video_\(Date().currentTimeMillis())_\(originalVideoName)"
+                            thumbName = "THUMB_Nexilis_video_\(Date().currentTimeMillis())_\(originalVideoName)"
                         }
                         let fileURL = documentsDirectory.appendingPathComponent(renamedVideoName)
                         if !FileManager.default.fileExists(atPath: fileURL.path) {
@@ -446,7 +446,7 @@ class PreviewAttachmentImageVideo: UIViewController, UIScrollViewDelegate, UITex
                         }
                         var dataThumbVideo: Data?
                         if !fromCopy {
-                            dataThumbVideo = imagePreview.image!.jpegData(compressionQuality:  1.0)
+                            dataThumbVideo = imagePreview.image!.jpegData(compressionQuality:  0.5)
                         }
                         let fileURLTHUMB = documentsDirectory.appendingPathComponent(thumbName)
                         if !FileManager.default.fileExists(atPath: fileURLTHUMB.path) {
@@ -456,7 +456,7 @@ class PreviewAttachmentImageVideo: UIViewController, UIScrollViewDelegate, UITex
                                 } else {
                                     if let dataGIF = dataGIF {
                                         if let dataThumbGif = UIImage(data: dataGIF) {
-                                            if let compressedDataThumbGif = dataThumbGif.jpegData(compressionQuality: 1.0) {
+                                            if let compressedDataThumbGif = dataThumbGif.jpegData(compressionQuality: 0.5) {
                                                 try compressedDataThumbGif.write(to: fileURLTHUMB)
                                             }
                                         }

+ 13 - 0
NexilisLite/NexilisLite/Source/View/Chat/SecureFolderView.swift

@@ -38,12 +38,25 @@ class SecureFolderViewController: UIViewController, UISearchBarDelegate, UIColle
         super.viewDidLoad()
         self.title = "Secure Folder"
         view.backgroundColor = .white
+        let attributes = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 16.0), 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
+        
+        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel".localized(), style: .plain, target: self, action: #selector(cancel(sender:)))
         
         setupSubviews()
         loadFiles()
         filteredFiles = files
     }
     
+    @objc func cancel(sender: Any) {
+        navigationController?.dismiss(animated: true, completion: nil)
+    }
+    
     func getThumbnail(for fileName: String) -> UIImage? {
         // Logic to get the file thumbnail if it exists.
         // This could include generating a thumbnail from the actual file.

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

@@ -119,7 +119,7 @@ public class ConfigureFloatingButton: UIViewController {
             Nexilis.floatingButton.removeFromSuperview()
             Nexilis.floatingButton = FloatingButton()
             let viewController = (UIApplication.shared.windows.first?.rootViewController)!
-            Nexilis.addFB(viewController: viewController, fromMAB: true)
+            Nexilis.addFB(viewController: viewController, fromMAB: Nexilis.fromMAB)
         }
         close()
     }

+ 31 - 1
NexilisLite/NexilisLite/Source/View/Control/ContactChatViewController.swift

@@ -229,6 +229,7 @@ class ContactChatViewController: UITableViewController {
             self.navigationController?.navigationBar.setNeedsLayout()
         }
         getData()
+        APIS.setDataForShareExtension()
     }
     
     @objc func addFriend(sender: UIBarButtonItem) {
@@ -322,7 +323,36 @@ class ContactChatViewController: UITableViewController {
     @objc func segmentChanged(sender: Any) {
         switch segment.selectedSegmentIndex {
         case 0:
-            Utils.inTabChats = true
+            if segment.numberOfSegments == 3 {
+                Utils.inTabChats = true
+            }
+        case 1:
+            Utils.inTabChats = false
+            if segment.numberOfSegments < 3 {
+                DispatchQueue.global().async {
+                    self.getOpenGroups(listGroups: self.groups, completion: { g in
+                        DispatchQueue.main.async {
+                            for og in g {
+                                if self.groups.first(where: { $0.id == og.id }) == nil {
+                                    self.groups.append(og)
+                                }
+                            }
+                            self.groups.sort { (a, b) -> Bool in
+                                if Int(a.official) == 1 {
+                                    return true
+                                } else if Int(b.official) == 1 {
+                                    return false
+                                } else {
+                                    return Int(a.official) ?? 0 > Int(b.official) ?? 0
+                                }
+                            }
+                            DispatchQueue.main.async {
+                                self.tableView.reloadData()
+                            }
+                        }
+                    })
+                }
+            }
         case 2:
             Utils.inTabChats = false
             DispatchQueue.global().async {

+ 4 - 3
NexilisLite/NexilisLite/Source/View/Control/HistoryBroadcastViewController.swift

@@ -41,8 +41,9 @@ class HistoryBroadcastViewController: UIViewController, UITableViewDelegate, UIT
         let me = User.getMyPin()!
         Database.shared.database?.inTransaction({ fmdb, rollback in
             do {
-                if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select FIRST_NAME, LAST_NAME, IMAGE_ID, USER_TYPE from BUDDY where F_PIN = '\(me)'"), cursor.next() {
-                    isAdmin = cursor.string(forColumnIndex: 3) == "23" || cursor.string(forColumnIndex: 3) == "24"
+                if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select official_account from BUDDY where F_PIN = '\(me)'"), cursor.next() {
+                    let official = cursor.string(forColumnIndex: 0)!
+                    isAdmin = User.isOfficial(official_account: official)
                     cursor.close()
                 }
             } catch {
@@ -50,7 +51,7 @@ class HistoryBroadcastViewController: UIViewController, UITableViewDelegate, UIT
                 print("Access database error: \(error.localizedDescription)")
             }
         })
-        if isAdmin {
+        if isAdmin  {
             let broadcastImage = UIImage(systemName: "plus.bubble.fill")
             let buttonBroadcast = UIBarButtonItem(image: broadcastImage,  style: .plain, target: self, action: #selector(didTapBroadcastButton(sender:)))
             navigationItem.rightBarButtonItem = buttonBroadcast

+ 7 - 7
NexilisLite/NexilisLite/Source/View/Control/HistoryCCViewController.swift

@@ -75,22 +75,22 @@ public class HistoryCCViewController: UITableViewController, QLPreviewController
             return cellNoData
         }
         let cell = tableView.dequeueReusableCell(withIdentifier: "cellHistoryCC", for: indexPath) as! CellMyHistory
-        let dataOfficer = getDataProfile(f_pin: data[indexPath.row]["officer"] as! String)
-        let dataRequester = getDataProfile(f_pin: data[indexPath.row]["requester"] as! String)
+        let dataOfficer = getDataProfile(f_pin: data[indexPath.row]["officer"] as? String ?? "")
+        let dataRequester = getDataProfile(f_pin: data[indexPath.row]["requester"] as? String ?? "")
         cell.imageOfficer.image = nil
         if dataOfficer.count > 0 {
             if isOfficer {
                 cell.labelOfficer.text = dataRequester["name"] ?? ""
             } else {
-                cell.labelOfficer.text = "Officer".localized() + " : " + dataOfficer["name"]!
+                cell.labelOfficer.text = "Officer".localized() + " : " + (dataOfficer["name"] ?? "")
             }
             if !(dataOfficer["image"] ?? "").isEmpty || !(dataRequester["image"] ?? "").isEmpty {
                 if isOfficer {
-                    getImage(name: dataRequester["image"]!, placeholderImage: UIImage(systemName: "person.circle.fill")!, isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+                    getImage(name: dataRequester["image"] ?? "", placeholderImage: UIImage(systemName: "person.circle.fill")!, isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
                         cell.imageOfficer.image = image
                     }
                 } else {
-                    getImage(name: dataOfficer["image"]!, placeholderImage: UIImage(systemName: "person.circle.fill")!, isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
+                    getImage(name: dataOfficer["image"] ?? "", placeholderImage: UIImage(systemName: "person.circle.fill")!, isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
                         cell.imageOfficer.image = image
                     }
                 }
@@ -114,8 +114,8 @@ public class HistoryCCViewController: UITableViewController, QLPreviewController
                 cell.labelRequester.text = "Requester".localized() + " : User"
             }
         }
-        cell.labelComplaintId.text = data[indexPath.row]["complaint_id"] as? String
-        let stringDate = data[indexPath.row]["date_start"] as! String
+        cell.labelComplaintId.text = data[indexPath.row]["complaint_id"] as? String ?? ""
+        let stringDate = data[indexPath.row]["date_start"] as? String ?? ""
         let date = Date(milliseconds: Int64(stringDate) ?? 0)
         let formatter = DateFormatter()
         formatter.dateFormat = "dd/MM/yyyy HH:mm"

+ 1 - 4
NexilisLite/NexilisLite/Source/View/Control/SettingTableViewController.swift

@@ -438,10 +438,7 @@ public class SettingTableViewController: UITableViewController, UIGestureRecogni
                 navigationController?.show(controller, sender: nil)
             }
         } else if item.title == "Secure Folder" {
-            if(Nexilis.checkIsChangePerson()){
-                let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "secureFolderView") as! SecureFolderViewController
-                navigationController?.show(controller, sender: nil)
-            }
+            APIS.openSecureFolder()
         } else if item.title == "Access Admin / Internal Features".localized() || item.title == "Change Admin / Internal Password".localized() {
             if(Nexilis.checkIsChangePerson()){
                 if !CheckConnection.isConnectedToNetwork()  || API.nGetCLXConnState() == 0 {