浏览代码

update push APN and fix bugs

alqindiirsyam 6 月之前
父节点
当前提交
d955621c60
共有 27 个文件被更改,包括 723 次插入258 次删除
  1. 二进制
      .DS_Store
  2. 二进制
      AppBuilder/.DS_Store
  3. 8 8
      AppBuilder/AppBuilder.xcodeproj/project.pbxproj
  4. 11 73
      AppBuilder/AppBuilder/AppDelegate.swift
  5. 2 5
      AppBuilder/AppBuilder/FirstTabViewController.swift
  6. 1 0
      AppBuilder/AppBuilder/FourthTabViewController.swift
  7. 3 14
      AppBuilder/AppBuilder/Info.plist
  8. 1 0
      AppBuilder/AppBuilder/SceneDelegate.swift
  9. 26 23
      AppBuilder/AppBuilder/SecondTabViewController.swift
  10. 2 6
      AppBuilder/AppBuilder/ThirdTabViewController.swift
  11. 8 2
      AppBuilder/AppBuilder/ViewController.swift
  12. 二进制
      NexilisLite/.DS_Store
  13. 373 0
      NexilisLite/NexilisLite/Source/APIS.swift
  14. 46 0
      NexilisLite/NexilisLite/Source/CoreMessage_TMessageBank.swift
  15. 15 0
      NexilisLite/NexilisLite/Source/CoreMessage_TMessageCode.swift
  16. 1 1
      NexilisLite/NexilisLite/Source/Database.swift
  17. 38 28
      NexilisLite/NexilisLite/Source/FloatingButton/FloatingButton.swift
  18. 29 8
      NexilisLite/NexilisLite/Source/IncomingThread.swift
  19. 9 6
      NexilisLite/NexilisLite/Source/Model/Chat.swift
  20. 21 5
      NexilisLite/NexilisLite/Source/Nexilis.swift
  21. 20 0
      NexilisLite/NexilisLite/Source/Utils.swift
  22. 36 33
      NexilisLite/NexilisLite/Source/View/Call/QmeraAudioViewController.swift
  23. 22 30
      NexilisLite/NexilisLite/Source/View/Call/QmeraVideoViewController.swift
  24. 3 1
      NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift
  25. 13 12
      NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift
  26. 34 3
      NexilisLite/NexilisLite/Source/View/Control/ContactChatViewController.swift
  27. 1 0
      NexilisLite/NexilisLite/Source/View/Control/SettingTableViewController.swift

二进制
.DS_Store


二进制
AppBuilder/.DS_Store


+ 8 - 8
AppBuilder/AppBuilder.xcodeproj/project.pbxproj

@@ -528,7 +528,7 @@
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 5;
-				DEVELOPMENT_TEAM = TS9C3K7699;
+				DEVELOPMENT_TEAM = FR2C2CZUYZ;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Frameworks",
@@ -541,7 +541,7 @@
 					"@executable_path/Frameworks",
 				);
 				MARKETING_VERSION = 5.0.1;
-				PRODUCT_BUNDLE_IDENTIFIER = io.newuniverse.AppBuilder2;
+				PRODUCT_BUNDLE_IDENTIFIER = io.newuniverse.AppBuilder1;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_VERSION = 5.0;
@@ -560,7 +560,7 @@
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 5;
-				DEVELOPMENT_TEAM = TS9C3K7699;
+				DEVELOPMENT_TEAM = FR2C2CZUYZ;
 				FRAMEWORK_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Frameworks",
@@ -573,7 +573,7 @@
 					"@executable_path/Frameworks",
 				);
 				MARKETING_VERSION = 5.0.1;
-				PRODUCT_BUNDLE_IDENTIFIER = io.newuniverse.AppBuilder2;
+				PRODUCT_BUNDLE_IDENTIFIER = io.newuniverse.AppBuilder1;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_VERSION = 5.0;
@@ -588,7 +588,7 @@
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = TS9C3K7699;
+				DEVELOPMENT_TEAM = FR2C2CZUYZ;
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = NotificationService/Info.plist;
 				INFOPLIST_KEY_CFBundleDisplayName = NotificationService;
@@ -600,7 +600,7 @@
 					"@executable_path/../../Frameworks",
 				);
 				MARKETING_VERSION = 1.0;
-				PRODUCT_BUNDLE_IDENTIFIER = io.newuniverse.AppBuilder2.NotificationService;
+				PRODUCT_BUNDLE_IDENTIFIER = io.newuniverse.AppBuilder1.NotificationService;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
 				SWIFT_EMIT_LOC_STRINGS = YES;
@@ -616,7 +616,7 @@
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = TS9C3K7699;
+				DEVELOPMENT_TEAM = FR2C2CZUYZ;
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = NotificationService/Info.plist;
 				INFOPLIST_KEY_CFBundleDisplayName = NotificationService;
@@ -628,7 +628,7 @@
 					"@executable_path/../../Frameworks",
 				);
 				MARKETING_VERSION = 1.0;
-				PRODUCT_BUNDLE_IDENTIFIER = io.newuniverse.AppBuilder2.NotificationService;
+				PRODUCT_BUNDLE_IDENTIFIER = io.newuniverse.AppBuilder1.NotificationService;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
 				SWIFT_EMIT_LOC_STRINGS = YES;

+ 11 - 73
AppBuilder/AppBuilder/AppDelegate.swift

@@ -42,7 +42,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
             let center  = UNUserNotificationCenter.current()
             center.delegate = self
             center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
-                if error == nil{
+                if error == nil && granted {
                     DispatchQueue.main.async {
                         UIApplication.shared.registerForRemoteNotifications()
                     }
@@ -61,7 +61,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
 extension AppDelegate: ConnectDelegate, UNUserNotificationCenterDelegate {
     
     func onSuccess(userId: String) {
-        //print(#function, "userId: \(userId)")
+//        print(#function, "userId: \(userId)")
     }
     
     func onFailed(error: String) {
@@ -70,86 +70,24 @@ extension AppDelegate: ConnectDelegate, UNUserNotificationCenterDelegate {
     
     func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
         let deviceTokenString = deviceToken.map { String(format: "%02x", $0) }.joined()
-        //print("TOKEN: \(deviceTokenString)")
+        APIS.sendPushToken(deviceTokenString)
     }
     
     func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
         //print(error)
     }
     
-    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
-        let userInfo = response.notification.request.content.userInfo
-        if let opposite = userInfo["opposite"] as? String {
-            let user = User.getDataCanNil(pin: opposite)
-            if user != nil {
-                openEditorPersonal(opposite: opposite)
-            } else {
-                let group = Group.getData(group_id: opposite)
-                let topic = Topic.getData(topic_id: opposite)
-                if group != nil || topic != nil {
-                    openEditorGroup(opposite: opposite)
-                }
-            }
-        }
-        UIApplication.shared.applicationIconBadgeNumber = 0
-        UNUserNotificationCenter.current().removeAllDeliveredNotifications()
-        completionHandler()
+    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
+        APIS.showNotificationNexilis(userInfo)
+        completionHandler(.newData)
     }
-    
-    func openEditorPersonal(opposite: String) {
-        let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
-        editorPersonalVC.hidesBottomBarWhenPushed = true
-        editorPersonalVC.unique_l_pin = opposite
-        editorPersonalVC.fromNotification = true
-        let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
-        if !onGoingCC.isEmpty {
-            let compalintId = onGoingCC.components(separatedBy: ",")[2]
-            let fPinCC = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
-            editorPersonalVC.isContactCenter = true
-            editorPersonalVC.fPinContacCenter = fPinCC
-            editorPersonalVC.complaintId = compalintId
-            editorPersonalVC.onGoingCC = true
-            editorPersonalVC.isRequestContactCenter = false
-        }
-        let navigationController = CustomNavigationController(rootViewController: editorPersonalVC)
-        navigationController.modalPresentationStyle = .fullScreen
-        navigationController.navigationBar.tintColor = .white
-        navigationController.navigationBar.barTintColor = UIApplication.shared.visibleViewController?.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
-        navigationController.navigationBar.isTranslucent = false
-        navigationController.navigationBar.overrideUserInterfaceStyle = .dark
-        navigationController.navigationBar.barStyle = .black
-        let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
-        UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
-        let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
-        navigationController.navigationBar.titleTextAttributes = textAttributes
-        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)
-        }
+    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
+        completionHandler([.alert, .sound, .badge])
     }
     
-    func openEditorGroup(opposite: String) {
-        let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "editorGroupVC") as! EditorGroup
-        editorGroupVC.hidesBottomBarWhenPushed = true
-        editorGroupVC.unique_l_pin = opposite
-        editorGroupVC.fromNotification = true
-        let navigationController = CustomNavigationController(rootViewController: editorGroupVC)
-        navigationController.modalPresentationStyle = .fullScreen
-        navigationController.navigationBar.tintColor = .white
-        navigationController.navigationBar.barTintColor = UIApplication.shared.visibleViewController?.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
-        navigationController.navigationBar.isTranslucent = false
-        navigationController.navigationBar.overrideUserInterfaceStyle = .dark
-        navigationController.navigationBar.barStyle = .black
-        let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
-        UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
-        let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
-        navigationController.navigationBar.titleTextAttributes = textAttributes
-        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)
-        }
+    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
+        APIS.openNotificationNexilis(response)
+        completionHandler()
     }
 }
 

+ 2 - 5
AppBuilder/AppBuilder/FirstTabViewController.swift

@@ -26,8 +26,7 @@ class FirstTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
     let audioEngine = AVAudioEngine()
     var alertController = LibAlertController()
     
-    var dateRefresh: Date?
-    public static var forceRefresh = false
+    public static var forceRefresh = true
     public static var atFirstPage = true
     public static var showModal = false
     
@@ -155,13 +154,11 @@ class FirstTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
         }
         if let u = myURL {
             self.webView.evaluateJavaScript("{window.localStorage.setItem('currentTab','\(ViewController.sURL)')}")
-            if ((dateRefresh == nil || Int(Date().timeIntervalSince(dateRefresh!)) >= 60) && FirstTabViewController.atFirstPage) || FirstTabViewController.forceRefresh {
-//                let myRequest = URLRequest(url: u)
+            if FirstTabViewController.forceRefresh {
                 loadURLWithCookie(url: u)
             } else {
                 self.webView.evaluateJavaScript("if(resumeAll){resumeAll();}")
             }
-            dateRefresh = Date()
             FirstTabViewController.forceRefresh = false
         }
         navigationController?.setNavigationBarHidden(true, animated: false)

+ 1 - 0
AppBuilder/AppBuilder/FourthTabViewController.swift

@@ -707,6 +707,7 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
                         if(!id.isEmpty){
 //                            Nexilis.changeUser(f_pin: id)
                             SecureUserDefaults.shared.set(id, forKey: "me")
+                            APIS.sendPushToken(Utils.getTokenAPN(), isResend: true)
                             Utils.setProfile(value: false)
                             if Utils.getForceAnonymous() {
                                 self.deleteAllRecordDatabase()

+ 3 - 14
AppBuilder/AppBuilder/Info.plist

@@ -114,7 +114,7 @@
 		<string>ms-outlook</string>
 		<string>readdle-spark</string>
 		<string>ymail</string>
-        <string>comgooglemaps</string>
+		<string>comgooglemaps</string>
 	</array>
 	<key>LSRequiresIPhoneOS</key>
 	<true/>
@@ -147,16 +147,6 @@
 	<array>
 		<string>INSendMessageIntent</string>
 	</array>
-<!--	<key>UIAppFonts</key>-->
-<!--	<array>-->
-<!--		<string>Poppins-Light.ttf</string>-->
-<!--		<string>Poppins-Medium.ttf</string>-->
-<!--		<string>Poppins-SemiBoldItalic.ttf</string>-->
-<!--		<string>Poppins-Regular.ttf</string>-->
-<!--		<string>Poppins-LightItalic.ttf</string>-->
-<!--		<string>Poppins-SemiBold.ttf</string>-->
-<!--		<string>Poppins-MediumItalic.ttf</string>-->
-<!--	</array>-->
 	<key>UIApplicationSceneManifest</key>
 	<dict>
 		<key>UIApplicationSupportsMultipleScenes</key>
@@ -183,7 +173,6 @@
 		<string>audio</string>
 		<string>bluetooth-central</string>
 		<string>bluetooth-peripheral</string>
-		<string>external-accessory</string>
 		<string>fetch</string>
 		<string>processing</string>
 		<string>remote-notification</string>
@@ -208,9 +197,9 @@
 		<string>UIInterfaceOrientationLandscapeLeft</string>
 		<string>UIInterfaceOrientationLandscapeRight</string>
 	</array>
+	<key>UIUserInterfaceStyle</key>
+	<string>Light</string>
 	<key>UIViewControllerBasedStatusBarAppearance</key>
 	<true/>
-    <key>UIUserInterfaceStyle</key>
-    <string>Light</string>
 </dict>
 </plist>

+ 1 - 0
AppBuilder/AppBuilder/SceneDelegate.swift

@@ -100,6 +100,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
         // Called as the scene transitions from the foreground to the background.
         // Use this method to save data, release shared resources, and store enough scene-specific state information
         // to restore the scene back to its current state.
+        exit(0)
     }
 
 

+ 26 - 23
AppBuilder/AppBuilder/SecondTabViewController.swift

@@ -552,29 +552,6 @@ class SecondTabViewController: UIViewController, UIScrollViewDelegate, UIGesture
             }
         })
         getData()
-        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()
-                    }
-                }
-            })
-        }
     }
     
     override func viewWillAppear(_ animated: Bool) {
@@ -896,6 +873,29 @@ class SecondTabViewController: UIViewController, UIScrollViewDelegate, UIGesture
             Utils.inTabChats = false
 //            searchController.searchBar.placeholder = "Search groups name".localized()
 //            searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Search groups name".localized(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)])
+            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()
+                        }
+                    }
+                })
+            }
         default:
             Utils.inTabChats = true
 //            searchController.searchBar.placeholder = "Search chats & messages".localized()
@@ -1010,6 +1010,9 @@ class SecondTabViewController: UIViewController, UIScrollViewDelegate, UIGesture
     }
     
     private func getOpenGroups(listGroups: [Group], completion: @escaping ([Group]) -> ()) {
+        while Nexilis.isProcessWriteSync {
+            Thread.sleep(forTimeInterval: 0.5)
+        }
         if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getOpenGroups(p_account: "1,2,3,6,5,7", offset: "0", search: "")) {
             var dataGroups: [Group] = []
             if (response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "00") {

+ 2 - 6
AppBuilder/AppBuilder/ThirdTabViewController.swift

@@ -26,8 +26,7 @@ class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
     let audioEngine = AVAudioEngine()
     var alertController = LibAlertController()
     
-    var dateRefresh: Date?
-    public static var forceRefresh = false
+    public static var forceRefresh = true
     public static var inView = false
     public static var atFirstPage = true
     public static var showModal = false
@@ -164,14 +163,11 @@ class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
         //print(address)
         if let u = myURL{
             self.webView.evaluateJavaScript("{window.localStorage.setItem('currentTab','\(ViewController.tab3)')}")
-            if ((dateRefresh == nil || Int(Date().timeIntervalSince(dateRefresh!)) >= 60) && ThirdTabViewController.atFirstPage) || ThirdTabViewController.forceRefresh {
-//                let myRequest = URLRequest(url: u)
-//                let myRequest = URLRequest(url: u)
+            if ThirdTabViewController.forceRefresh {
                 loadURLWithCookie(url: u)
             } else {
                 self.webView.evaluateJavaScript("if(resumeAll){resumeAll();}")
             }
-            dateRefresh = Date()
             ThirdTabViewController.forceRefresh = false
         }
         navigationController?.setNavigationBarHidden(true, animated: false)

+ 8 - 2
AppBuilder/AppBuilder/ViewController.swift

@@ -240,13 +240,14 @@ 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)"
             if let cachedImage = ImageCache.shared.image(forKey: fullUrl) {
                 DispatchQueue.main.async() { [self] in
                     imageIndicator = cachedImage
-                    addCustomViewAboveTabBarItem(at: 0, image: imageIndicator)
+                    addCustomViewAboveTabBarItem(at: Utils.getLastTabSelected(), image: imageIndicator)
                 }
             } else {
                 Utils.fetchDataWithCookiesAndUserAgent(from: URL(string: fullUrl)!) { data, response, error in
@@ -255,7 +256,7 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
                     DispatchQueue.main.async() { [self] in
                         if UIImage(data: data) != nil {
                             imageIndicator = UIImage(data: data)!
-                            addCustomViewAboveTabBarItem(at: 0, image: imageIndicator)
+                            addCustomViewAboveTabBarItem(at: Utils.getLastTabSelected(), image: imageIndicator)
                             ImageCache.shared.save(image: UIImage(data: data)!, forKey: fullUrl)
                         }
                     }
@@ -368,6 +369,7 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
                 cursor.close()
             }
         })
+        UIApplication.shared.applicationIconBadgeNumber = Int(counter ?? 0)
         return counter ?? 0
     }
 
@@ -607,6 +609,7 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
                 indicatorImage.removeFromSuperview()
                 addCustomViewAboveTabBarItem(at: selectedIndex, image: imageIndicator)
             }
+            Utils.setLastTabSelected(value: selectedIndex)
         }
         if viewController != secondTab {
             let idxTabChat = self.viewControllers?.firstIndex(where: {$0 == secondTab})
@@ -1145,6 +1148,9 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
                     }
                     return
                 }
+                while Nexilis.isProcessWriteSync {
+                    Thread.sleep(forTimeInterval: 0.5)
+                }
                 if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.pullFloatingButton(), timeout: 30 * 1000) {
                     if response.isOk() {
                         let data = response.getBody(key: CoreMessage_TMessageKey.DATA, default_value: "")

二进制
NexilisLite/.DS_Store


+ 373 - 0
NexilisLite/NexilisLite/Source/APIS.swift

@@ -13,6 +13,7 @@ import Toast_Swift
 import nuSDKService
 import AVFoundation
 import AVKit
+import Intents
 
 public class APIS: NSObject {
     public static func connect(appName: String, apiKey: String, delegate: ConnectDelegate, showButton: Bool = true, fromMAB: Bool = false) {
@@ -802,6 +803,378 @@ public class APIS: NSObject {
         Nexilis.openmailAction()
     }
     
+    public static func sendPushToken(_ token: String, isResend: Bool = false) {
+        DispatchQueue.global().async{
+            var isResend = isResend
+            if Utils.getTokenAPN().isEmpty || token != Utils.getTokenAPN() || isResend {
+                Utils.setTokenAPN(value: token)
+                isResend = true
+            }
+            if isResend {
+                _ = Nexilis.write(message: CoreMessage_TMessageBank.getToken(token: token))
+            }
+        }
+    }
+    
+    public static var uuidCall: UUID?
+    public static var fpinCall: String?
+    public static func showNotificationNexilis(_ userInfo: [AnyHashable : Any]) {
+//        let center = UNUserNotificationCenter.current()
+//        let content = UNMutableNotificationContent()
+//        content.title = "showNotificationNexilis"
+//        content.body = ""
+//        content.sound = .default
+//        content.userInfo = userInfo
+//        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
+//        let request = UNNotificationRequest(identifier: "HJK", content: content, trigger: trigger)
+//        center.add(request) { error in
+//            if let error = error {
+//                print("Error scheduling notification: \(error.localizedDescription)")
+//            }
+//        }
+        if checkAppStateisBackground() {
+            print("showNotificationNexilis1: \(userInfo)")
+            if let payload = userInfo["payload"] as? [String: Any] {
+                if let messagePayload = payload["message"] as? [String: Any] {
+                    if let data = messagePayload["data"] as? [String: Any] {
+                        let code = data["nx_code"] as? String ?? ""
+                        if code == "CL01" {
+                            if let message = data["bodies"] as? [String: Any] {
+                                var text = message[CoreMessage_TMessageKey.MESSAGE_TEXT] as? String ?? ""
+                                let nameUser = message[CoreMessage_TMessageKey.F_DISPLAY_NAME] as? String ?? ""
+                                let threadIdentifier = message[CoreMessage_TMessageKey.OPPOSITE_PIN] as? String ?? ""
+                                var nameSubtitle = ""
+                                let imageId = CoreMessage_TMessageKey.IMAGE_ID
+                                let videoId = CoreMessage_TMessageKey.VIDEO_ID
+                                let fileId = CoreMessage_TMessageKey.FILE_ID
+                                let audioId = CoreMessage_TMessageKey.AUDIO_ID
+                                let attachmentFlag = CoreMessage_TMessageKey.ATTACHMENT_FLAG
+                                let messageScopeId = CoreMessage_TMessageKey.MESSAGE_SCOPE_ID
+                                let credential = CoreMessage_TMessageKey.CREDENTIAL
+                                let gif_id = CoreMessage_TMessageKey.GIF_ID
+                                let is_secret = CoreMessage_TMessageKey.IS_SECRET
+                                if (message[is_secret] as? String ?? "") == "1" {
+                                  text = "You got messages..."
+                                } else if (message[gif_id] as? String ?? "") != "" {
+                                  text = "Sent GIF 🎬"
+                                } else if !(message[imageId] as? String ?? "").isEmpty {
+                                    text = "Sent Image 📷"
+                                } else if (message[attachmentFlag] as? String ?? "") == "11" {
+                                    text = "Sent Sticker ❤️"
+                                } else if !(message[videoId] as? String ?? "").isEmpty {
+                                    text = "Sent Video 📹"
+                                } else if !(message[fileId] as? String ?? "").isEmpty {
+                                    if (message[messageScopeId] as? String ?? "") == "18" {
+                                        text = "Sent Form 📄"
+                                    } else {
+                                        text = "Sent File 📄"
+                                    }
+                                } else if !(message[audioId] as? String ?? "").isEmpty {
+                                    text = "Sent Audio ♫"
+                                } else if text.contains("Share%20location%20") {
+                                    text = "Sent Location 📌"
+                                } else if (message[attachmentFlag] as? String ?? "") == "27" {
+                                    text = "Sent Live Streaming"
+                                } else if (message[attachmentFlag] as? String ?? "") == "26" {
+                                    text = "Sent Seminar"
+                                } else if (message[attachmentFlag] as? String ?? "") == "25" {
+                                    text = "Sent Video Conference Room"
+                                } else if (message[attachmentFlag] as? String ?? "") == "24" {
+                                    text = "Sent Quiz"
+                                } else if (message[credential] as? String ?? "") == "1" {
+                                    text = "Sent Confidential Message"
+                                }
+                                copySoundToLocalPath()
+                                let center = UNUserNotificationCenter.current()
+                                let content = UNMutableNotificationContent()
+                                content.title = nameUser
+                                content.body = text
+                                var type = "1"
+                                if (message[messageScopeId] as? String ?? "") == "3" || (message[messageScopeId] as? String ?? "") == "18" || (message[messageScopeId] as? String ?? "") == "5"{
+                                    type = "0"
+                                }
+                                var nameTopic = "Lounge".localized()
+                                var idGroup = ""
+                                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                                    do {
+                                        if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "SELECT title, group_id FROM DISCUSSION_FORUM WHERE chat_id='\(threadIdentifier)'"), cursor.next() {
+                                            nameTopic = cursor.string(forColumnIndex: 0) ?? ""
+                                            idGroup = cursor.string(forColumnIndex: 1) ?? ""
+                                            cursor.close()
+                                        }
+                                        if idGroup.isEmpty {
+                                            idGroup = threadIdentifier
+                                        }
+                                        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))"
+                                            cursorGroup.close()
+                                        }
+                                    } catch {
+                                        rollback.pointee = true
+                                        print("Access database error: \(error.localizedDescription)")
+                                    }
+                                })
+                                if type == "1" {
+                                    content.subtitle = nameSubtitle
+                                }
+                                content.userInfo = ["id" : threadIdentifier, "type" : type]
+                                content.sound = UNNotificationSound(named: UNNotificationSoundName("pb_call_in.mp3"))
+                                let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
+                                let request = UNNotificationRequest(identifier: threadIdentifier, content: content, trigger: trigger)
+                                center.add(request) { error in
+                                    if let error = error {
+                                        print("Error scheduling notification: \(error.localizedDescription)")
+                                    }
+                                }
+                            }
+                        } else if code == "CL03" {
+                            let callFromName = data["call-from-name"] as? String ?? ""
+                            let callFrom = data["call-from"] as? String ?? ""
+                            let callType = data["call-type"] as? String ?? ""
+                            uuidCall = UUID()
+                            fpinCall = callFrom
+                            Nexilis.callAPNActivated = true
+//                                    CallManager.shared.reportIncomingCall(uuid: uuidCall!, callerName: callFromName, callerId: callFrom, isVideo: callType != "1") { error in
+//                                        if let error = error {
+//                                            print("Error reporting incoming call: \(error.localizedDescription)")
+//                                        } else {
+//                                            print("Incoming call reported successfully")
+//                                        }
+//                                    }
+                            copySoundToLocalPath()
+                            let center = UNUserNotificationCenter.current()
+                            let content = UNMutableNotificationContent()
+                            content.title = callFromName
+                            if callType == "1" {
+                                content.body = "Incoming Audio Call".localized()
+                            } else {
+                                content.body = "Incoming Video Call".localized()
+                            }
+                            content.userInfo = ["id" : callFrom, "type" : code, "callType": callType]
+                            content.sound = UNNotificationSound(named: UNNotificationSoundName("pb_call_in.mp3"))
+                            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
+                            let request = UNNotificationRequest(identifier: "\(uuidCall!)", content: content, trigger: trigger)
+                            center.add(request) { error in
+                                if let error = error {
+                                    print("Error scheduling notification: \(error.localizedDescription)")
+                                }
+                            }
+                        } else if code == "CL02" {
+                            if let uuidCall = uuidCall {
+                                Nexilis.callAPNActivated = false
+                                let center = UNUserNotificationCenter.current()
+                                center.removePendingNotificationRequests(withIdentifiers: ["\(uuidCall)"])
+                                center.removeDeliveredNotifications(withIdentifiers: ["\(uuidCall)"])
+//                                    CallManager.shared.endCall(with: uuidCall)
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    private static func copySoundToLocalPath() {
+        guard let sourceURL = Bundle.resourceBundle(for: Nexilis.self).url(forResource: "pb_call_in", withExtension: "mp3") else {
+            return
+        }
+
+        // Define the destination path in the Library/Sounds directory
+        let fileManager = FileManager.default
+        let soundDirectory = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!.appendingPathComponent("Sounds", isDirectory: true)
+
+        // Ensure the Sounds directory exists
+        if !fileManager.fileExists(atPath: soundDirectory.path) {
+            do {
+                try fileManager.createDirectory(at: soundDirectory, withIntermediateDirectories: true, attributes: nil)
+            } catch {
+                print("Error creating Sounds directory: \(error)")
+                return
+            }
+        }
+
+        let destinationURL = soundDirectory.appendingPathComponent("pb_call_in.mp3")
+        if !fileManager.fileExists(atPath: destinationURL.path) {
+            do {
+                try fileManager.copyItem(at: sourceURL, to: destinationURL)
+            } catch {
+                
+            }
+        }
+    }
+    
+    public static func openNotificationNexilis(_ response: UNNotificationResponse) {
+        DispatchQueue.main.async{
+            if let userInfo = response.notification.request.content.userInfo as? [String: String] {
+                let id = userInfo["id"] ?? ""
+                let type = userInfo["type"] ?? ""
+                if type == "0" {
+                    if let user = User.getData(pin: id), user.firstName == "User".localized() {
+                        return
+                    }
+                    let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
+                    editorPersonalVC.hidesBottomBarWhenPushed = true
+                    editorPersonalVC.unique_l_pin = id
+                    editorPersonalVC.fromNotification = true
+                    let navigationController = CustomNavigationController(rootViewController: editorPersonalVC)
+                    navigationController.modalPresentationStyle = .fullScreen
+                    navigationController.navigationBar.tintColor = .white
+                    navigationController.navigationBar.barTintColor = UIApplication.shared.visibleViewController?.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
+                    navigationController.navigationBar.isTranslucent = false
+                    navigationController.navigationBar.overrideUserInterfaceStyle = .dark
+                    navigationController.navigationBar.barStyle = .black
+                    let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
+                    UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
+                    let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
+                    navigationController.navigationBar.titleTextAttributes = textAttributes
+                    if UIApplication.shared.visibleViewController?.navigationController != nil {
+                        if UIApplication.shared.visibleViewController?.navigationController?.presentedViewController == editorPersonalVC {
+                            return
+                        }
+                        UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+                    } else {
+                        if UIApplication.shared.visibleViewController == editorPersonalVC {
+                            return
+                        }
+                        UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+                    }
+                } else if type == "1" {
+                    var groupExist = false
+                    Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                        var idGroup = ""
+                        if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "SELECT title, group_id FROM DISCUSSION_FORUM WHERE chat_id='\(id)'"), cursor.next() {
+                            groupExist = true
+                            cursor.close()
+                        } else {
+                            if idGroup.isEmpty {
+                                idGroup = id
+                            }
+                            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) ?? ""
+                                groupExist = true
+                                cursorGroup.close()
+                            }
+                        }
+                    })
+                    if !groupExist {
+                        return
+                    }
+                    let editorGroupVC = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "editorGroupVC") as! EditorGroup
+                    editorGroupVC.hidesBottomBarWhenPushed = true
+                    editorGroupVC.unique_l_pin = id
+                    editorGroupVC.fromNotification = true
+                    let navigationController = CustomNavigationController(rootViewController: editorGroupVC)
+                    navigationController.modalPresentationStyle = .fullScreen
+                    navigationController.navigationBar.tintColor = .white
+                    navigationController.navigationBar.barTintColor = UIApplication.shared.visibleViewController?.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .mainColor
+                    navigationController.navigationBar.isTranslucent = false
+                    navigationController.navigationBar.overrideUserInterfaceStyle = .dark
+                    navigationController.navigationBar.barStyle = .black
+                    let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
+                    UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
+                    let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
+                    navigationController.navigationBar.titleTextAttributes = textAttributes
+                    if UIApplication.shared.visibleViewController?.navigationController != nil {
+                        if UIApplication.shared.visibleViewController?.navigationController?.presentedViewController == editorGroupVC {
+                            return
+                        }
+                        UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+                    } else {
+                        if UIApplication.shared.visibleViewController == editorGroupVC {
+                            return
+                        }
+                        UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+                    }
+                } else if type == "CL03" {
+                    let callType = userInfo["callType"] ?? ""
+                    if callType == "1" {
+                        if let user = User.getData(pin: id), user.firstName == "User".localized() {
+                            return
+                        }
+                        let controller = QmeraAudioViewController()
+                        controller.isOutgoing = false
+                        controller.user = User.getData(pin: id)
+                        controller.autoAcceptAPN = true
+                        controller.modalPresentationStyle = .overCurrentContext
+                        if UIApplication.shared.visibleViewController is UIAlertController {
+                            let vc = UIApplication.shared.visibleViewController as! UIAlertController
+                            vc.dismiss(animated: true, completion: {
+                                if UIApplication.shared.visibleViewController?.navigationController != nil {
+                                    UIApplication.shared.visibleViewController?.navigationController?.present(controller, animated: true, completion: nil)
+                                } else {
+                                    UIApplication.shared.visibleViewController?.present(controller, animated: true, completion: nil)
+                                }
+                            })
+                            return
+                        }
+                        if UIApplication.shared.visibleViewController?.navigationController != nil {
+                            UIApplication.shared.visibleViewController?.navigationController?.present(controller, animated: true, completion: nil)
+                        } else {
+                            UIApplication.shared.visibleViewController?.present(controller, animated: true, completion: nil)
+                        }
+                    } else {
+                        if let user = User.getData(pin: id), user.firstName == "User".localized() {
+                            return
+                        }
+                        let videoController = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "videoVCQmera") as! QmeraVideoViewController
+                        videoController.fPin = id
+                        videoController.isInisiator = false
+                        videoController.autoAcceptAPN = true
+                        let navigationController = CustomNavigationController(rootViewController: videoController)
+                        navigationController.modalPresentationStyle = .fullScreen
+                        if UIApplication.shared.visibleViewController is UIAlertController {
+                            let vc = UIApplication.shared.visibleViewController as! UIAlertController
+                            vc.dismiss(animated: true, completion: {
+                                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)
+                                }
+                            })
+                            return
+                        }
+                        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)
+                        }
+                    }
+                }
+            }
+        }
+//        UNUserNotificationCenter.current().removeAllDeliveredNotifications()
+    }
+    
+    public static func checkAppStateisBackground() -> Bool {
+        let state = UIApplication.shared.applicationState
+        
+        switch state {
+        case .active:
+            return false
+        case .inactive:
+            return true
+        case .background:
+            return true
+        @unknown default:
+            return false
+        }
+    }
+    
+    public static func enterBackground() {
+        do {
+            try API.switchCBI(cbiI: Callback(), bLight: true)
+        } catch {
+        }
+    }
+    
+    public static func enterForeground() {
+        do {
+            try API.switchCBI(cbiI: Callback(), bLight: false)
+        } catch {
+        }
+    }
+    
     public static func setCheckEmulator(isActive: Bool) {
 //        Utils.bCheckEmulator = isActive
     }

+ 46 - 0
NexilisLite/NexilisLite/Source/CoreMessage_TMessageBank.swift

@@ -35,6 +35,7 @@ public class CoreMessage_TMessageBank {
         tmessage.mBodies[CoreMessage_TMessageKey.API] = api
         tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_APP_NAME] = APIS.getAppNm()
         tmessage.mBodies[CoreMessage_TMessageKey.CPAAS_VERSION] = Utils.CPAAS_VERSION
+        tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_PACKAGE_NAME] = (Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String) ?? ""
         tmessage.mPIN = p_pin
         return tmessage
     }
@@ -48,6 +49,7 @@ public class CoreMessage_TMessageBank {
         tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_ID] = Utils.M_USER_ANDROID_ID
         tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_APP_NAME] = APIS.getAppNm()
         tmessage.mBodies[CoreMessage_TMessageKey.CPAAS_VERSION] = Utils.CPAAS_VERSION
+        tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_PACKAGE_NAME] = (Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String) ?? ""
 //        tmessage.mBodies[CoreMessage_TMessageKey.BUSINESS_ENTITY] = "74"
         return tmessage
     }
@@ -1416,6 +1418,7 @@ public class CoreMessage_TMessageBank {
         tmessage.mBodies[CoreMessage_TMessageKey.EMAIL] = p_email
         tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_APP_NAME] = APIS.getAppNm()
         tmessage.mBodies[CoreMessage_TMessageKey.CPAAS_VERSION] = Utils.CPAAS_VERSION
+        tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_PACKAGE_NAME] = (Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String) ?? ""
         return tmessage
     }
 
@@ -2299,6 +2302,7 @@ public class CoreMessage_TMessageBank {
         tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_ID] = Utils.M_USER_ANDROID_ID
         tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_APP_NAME] = APIS.getAppNm()
         tmessage.mBodies[CoreMessage_TMessageKey.CPAAS_VERSION] = Utils.CPAAS_VERSION
+        tmessage.mBodies[CoreMessage_TMessageKey.ANDROID_PACKAGE_NAME] = (Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String) ?? ""
         return tmessage
     }
     
@@ -2599,4 +2603,46 @@ public class CoreMessage_TMessageBank {
         return tMessage
     }
     
+    public static func getToken(token: String) -> TMessage {
+        let tMessage = NexilisLite.TMessage()
+        let me = User.getMyPin() ?? ""
+        tMessage.mPIN = me
+        tMessage.mCode = CoreMessage_TMessageCode.APN_TOKEN
+        tMessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tMessage.mBodies[CoreMessage_TMessageKey.TOKEN] = token
+        tMessage.mBodies[CoreMessage_TMessageKey.DEVICE_BRAND] = "iOS"
+        tMessage.mBodies[CoreMessage_TMessageKey.ANDROID_ID] = Utils.M_USER_ANDROID_ID
+        return tMessage
+    }
+    
+    public static func getMessageById(messageId: String) -> TMessage {
+        let tMessage = NexilisLite.TMessage()
+        let me = User.getMyPin() ?? ""
+        tMessage.mPIN = me
+        tMessage.mCode = CoreMessage_TMessageCode.GET_MESSAGE_BY_ID
+        tMessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tMessage.mBodies[CoreMessage_TMessageKey.MESSAGE_ID] = messageId
+        return tMessage
+    }
+    
+    public static func getAckMessage(messageId: String) -> TMessage {
+        let tMessage = NexilisLite.TMessage()
+        let me = User.getMyPin() ?? ""
+        tMessage.mPIN = me
+        tMessage.mCode = CoreMessage_TMessageCode.ACK_MESSAGE_BY_ID
+        tMessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tMessage.mBodies[CoreMessage_TMessageKey.MESSAGE_ID] = messageId
+        return tMessage
+    }
+    
+    public static func getCallStatus(lPin: String) -> TMessage {
+        let tMessage = NexilisLite.TMessage()
+        let me = User.getMyPin() ?? ""
+        tMessage.mPIN = me
+        tMessage.mCode = CoreMessage_TMessageCode.IS_CALLING
+        tMessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tMessage.mBodies[CoreMessage_TMessageKey.L_PIN] = lPin
+        return tMessage
+    }
+    
 }

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

@@ -783,4 +783,19 @@ public class CoreMessage_TMessageCode {
     
     public static let RESET_SUPER_APP = "RSA";
     public static let GPT = "GPT";
+    
+    public static let MUTE_AC_VC = "MAV";
+    public static let LS_TARGET = "LST";
+    public static let GET_MESSAGE_BY_ID = "GMID";
+    public static let SECURITY_SHIELD_LOGGING = "SSG";
+
+    public static let IS_CALLING = "ICA";
+    public static let ACK_PUSH_CC = "ACC";
+    public static let ACK_MESSAGE_BY_ID = "AMID";
+    public static let MOBILE_ACTIVITY_REPORT = "MAR";
+    public static let CHECK_OFFLINE = "COF";
+    public static let GET_SS_CONTENT = "SS02";
+    public static let ACCEPT_REJECT_MEETING = "MTG";
+    public static let GET_CHATBOT_SCHEDULE = "CHS";
+    public static let GPT_SERVICE = "GPTS";
 }

+ 1 - 1
NexilisLite/NexilisLite/Source/Database.swift

@@ -30,7 +30,7 @@ public class Database {
         }
         
         do {
-            try FileManager.default.setAttributes([.protectionKey: FileProtectionType.complete], ofItemAtPath: databasePath)
+            try FileManager.default.setAttributes([.protectionKey: FileProtectionType.none], ofItemAtPath: databasePath)
 //            print("File protection attribute set to 'complete'.")
         } catch {
 //            print("Error setting file protection attribute: \(error)")

+ 38 - 28
NexilisLite/NexilisLite/Source/FloatingButton/FloatingButton.swift

@@ -360,21 +360,23 @@ public class FloatingButton: UIView, UIGestureRecognizerDelegate {
                         let newButton = UIButton()
                         newButton.heightAnchor.constraint(equalToConstant: defaultWidthHeightMenuFB).isActive = true
                         newButton.translatesAutoresizingMaskIntoConstraints = false
-                        DispatchQueue.global().async {
-                            let urlString = Utils.getURLBase() + "get_file_from_path?img=" + icon
-                            if let cachedImage = ImageCache.shared.image(forKey: urlString) {
-                                DispatchQueue.main.async() {
-                                    newButton.setImage(cachedImage, for: .normal)
+                        if !APIS.checkAppStateisBackground() {
+                            DispatchQueue.global().async {
+                                let urlString = Utils.getURLBase() + "get_file_from_path?img=" + icon
+                                if let cachedImage = ImageCache.shared.image(forKey: urlString) {
+                                    DispatchQueue.main.async() {
+                                        newButton.setImage(cachedImage, for: .normal)
+                                    }
+                                    return
                                 }
-                                return
-                            }
-                            Utils.fetchDataWithCookiesAndUserAgent(from: URL(string: urlString)!) { data, response, error in
-                                guard let data = data, error == nil else { return }
-                                // always update the UI from the main thread
-                                DispatchQueue.main.async() {
-                                    if let image = UIImage(data: data) {
-                                        newButton.setImage(image, for: .normal)
-                                        ImageCache.shared.save(image: UIImage(data: data)!, forKey: urlString)
+                                Utils.fetchDataWithCookiesAndUserAgent(from: URL(string: urlString)!) { data, response, error in
+                                    guard let data = data, error == nil else { return }
+                                    // always update the UI from the main thread
+                                    DispatchQueue.main.async() {
+                                        if let image = UIImage(data: data) {
+                                            newButton.setImage(image, for: .normal)
+                                            ImageCache.shared.save(image: UIImage(data: data)!, forKey: urlString)
+                                        }
                                     }
                                 }
                             }
@@ -391,6 +393,9 @@ public class FloatingButton: UIView, UIGestureRecognizerDelegate {
                 if !Utils.getHistoryPullFB().isEmpty {
                     setFBFromPull()
                 }
+                while Nexilis.isProcessWriteSync {
+                    Thread.sleep(forTimeInterval: 0.5)
+                }
                 if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.pullFloatingButton(), timeout: 30 * 1000){
                     if response.isOk() {
                         Utils.setHistoryPullFB(value: response.getBody(key: CoreMessage_TMessageKey.DATA, default_value: ""))
@@ -464,21 +469,23 @@ public class FloatingButton: UIView, UIGestureRecognizerDelegate {
                                     newButton.setImage(UIImage(named: mode == MODE_HORIZONTAL_SIDE_TAB ? "pb_button_hrz_more" : mode == MODE_HORIZONTAL_ANIMATION ? "pb_button_hrz_anim_more" : "pb_button_others", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), for: .normal)
                                 }
                                 if !icon.isEmpty {
-                                    DispatchQueue.global().async {
-                                        let urlString = Utils.getURLBase() + "get_file_from_path?img=" + icon
-                                        if let cachedImage = ImageCache.shared.image(forKey: urlString) {
-                                            DispatchQueue.main.async() {
-                                                newButton.setImage(cachedImage, for: .normal)
+                                    if !APIS.checkAppStateisBackground() {
+                                        DispatchQueue.global().async {
+                                            let urlString = Utils.getURLBase() + "get_file_from_path?img=" + icon
+                                            if let cachedImage = ImageCache.shared.image(forKey: urlString) {
+                                                DispatchQueue.main.async() {
+                                                    newButton.setImage(cachedImage, for: .normal)
+                                                }
+                                                return
                                             }
-                                            return
-                                        }
-                                        Utils.fetchDataWithCookiesAndUserAgent(from: URL(string: urlString)!) { data, response, error in
-                                            guard let data = data, error == nil else { return }
-                                            // always update the UI from the main thread
-                                            DispatchQueue.main.async() {
-                                                if let image = UIImage(data: data) {
-                                                    newButton.setImage(image, for: .normal)
-                                                    ImageCache.shared.save(image: UIImage(data: data)!, forKey: urlString)
+                                            Utils.fetchDataWithCookiesAndUserAgent(from: URL(string: urlString)!) { data, response, error in
+                                                guard let data = data, error == nil else { return }
+                                                // always update the UI from the main thread
+                                                DispatchQueue.main.async() {
+                                                    if let image = UIImage(data: data) {
+                                                        newButton.setImage(image, for: .normal)
+                                                        ImageCache.shared.save(image: UIImage(data: data)!, forKey: urlString)
+                                                    }
                                                 }
                                             }
                                         }
@@ -661,6 +668,9 @@ public class FloatingButton: UIView, UIGestureRecognizerDelegate {
                 print("Access database error: \(error.localizedDescription)")
             }
         })
+        if !Nexilis.fromMAB {
+            UIApplication.shared.applicationIconBadgeNumber = Int(counter ?? 0)
+        }
         return counter ?? 0
     }
     

+ 29 - 8
NexilisLite/NexilisLite/Source/IncomingThread.swift

@@ -240,6 +240,7 @@ class IncomingThread {
                 if(!id.isEmpty){
 //                            Nexilis.changeUser(f_pin: id)
                     SecureUserDefaults.shared.set(id, forKey: "me")
+                    APIS.sendPushToken(Utils.getTokenAPN(), isResend: true)
                     Utils.setProfile(value: false)
                     if Utils.getForceAnonymous() {
                         viewController?.deleteAllRecordDatabase()
@@ -1263,25 +1264,34 @@ class IncomingThread {
         }
     }
     
-    private func receiveMessage(message: TMessage) -> Void {
+    public func receiveMessage(message: TMessage, withoutACK: Bool = false) -> Void {
         let message_id = message.getBody(key: CoreMessage_TMessageKey.MESSAGE_ID)
         guard let _: String = SecureUserDefaults.shared.value(forKey: "status") else {
             //print("App not ready!!! skip receive message \(message_id)")
-            ack(message: message)
+            if !withoutACK {
+                ack(message: message)
+            }
             return
         }
+        var messageExist = false
         Database.shared.database?.inTransaction({ (fmdb, rollback) in
             do {
                 if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select message_id from MESSAGE where message_id = '\(message_id)'"), cursor.next() {
-                    ack(message: message)
+                    if !withoutACK {
+                        ack(message: message)
+                    }
                     cursor.close()
-                    return
+                    messageExist = true
+                    print("MASUK RETURN MESSAGE EXIST")
                 }
             } catch {
                 rollback.pointee = true
                 print("Access database error: \(error.localizedDescription)")
             }
         })
+        if messageExist {
+            return
+        }
         let media = message.getMedia()
         //print("MEDIA \(media)");
         let thumb_id = message.getBody(key: CoreMessage_TMessageKey.THUMB_ID)
@@ -1308,12 +1318,16 @@ class IncomingThread {
                     //print("save message incoming")
                 }
             }
-            ack(message: message)
+            if !withoutACK {
+                ack(message: message)
+            }
             return
         }
         Nexilis.saveMessage(message: message, withStatus: false)
         //print("save message incoming")
-        ack(message: message)
+        if !withoutACK {
+            ack(message: message)
+        }
     }
     
     private func receiveMessageStatus(message: TMessage) -> Void {
@@ -1422,7 +1436,10 @@ class IncomingThread {
                     }
                     let device_id: String = SecureUserDefaults.shared.value(forKey: "device_id") ?? ""
                     if !device_id.isEmpty, let cursorUser = Database.shared.getRecords(fmdb: fmdb, query: "SELECT f_pin FROM BUDDY where device_id='\(device_id)'"), cursorUser.next() {
-                        SecureUserDefaults.shared.set(cursorUser.string(forColumnIndex: 0), forKey: "me")
+                        if User.getMyPin() != cursorUser.string(forColumnIndex: 0) {
+                            SecureUserDefaults.shared.set(cursorUser.string(forColumnIndex: 0), forKey: "me")
+                            APIS.sendPushToken(Utils.getTokenAPN(), isResend: true)
+                        }
                         cursorUser.close()
                     }
                     if let delegate = Nexilis.shared.personInfoDelegate {
@@ -1529,7 +1546,10 @@ class IncomingThread {
                 ], replace: true)
                 let device_id: String = SecureUserDefaults.shared.value(forKey: "device_id") ?? ""
                 if !device_id.isEmpty, let cursorUser = Database.shared.getRecords(fmdb: fmdb, query: "SELECT f_pin FROM BUDDY where device_id='\(device_id)'"), cursorUser.next() {
-                    SecureUserDefaults.shared.set(cursorUser.string(forColumnIndex: 0), forKey: "me")
+                    if User.getMyPin() != cursorUser.string(forColumnIndex: 0) {
+                        SecureUserDefaults.shared.set(cursorUser.string(forColumnIndex: 0), forKey: "me")
+                        APIS.sendPushToken(Utils.getTokenAPN(), isResend: true)
+                    }
                     cursorUser.close()
                 }
                 ack(message: message)
@@ -2031,6 +2051,7 @@ class IncomingThread {
         if (message.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "00") == "00") {
             let f_pin = message.getBody(key: CoreMessage_TMessageKey.F_PIN, default_value: "00")
             SecureUserDefaults.shared.set(f_pin, forKey: "me")
+            APIS.sendPushToken(Utils.getTokenAPN(), isResend: true)
             if let delegate = Nexilis.shared.loginDelegate {
                 delegate.onProcess(message: f_pin, status: message.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "00"))
             }

+ 9 - 6
NexilisLite/NexilisLite/Source/Model/Chat.swift

@@ -137,18 +137,18 @@ public class Chat: Model {
                 let query = """
                             select m.f_pin, \(!lastQuery.isEmpty ? "m.l_pin, m.message_id" : "ms.l_pin, ms.message_id"), \(!lastQuery.isEmpty ? "m.thumb_id," : "ms.counter,") m.message_text, m.server_date, m.image_id, m.video_id, m.file_id, m.attachment_flag, m.message_scope_id, b.first_name || ' ' || ifnull(b.last_name, '') name, b.image_id profile, b.official_account, m.status, m.credential, m.lock, m.audio_id, m.gif_id from MESSAGE_SUMMARY ms, MESSAGE m, BUDDY b where ms.l_pin = b.f_pin and \(!lastQuery.isEmpty ? lastQuery : "ms.message_id = m.message_id \(messageId.isEmpty ? "" : " and m.message_id = '\(messageId)'")") and m.is_call_center = 0
                             union
-                            select m.f_pin, \(!lastQuery.isEmpty ? "m.l_pin, m.message_id" : "ms.l_pin, ms.message_id"), \(!lastQuery.isEmpty ? "m.thumb_id," : "ms.counter,") m.message_text, m.server_date, m.image_id, m.video_id, m.file_id, m.attachment_flag, m.message_scope_id, 'Bot' name, '' profile, '', m.status, m.credential, m.lock, m.audio_id, m.gif_id from MESSAGE_SUMMARY ms, MESSAGE m where ms.l_pin = '-999' and \(!lastQuery.isEmpty ? lastQuery : "ms.message_id = m.message_id \(messageId.isEmpty ? "" : " and m.message_id = '\(messageId)'")") and m.is_call_center = 0
+                            select m.f_pin, ms.l_pin, ms.message_id, ms.counter, m.message_text, m.server_date, m.image_id, m.video_id, m.file_id, m.attachment_flag, m.message_scope_id, 'Bot' name, '' profile, '', m.status, m.credential, m.lock, m.audio_id, m.gif_id from MESSAGE_SUMMARY ms, MESSAGE m where ms.l_pin = '-999' and "ms.message_id = m.message_id \(messageId.isEmpty ? "" : " and m.message_id = '\(messageId)'")" and m.is_call_center = 0
                             union
-                            select m.f_pin, \(!lastQuery.isEmpty ? "m.l_pin, m.message_id" : "ms.l_pin, ms.message_id"), \(!lastQuery.isEmpty ? "m.thumb_id," : "ms.counter,") m.message_text, m.server_date, m.image_id, m.video_id, m.file_id, m.attachment_flag, m.message_scope_id, 'GPT SmartBot' name, '' profile, '', m.status, m.credential, m.lock, m.audio_id, m.gif_id from MESSAGE_SUMMARY ms, MESSAGE m where ms.l_pin = '-997' and \(!lastQuery.isEmpty ? lastQuery : "ms.message_id = m.message_id \(messageId.isEmpty ? "" : " and m.message_id = '\(messageId)'")") and m.is_call_center = 0
+                            select m.f_pin, ms.l_pin, ms.message_id, ms.counter, m.message_text, m.server_date, m.image_id, m.video_id, m.file_id, m.attachment_flag, m.message_scope_id, 'GPT SmartBot' name, '' profile, '', m.status, m.credential, m.lock, m.audio_id, m.gif_id from MESSAGE_SUMMARY ms, MESSAGE m where ms.l_pin = '-997' and "ms.message_id = m.message_id \(messageId.isEmpty ? "" : " and m.message_id = '\(messageId)'")" and m.is_call_center = 0
                             union
-                            select m.f_pin, \(!lastQuery.isEmpty ? "m.l_pin, m.message_id" : "ms.l_pin, ms.message_id"), \(!lastQuery.isEmpty ? "m.thumb_id," : "ms.counter,") m.message_text, m.server_date, m.image_id, m.video_id, m.file_id, m.attachment_flag, m.message_scope_id, b.f_name || ' (\("Lounge".localized()))', b.image_id profile, b.official, m.status, m.credential, m.lock, m.audio_id, m.gif_id from MESSAGE_SUMMARY ms, MESSAGE m, GROUPZ b where ms.l_pin = b.group_id and \(!lastQuery.isEmpty ? lastQuery : "ms.message_id = m.message_id \(messageId.isEmpty ? "" : " and m.message_id = '\(messageId)'")") and m.is_call_center = 0
+                            select m.f_pin, ms.l_pin, ms.message_id, ms.counter, m.message_text, m.server_date, m.image_id, m.video_id, m.file_id, m.attachment_flag, m.message_scope_id, b.f_name || ' (\("Lounge".localized()))', b.image_id profile, b.official, m.status, m.credential, m.lock, m.audio_id, m.gif_id from MESSAGE_SUMMARY ms, MESSAGE m, GROUPZ b where ms.l_pin = b.group_id and ms.message_id = m.message_id \(messageId.isEmpty ? "" : " and m.message_id = '\(messageId)'") and m.is_call_center = 0
                             union
-                            select m.f_pin, \(!lastQuery.isEmpty ? "m.l_pin, m.message_id" : "ms.l_pin, ms.message_id"), \(!lastQuery.isEmpty ? "m.thumb_id," : "ms.counter,") m.message_text, m.server_date, m.image_id, m.video_id, m.file_id, m.attachment_flag, m.message_scope_id, c.f_name || ' (' || b.title || ')', c.image_id profile, '', m.status, m.credential, m.lock, m.audio_id, m.gif_id from MESSAGE_SUMMARY ms, MESSAGE m, DISCUSSION_FORUM b, GROUPZ c where b.group_id = c.group_id and ms.l_pin = b.chat_id and \(!lastQuery.isEmpty ? lastQuery : "ms.message_id = m.message_id \(messageId.isEmpty ? "" : " and m.message_id = '\(messageId)'")") and m.is_call_center = 0
+                            select m.f_pin, ms.l_pin, ms.message_id, ms.counter, m.message_text, m.server_date, m.image_id, m.video_id, m.file_id, m.attachment_flag, m.message_scope_id, c.f_name || ' (' || b.title || ')', c.image_id profile, '', m.status, m.credential, m.lock, m.audio_id, m.gif_id from MESSAGE_SUMMARY ms, MESSAGE m, DISCUSSION_FORUM b, GROUPZ c where b.group_id = c.group_id and ms.l_pin = b.chat_id and ms.message_id = m.message_id \(messageId.isEmpty ? "" : " and m.message_id = '\(messageId)'") and m.is_call_center = 0
                             order by 6 desc
                             """
                 if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
                     while cursorData.next() {
-//                        if !lastQuery.isEmpty {
+                        if !lastQuery.isEmpty {
 //                            for columnIndex in 0..<cursorData.columnCount {
 //                                if let columnName = cursorData.columnName(for: columnIndex) {
 //                                    if let value = cursorData.object(forColumn: columnName) {
@@ -159,7 +159,10 @@ public class Chat: Model {
 //                                }
 //                            }
 //                            print("---------------------")
-//                        }
+                            if (isImage || isVideo) && cursorData.string(forColumnIndex: 3) == "0" {
+                                continue
+                            }
+                        }
                         let chat = Chat(fpin: cursorData.string(forColumnIndex: 0) ?? "",
                                         pin: cursorData.string(forColumnIndex: 1) ?? "",
                                         messageId: cursorData.string(forColumnIndex: 2) ?? "",

+ 21 - 5
NexilisLite/NexilisLite/Source/Nexilis.swift

@@ -120,6 +120,8 @@ public class Nexilis: NSObject {
     public static let IDX_SOCIAL_COMMERCE = 101
     public static let IDX_NEWS = 102
     
+    public static var callAPNActivated = false
+    
     static var ringtonePlayer: AVAudioPlayer?
     static var ringbacktonePlayer: AVAudioPlayer?
     
@@ -366,6 +368,9 @@ public class Nexilis: NSObject {
     }
     
     private static func getPullGroupNoMember() {
+        while Nexilis.isProcessWriteSync {
+            Thread.sleep(forTimeInterval: 0.5)
+        }
         if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.pullGroupNoMember(), timeout: 30 * 1000), response.isOk() {
             let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
             if !data.isEmpty {
@@ -509,6 +514,9 @@ public class Nexilis: NSObject {
     }
     
     private static func getPullWorkingArea() {
+        while Nexilis.isProcessWriteSync {
+            Thread.sleep(forTimeInterval: 0.5)
+        }
         if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getWorkingAreaContactCenter(), timeout: 30 * 1000), response.isOk() {
             let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
             if !data.isEmpty {
@@ -865,12 +873,16 @@ public class Nexilis: NSObject {
         return me
     }
     
+    public static var isProcessWriteSync = false
     public static func writeSync(message: TMessage, timeout: Int = 15 * 1000) -> TMessage? {
+        isProcessWriteSync = true
         do {
-            //print(">> SENDING MESSAGE >> ", message.toLogString())
+            print(">> SENDING MESSAGE >> ", message.toLogString())
             if let data = try API.sGetResponse(sRequest: message.pack(), lTimeout: timeout, bKeepTOResp: true) {
                 let response = TMessage(data: data)
-                //print("<< RESPONSE MESSAGE << ", response.toLogString())
+                print(">> RESPONSE WRITESYNC >> ")
+//                print("<< RESPONSE MESSAGE << ", response.toLogString())
+                isProcessWriteSync = false
                 return response
             }
         } catch {
@@ -2238,6 +2250,9 @@ extension Nexilis: CallDelegate {
                 }
             }
             if (state == Nexilis.AUDIO_CALL_INCOMING && message.split(separator: ",")[1] != "joining Ac.room on channel 0") {
+                if Nexilis.callAPNActivated || APIS.checkAppStateisBackground() {
+                    return
+                }
                 let data = User.getDataCanNil(pin: String(deviceId))
                 if data == nil {
                     DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
@@ -2250,7 +2265,6 @@ extension Nexilis: CallDelegate {
 //                    self.displayIncomingCall(uuid: uuidOngoing, handle: String(deviceId), hasVideo: false) { error in
 //                        UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier)
 //                    }
-                Nexilis.ringtonePlayer?.play()
                 let controller = QmeraAudioViewController()
                 controller.user = User.getData(pin: String(deviceId))
                 controller.isOutgoing = false
@@ -2273,6 +2287,9 @@ extension Nexilis: CallDelegate {
                 }
 //                    API.receiveCCall(sParty: String(deviceId))
             } else if state == Nexilis.VIDEO_CALL_INCOMING {
+                if Nexilis.callAPNActivated || APIS.checkAppStateisBackground() {
+                    return
+                }
                 let dataUser = User.getDataCanNil(pin: String(deviceId))
                 if dataUser == nil {
                     DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
@@ -2280,7 +2297,6 @@ extension Nexilis: CallDelegate {
                     })
                     return
                 }
-                Nexilis.ringtonePlayer?.play()
                 let videoController = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "videoVCQmera") as! QmeraVideoViewController
                 videoController.fPin = String(deviceId)
                 videoController.isInisiator = false
@@ -3558,7 +3574,7 @@ extension Nexilis: MessageDelegate {
                 }
                 return
             }
-            if !Nexilis.showLibraryNotification {
+            if !Nexilis.showLibraryNotification || APIS.checkAppStateisBackground() {
                 return
             }
             let sender = message.getBody(key: CoreMessage_TMessageKey.F_PIN)

+ 20 - 0
NexilisLite/NexilisLite/Source/Utils.swift

@@ -1282,6 +1282,26 @@ public final class Utils {
         return ""
     }
     
+    public static func setTokenAPN(value: String) {
+        SecureUserDefaults.shared.set(value, forKey: "token_apn")
+    }
+    public static func getTokenAPN() -> String {
+        if let value: String = SecureUserDefaults.shared.value(forKey: "token_apn") {
+            return value
+        }
+        return ""
+    }
+    
+    public static func setLastTabSelected(value: Int) {
+        SecureUserDefaults.shared.set(value, forKey: "last_selected_tab")
+    }
+    public static func getLastTabSelected() -> Int {
+        if let value: Int = SecureUserDefaults.shared.value(forKey: "last_selected_tab") {
+            return value
+        }
+        return 0
+    }
+    
     static func getPasswordDB() -> String? {
         do {
             let p = getPassEncDB()

+ 36 - 33
NexilisLite/NexilisLite/Source/View/Call/QmeraAudioViewController.swift

@@ -20,7 +20,8 @@ class QmeraAudioViewController: UIViewController {
     var wbTimer = Timer()
     var wbBlink = false
     var wbRoomId = ""
-    var callFCM = false
+    var callFCM = true
+    var autoAcceptAPN = false
     
     let buttonSize: CGFloat = 70
     
@@ -255,12 +256,14 @@ class QmeraAudioViewController: UIViewController {
         UIDevice.current.isProximityMonitoringEnabled = false
         NotificationCenter.default.removeObserver(self)
         Nexilis.floatingButton.isHidden = false
+        Nexilis.callAPNActivated = false
     }
     
     deinit {
         UIDevice.current.isProximityMonitoringEnabled = false
         NotificationCenter.default.removeObserver(self)
         Nexilis.floatingButton.isHidden = false
+        Nexilis.callAPNActivated = false
     }
     
     override func viewDidAppear(_ animated: Bool) {
@@ -311,6 +314,8 @@ class QmeraAudioViewController: UIViewController {
                     DispatchQueue.global().async {
                         if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getCalling(fPin: u.pin, type: "1"), timeout: 30 * 1000) {
                             if response.isOk() {
+                            } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "01" {
+                                API.initiateCCall(sParty: u.pin)
                             } else {
                                 DispatchQueue.main.async {
                                     self.status.text = "Busy"
@@ -325,7 +330,6 @@ class QmeraAudioViewController: UIViewController {
                         }
                     }
                 } else {
-                    Nexilis.ringbacktonePlayer?.play()
                     API.initiateCCall(sParty: u.pin)
                 }
             } else if !ticketId.isEmpty {
@@ -339,6 +343,18 @@ class QmeraAudioViewController: UIViewController {
                 } else {
                     API.csa(sTicketID: ticketId, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView, bCameraOn: false)
                 }
+            } else if autoAcceptAPN {
+//                API.receiveCCall(sParty: u.pin)
+//                DispatchQueue.global().asyncAfter(deadline: .now() + 1, execute: {
+//                    CallManager.shared.endCall(with: APIS.uuidCall!)
+                DispatchQueue.global().async {
+                    if let response1 = Nexilis.writeSync(message: CoreMessage_TMessageBank.getNotifyCalling(fPin: u.pin, lPin: User.getMyPin()!, type: "1")) {
+                        if response1.isOk() {
+                //                            API.receiveCCall(sParty: u.pin)
+                        }
+                    }
+                }
+//                })
             }
         }
     }
@@ -347,8 +363,11 @@ class QmeraAudioViewController: UIViewController {
         DispatchQueue.main.async {
             let data:[AnyHashable : Any] = notification.userInfo!
             if let l_pin = data["l_pin"] as? String {
-                Nexilis.ringbacktonePlayer?.play()
-                API.initiateCCall(sParty: l_pin)
+                if let f_pin = data["f_pin"] as? String {
+                    if f_pin == User.getMyPin()!  {
+                        API.initiateCCall(sParty: l_pin)
+                    }
+                }
             }
         }
     }
@@ -389,6 +408,7 @@ class QmeraAudioViewController: UIViewController {
     }
     
     private func outgoingView() {
+        status.text = "Connecting..."
         view.addSubview(end)
         end.anchor(bottom: view.bottomAnchor, paddingBottom: 60, centerX: view.centerXAnchor, width: buttonSize, height: buttonSize)
         
@@ -761,6 +781,17 @@ class QmeraAudioViewController: UIViewController {
 //                for i in 0..<Nexilis.shared.callManager.calls.count {
 //                    Nexilis.shared.callManager.end(call: Nexilis.shared.callManager.calls[i])
 //                }
+                if callFCM {
+                    DispatchQueue.global().async {
+                        if let _ = Nexilis.writeSync(message: CoreMessage_TMessageBank.getCancelCall(fPin: self.user!.pin, type: "1"), timeout: 30 * 1000) {
+                        } else {
+                            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
+                            imageView.tintColor = .white
+                            let banner = FloatingNotificationBanner(title: "Unable to access servers. Try again later".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                            banner.show()
+                        }
+                    }
+                }
                 API.terminateCall(sParty: nil)
                 DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
                     self.dismiss(animated: false, completion: nil)
@@ -777,20 +808,10 @@ class QmeraAudioViewController: UIViewController {
     }
     
     @objc func didReject(sender: Any?) {
-        if isOutgoing {
-            Nexilis.ringbacktonePlayer?.stop()
-        } else {
-            Nexilis.ringtonePlayer?.stop()
-        }
         didEnd(sender: sender)
     }
     
     @objc func didAccept(sender: Any?) {
-        if isOutgoing {
-            Nexilis.ringbacktonePlayer?.stop()
-        } else {
-            Nexilis.ringtonePlayer?.stop()
-        }
         NSLayoutConstraint.deactivate(stack.constraints)
         stack.subviews.forEach { subview in
             subview.removeFromSuperview()
@@ -858,13 +879,10 @@ class QmeraAudioViewController: UIViewController {
             } else if state == Nexilis.AUDIO_CALL_RINGING || (!ticketId.isEmpty && state == Nexilis.VIDEO_CALL_RINGING) {
                 if users.count == 1 {
                     DispatchQueue.main.async {
-                        self.status.text = "Ringing..."
+                        self.status.text = "Waiting for answer".localized()
                     }
                 }
             } else if state == Nexilis.AUDIO_CALL_OFFHOOK || (!ticketId.isEmpty && state == Nexilis.VIDEO_CALL_OFFHOOK) {
-                if isOutgoing {
-                    Nexilis.ringbacktonePlayer?.stop()
-                }
                 if users.count == 1 && firstCall {
                     DispatchQueue.main.async {
                         if !self.ticketId.isEmpty {
@@ -890,11 +908,6 @@ class QmeraAudioViewController: UIViewController {
                     self.users.append(user)
                 }
             } else if state == Nexilis.AUDIO_CALL_END || (!ticketId.isEmpty && state == Nexilis.VIDEO_CALL_END) {
-                if isOutgoing {
-                    Nexilis.ringbacktonePlayer?.stop()
-                } else {
-                    Nexilis.ringtonePlayer?.stop()
-                }
                 let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
                 if let pin = arrayMessage.first, let index = users.firstIndex(of: User(pin: String(pin))) {
                     users.remove(at: index)
@@ -985,11 +998,6 @@ class QmeraAudioViewController: UIViewController {
 //                    }
 //                }
             } else if state == Nexilis.OFFLINE { // Offline
-                if isOutgoing {
-                    Nexilis.ringbacktonePlayer?.stop()
-                } else {
-                    Nexilis.ringtonePlayer?.stop()
-                }
                 let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
                 if let pin = arrayMessage.first, let index = users.firstIndex(of: User(pin: String(pin))) {
                     users.remove(at: index)
@@ -1022,11 +1030,6 @@ class QmeraAudioViewController: UIViewController {
                     }
                 }
             } else if state == Nexilis.BUSY { // Busy
-                if isOutgoing {
-                    Nexilis.ringbacktonePlayer?.stop()
-                } else {
-                    Nexilis.ringtonePlayer?.stop()
-                }
                 let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
                 if let pin = arrayMessage.first, let index = users.firstIndex(of: User(pin: String(pin))) {
                     users.remove(at: index)

+ 22 - 30
NexilisLite/NexilisLite/Source/View/Call/QmeraVideoViewController.swift

@@ -23,7 +23,7 @@ class QmeraVideoViewController: UIViewController {
     var isSpeaker = true
     var isMuted = false
     var isPresent = false
-    var callFCM = false
+    var callFCM = true
     var isNavigationHidden = false
     var listRemoteViewFix: [UIImageView] = [
         UIImageView(),
@@ -86,6 +86,7 @@ class QmeraVideoViewController: UIViewController {
     var transformZoomAfterNewUserMore2 = false
     var isAddCall = ""
     var ticketId = ""
+    var autoAcceptAPN = false
     private var frontCamera = true
     var users: [User] = []
     let poweredByView: UIStackView = {
@@ -138,6 +139,7 @@ class QmeraVideoViewController: UIViewController {
         navigationController?.interactivePopGestureRecognizer?.isEnabled = true
         NotificationCenter.default.removeObserver(self)
         Nexilis.floatingButton.isHidden = false
+        Nexilis.callAPNActivated = false
     }
     
     override func viewWillDisappear(_ animated: Bool) {
@@ -150,6 +152,7 @@ class QmeraVideoViewController: UIViewController {
             NotificationCenter.default.removeObserver(self)
         }
         Nexilis.floatingButton.isHidden = false
+        Nexilis.callAPNActivated = false
     }
 
     override func viewDidLoad() {
@@ -183,6 +186,15 @@ class QmeraVideoViewController: UIViewController {
         if isAutoAccept {
             didTapAcceptCallButton()
         }
+        if autoAcceptAPN {
+//            API.receiveCCall(sParty: dataPerson[0]["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView,ivRemoteZ: zoomView)
+            DispatchQueue.global().async {
+                if let response1 = Nexilis.writeSync(message: CoreMessage_TMessageBank.getNotifyCalling(fPin: self.fPin, lPin: User.getMyPin()!, type: "1")) {
+                    if response1.isOk() {
+                    }
+                }
+            }
+        }
         
     }
     
@@ -394,14 +406,15 @@ class QmeraVideoViewController: UIViewController {
             labelIncomingOutgoing.centerXAnchor.constraint(equalTo: view.centerXAnchor)
         ])
         if isInisiator {
-            labelIncomingOutgoing.text = "Outgoing video call".localized() + "..."
-//            Nexilis.startAudio()
+            labelIncomingOutgoing.text = "Connecting".localized()
             if ticketId.isEmpty {
                 if callFCM {
                     DispatchQueue.global().async {
                         if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getCalling(fPin: self.dataPerson[0]["f_pin"]!!, type: "2"), timeout: 30 * 1000) {
                             if response.isOk() {
                                 
+                            } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "01" {
+                                API.initiateCCall(sParty: self.dataPerson[0]["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: self.listRemoteViewFix, ivLocalView: self.cameraView, ivRemoteZ: self.zoomView)
                             } else {
                                 DispatchQueue.main.async {
                                     if self.labelIncomingOutgoing.isDescendant(of: self.view) {
@@ -423,7 +436,6 @@ class QmeraVideoViewController: UIViewController {
                         }
                     }
                 } else {
-                    Nexilis.ringbacktonePlayer?.play()
                     API.initiateCCall(sParty: dataPerson[0]["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView)
                 }
             } else {
@@ -490,8 +502,12 @@ class QmeraVideoViewController: UIViewController {
         DispatchQueue.main.async { [self] in
             let data:[AnyHashable : Any] = notification.userInfo!
             if let l_pin = data["l_pin"] as? String {
-                Nexilis.ringtonePlayer?.play()
-                API.initiateCCall(sParty: l_pin, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView)
+                if let f_pin = data["f_pin"] as? String {
+                    if f_pin == User.getMyPin()!  {
+                        labelIncomingOutgoing.text = "Waiting for answer".localized()
+                        API.initiateCCall(sParty: l_pin, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView)
+                    }
+                }
             }
         }
     }
@@ -613,7 +629,6 @@ class QmeraVideoViewController: UIViewController {
             } else {
                 API.csa(sTicketID: ticketId, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView, bCameraOn: true)
             }
-            Nexilis.ringtonePlayer?.stop()
         }
         DispatchQueue.main.async {
             self.myImage.removeFromSuperview()
@@ -896,11 +911,6 @@ class QmeraVideoViewController: UIViewController {
     }
     
     func endAllCall() {
-        if isInisiator {
-            Nexilis.ringbacktonePlayer?.stop()
-        } else {
-            Nexilis.ringtonePlayer?.stop()
-        }
         let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
         if !onGoingCC.isEmpty {
             let requester = onGoingCC.components(separatedBy: ",")[0]
@@ -1079,9 +1089,6 @@ class QmeraVideoViewController: UIViewController {
             }
         }
         else if (state == Nexilis.VIDEO_CALL_OFFHOOK) {
-            if isInisiator {
-                Nexilis.ringbacktonePlayer?.stop()
-            }
             let channel = arrayMessage[3]
             remoteChannel[String(channel)] = String(arrayMessage[5])
             DispatchQueue.main.async {
@@ -1216,11 +1223,6 @@ class QmeraVideoViewController: UIViewController {
                 }
             }
         } else if (state == Nexilis.VIDEO_CALL_END || state == Nexilis.AUDIO_CALL_END) {
-            if isInisiator {
-                Nexilis.ringbacktonePlayer?.stop()
-            } else {
-                Nexilis.ringtonePlayer?.stop()
-            }
             let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
             if !onGoingCC.isEmpty {
                 let requester = onGoingCC.components(separatedBy: ",")[0]
@@ -1370,11 +1372,6 @@ class QmeraVideoViewController: UIViewController {
                 }
             }
         } else if (state == Nexilis.OFFLINE) {
-            if isInisiator {
-                Nexilis.ringbacktonePlayer?.stop()
-            } else {
-                Nexilis.ringtonePlayer?.stop()
-            }
             let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
             DispatchQueue.main.async {
                 if (self.dataPerson.count == 1) {
@@ -1447,11 +1444,6 @@ class QmeraVideoViewController: UIViewController {
                 }
             }
         } else if (state == Nexilis.BUSY) {
-            if isInisiator {
-                Nexilis.ringbacktonePlayer?.stop()
-            } else {
-                Nexilis.ringtonePlayer?.stop()
-            }
             let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
             DispatchQueue.main.async { [self] in
                 if (self.dataPerson.count == 1) {

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

@@ -2047,7 +2047,9 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
             message.mStatus = CoreMessage_TMessageUtil.getTID()
             message.mBodies[CoreMessage_TMessageKey.L_PIN] = f_pin
             message.mBodies[CoreMessage_TMessageKey.MESSAGE_ID] = "-2,\(message_id)"
-            _ = Nexilis.write(message: message)
+            DispatchQueue.global().async {
+                _ = Nexilis.write(message: message)
+            }
         }
         if let index = dataMessages.firstIndex(where: {$0["message_id"] as? String == message_id}) {
             dataMessages[index]["status"] = "4"

+ 13 - 12
NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -3085,7 +3085,9 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                     message.mStatus = CoreMessage_TMessageUtil.getTID()
                     message.mBodies[CoreMessage_TMessageKey.L_PIN] = f_pin
                     message.mBodies[CoreMessage_TMessageKey.MESSAGE_ID] = "-2,\(valueListGroupImages[i].messageId)"
-                    _ = Nexilis.write(message: message)
+                    DispatchQueue.global().async {
+                        _ = Nexilis.write(message: message)
+                    }
                 }
             } else {
                 Database.shared.database?.inTransaction({ (fmdb, rollback) in
@@ -3101,7 +3103,9 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                 message.mStatus = CoreMessage_TMessageUtil.getTID()
                 message.mBodies[CoreMessage_TMessageKey.L_PIN] = f_pin
                 message.mBodies[CoreMessage_TMessageKey.MESSAGE_ID] = "-2,\(message_id)"
-                _ = Nexilis.write(message: message)
+                DispatchQueue.global().async {
+                    _ = Nexilis.write(message: message)
+                }
             }
         }
         if let index = dataMessages.firstIndex(where: {$0["message_id"] as? String == message_id}) {
@@ -5189,6 +5193,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
         let dataMessages = dataMessages.filter({$0["chat_date"] as! String == dataDates[indexPath.section]})
         let profileMessage = UIImageView()
         let cell = tableView.dequeueReusableCell(withIdentifier: "cellEditorPersonal", for: indexPath as IndexPath)
+        cell.contentView.subviews.forEach({ $0.removeConstraints($0.constraints) })
         cell.contentView.subviews.forEach({ $0.removeFromSuperview() })
         
         if isContactCenter && isRequestContactCenter && dataMessages[indexPath.row]["category_cc"] != nil {
@@ -5704,7 +5709,6 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
         containerMessage.addSubview(messageText)
         messageText.translatesAutoresizingMaskIntoConstraints = false
         let topMarginText = messageText.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15)
-        topMarginText.isActive = true
         messageText.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
         messageText.font = .systemFont(ofSize: 12)
         if dataMessages[indexPath.row]["attachment_flag"] as? String == "27" || dataMessages[indexPath.row]["attachment_flag"] as? String == "26" || dataMessages[indexPath.row]["message_scope_id"] as? String == "18" {
@@ -6048,13 +6052,13 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                     countRestImages.textColor = .white
                 }
             } else {
-                let getHeightImage = ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.height
-                let getWidthImage = ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.width
-                topMarginText.constant = topMarginText.constant + (getHeightImage < 40 ? 40 : getHeightImage)
+                let getHeightImage: CGFloat = ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.height
+                let getWidthImage: CGFloat = ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.width
+                topMarginText.constant = topMarginText.constant + (getHeightImage < 40 ? 45 : getHeightImage + 5)
                 
                 containerMessage.addSubview(imageThumb)
-                imageThumb.translatesAutoresizingMaskIntoConstraints = false
                 imageThumb.frame = CGRect(x: 0, y: 0, width: getWidthImage, height: getHeightImage)
+                imageThumb.translatesAutoresizingMaskIntoConstraints = false
                 let data = queryMessageReply(message_id: reffChat)
                 if (reffChat.isEmpty || data.count == 0) && (dataMessages[indexPath.row][TypeDataMessage.is_forwarded] == nil || dataMessages[indexPath.row][TypeDataMessage.is_forwarded] as! Int == 0) {
                     imageThumb.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 15).isActive = true
@@ -6359,11 +6363,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                         containerMessage.addSubview(containerLinkMessage)
                         containerLinkMessage.translatesAutoresizingMaskIntoConstraints = false
                         containerLinkMessage.leadingAnchor.constraint(equalTo:containerMessage.leadingAnchor, constant: 15).isActive = true
-                        if dataMessages[indexPath.row]["attachment_flag"] as? String == "11" {
-                            containerLinkMessage.bottomAnchor.constraint(equalTo: imageSticker.topAnchor, constant: -5).isActive = true
-                        } else {
-                            containerLinkMessage.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
-                        }
+                        containerLinkMessage.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
                         containerLinkMessage.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
                         containerLinkMessage.heightAnchor.constraint(equalToConstant: 80.0).isActive = true
                         containerLinkMessage.backgroundColor = .gray.withAlphaComponent(0.2)
@@ -6704,6 +6704,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
             let textForwarded = "Forwarded".localized()
             titleForwarded.attributedText = " %\(textForwarded)%".richText()
         }
+        topMarginText.isActive = true
 //        let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureCellAction))
 //        panGestureRecognizer.delegate = self
 //        cellMessage.addGestureRecognizer(panGestureRecognizer)

+ 34 - 3
NexilisLite/NexilisLite/Source/View/Control/ContactChatViewController.swift

@@ -60,13 +60,10 @@ class ContactChatViewController: UITableViewController {
             if segment.numberOfSegments == 3 {
                 switch segment.selectedSegmentIndex {
                 case 1:
-                    Utils.inTabChats = false
                     fillteredData = self.contacts.filter { $0.fullName.lowercased().contains(searchText.lowercased()) }
                 case 2:
-                    Utils.inTabChats = false
                     fillteredData = self.groups.filter { $0.name.lowercased().contains(searchText.lowercased()) }
                 default:
-                    Utils.inTabChats = true
                     fillteredData = self.chats.filter { $0.name.lowercased().contains(searchText.lowercased()) || $0.messageText.lowercased().contains(searchText.lowercased()) }
                 }
             } else {
@@ -382,6 +379,37 @@ class ContactChatViewController: UITableViewController {
     }
     
     @objc func segmentChanged(sender: Any) {
+        switch segment.selectedSegmentIndex {
+        case 0:
+            Utils.inTabChats = true
+        case 2:
+            Utils.inTabChats = false
+            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()
+                        }
+                    }
+                })
+            }
+        default:
+            Utils.inTabChats = false
+        }
         filterContentForSearchText(searchController.searchBar.text!)
     }
     
@@ -533,6 +561,9 @@ class ContactChatViewController: UITableViewController {
     }
     
     private func getOpenGroups(listGroups: [Group], completion: @escaping ([Group]) -> ()) {
+        while Nexilis.isProcessWriteSync {
+            Thread.sleep(forTimeInterval: 0.5)
+        }
         if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getOpenGroups(p_account: "1,2,3,5,6,7", offset: "0", search: "")) {
             var dataGroups: [Group] = []
             if (response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "00") {

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

@@ -540,6 +540,7 @@ public class SettingTableViewController: UITableViewController, UIGestureRecogni
                         if(!id.isEmpty){
 //                            Nexilis.changeUser(f_pin: id)
                             SecureUserDefaults.shared.set(id, forKey: "me")
+                            APIS.sendPushToken(Utils.getTokenAPN(), isResend: true)
                             Utils.setProfile(value: false)
                             if Utils.getForceAnonymous() {
                                 self.deleteAllRecordDatabase()