Преглед на файлове

update show broadcast gif

alqindiirsyam преди 1 година
родител
ревизия
854c2364d4

+ 131 - 17
appbuilder-ios/NexilisLite/NexilisLite/Source/Extension.swift

@@ -9,6 +9,8 @@
 import Foundation
 import UIKit
 import SDWebImage
+import ImageIO
+import MobileCoreServices
 
 extension Date {
     
@@ -223,6 +225,56 @@ extension UIViewController {
 }
 
 extension UIImage {
+    class func gifImageWithData(_ data: Data) -> UIImage? {
+        guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
+            return nil
+        }
+        
+        let frameCount = CGImageSourceGetCount(source)
+        var images: [UIImage] = []
+        
+        for i in 0..<frameCount {
+            if let cgImage = CGImageSourceCreateImageAtIndex(source, i, nil) {
+                let image = UIImage(cgImage: cgImage)
+                images.append(image)
+            }
+        }
+        
+        return UIImage.animatedImage(with: images, duration: 0.0)
+    }
+    
+    func gifData() -> Data? {
+        guard let cgImages = self.images?.compactMap({ $0.cgImage }) else {
+            return nil
+        }
+
+        let frameProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: 0.1]] // Adjust delay time if necessary
+
+        let destinationData = NSMutableData()
+        guard let destination = CGImageDestinationCreateWithData(destinationData as CFMutableData, kUTTypeGIF, cgImages.count, nil) else {
+            return nil
+        }
+
+        let framePropertiesPointer = UnsafeMutablePointer<NSDictionary>.allocate(capacity: cgImages.count)
+        defer {
+            framePropertiesPointer.deallocate()
+        }
+        for i in 0..<cgImages.count {
+            framePropertiesPointer[i] = frameProperties as NSDictionary
+        }
+
+        CGImageDestinationSetProperties(destination, [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]] as CFDictionary)
+        for image in cgImages {
+            CGImageDestinationAddImage(destination, image, framePropertiesPointer.pointee)
+        }
+
+        guard CGImageDestinationFinalize(destination) else {
+            return nil
+        }
+
+        return destinationData as Data
+    }
+    
     func resize(target: CGSize) -> UIImage {
         // Determine the scale factor that preserves aspect ratio
         let widthRatio = target.width / size.width
@@ -1100,7 +1152,7 @@ extension UIImageView {
         set { objc_setAssociatedObject(self, &UIImageView.urlKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
     }
 
-    func loadImageAsync(with urlString: String?) {
+    func loadImageAsync(with urlString: String?, isGif: Bool = false) {
         // cancel prior task, if any
 
         weak var oldTask = currentTask
@@ -1116,6 +1168,15 @@ extension UIImageView {
         guard let urlString = urlString else { return }
 
         // check cache
+        
+        if isGif, let cachedImageGif = ImageCache.shared.imageGif(forKey: urlString) {
+            guard let gifImage = UIImage.gifImageWithData(Data(referencing: cachedImageGif)) else {
+                print("Failed to create the GIF image.")
+                return
+            }
+            self.image = gifImage
+            return
+        }
 
         if let cachedImage = ImageCache.shared.image(forKey: urlString) {
             self.image = cachedImage
@@ -1145,16 +1206,31 @@ extension UIImageView {
                 return
             }
 
-            guard let data = data, let downloadedImage = UIImage(data: data) else {
+            guard let data = data else {
                 //print("unable to extract image")
                 return
             }
-
-            ImageCache.shared.save(image: downloadedImage, forKey: urlString)
+            
+            let downloadedImage = UIImage(data: data)
+            if isGif {
+                ImageCache.shared.saveGif(data: NSData(data: data), forKey: urlString)
+            } else {
+                if downloadedImage != nil {
+                    ImageCache.shared.save(image: downloadedImage!, forKey: urlString)
+                }
+            }
 
             if url == self?.currentURL {
                 DispatchQueue.main.async {
-                    self?.image = downloadedImage
+                    if isGif {
+                        guard let gifImage = UIImage.gifImageWithData(data) else {
+                            print("Failed to create the GIF image.")
+                            return
+                        }
+                        self?.image = gifImage
+                    } else {
+                        self?.image = downloadedImage
+                    }
                 }
             }
         }
@@ -1227,29 +1303,67 @@ extension UITextField {
 }
 
 public class ImageCache {
-    private let cache = NSCache<NSString, UIImage>()
-    private var observer: NSObjectProtocol!
-
     public static let shared = ImageCache()
+    private let cache = NSCache<NSString, UIImage>()
+    private let cacheGif = NSCache<NSString, NSData>()
 
     private init() {
-        // make sure to purge cache on memory pressure
-
-        observer = NotificationCenter.default.addObserver(forName: UIApplication.didReceiveMemoryWarningNotification, object: nil, queue: nil) { [weak self] notification in
-            self?.cache.removeAllObjects()
-        }
+        loadCache()
+//        UserDefaults.standard.removeObject(forKey: "imageCache")
+        NotificationCenter.default.addObserver(self, selector: #selector(saveCache), name: UIApplication.didEnterBackgroundNotification, object: nil)
     }
 
-    deinit {
-        NotificationCenter.default.removeObserver(observer as Any)
+    public func save(image: UIImage, forKey key: String) {
+        cache.setObject(image, forKey: key as NSString)
+        cacheKeys.append(key)
+        saveCache()
+    }
+    
+    public func saveGif(data: NSData, forKey key: String) {
+        cacheGif.setObject(data, forKey: key as NSString)
+        cacheKeys.append(key)
+        saveCache()
     }
 
     public func image(forKey key: String) -> UIImage? {
         return cache.object(forKey: key as NSString)
     }
+    public func imageGif(forKey key: String) -> NSData? {
+        return cacheGif.object(forKey: key as NSString)
+    }
+    
+    private var cacheKeys: [String] = []
+    private func loadCache() {
+        if let cachedData = UserDefaults.standard.object(forKey: "imageCache") as? Data {
+            if let decodedCache = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(cachedData) as? [String: Data] {
+                for (key, valueData) in decodedCache {
+                    if let image = UIImage(data: valueData) {
+                        cache.setObject(image, forKey: key as NSString)
+                        cacheKeys.append(key)
+                    }
+                    let dataGif = NSData(data: valueData)
+                    cacheGif.setObject(dataGif, forKey: key as NSString)
+                    cacheKeys.append(key)
+                }
+            }
+        }
+    }
 
-    public func save(image: UIImage, forKey key: String) {
-        cache.setObject(image, forKey: key as NSString)
+    @objc private func saveCache() {
+        var cacheDictionary = [String: Data]()
+        for key in cacheKeys {
+            if let dataGif = cacheGif.object(forKey: key as NSString) {
+                cacheDictionary[key] = Data(referencing: dataGif)
+            } else {
+                if let image = cache.object(forKey: key as NSString) {
+                    if let imageData = image.pngData() {
+                        cacheDictionary[key] = imageData
+                    }
+                }
+            }
+        }
+        let encodedData = try? NSKeyedArchiver.archivedData(withRootObject: cacheDictionary, requiringSecureCoding: false)
+        UserDefaults.standard.set(encodedData, forKey: "imageCache")
     }
 }
 

+ 1 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/IncomingThread.swift

@@ -60,7 +60,7 @@ class IncomingThread {
     }
     
     private func process(message: TMessage) {
-        print("incoming process", message.toLogString())
+//        print("incoming process", message.toLogString())
         if message.getCode() == CoreMessage_TMessageCode.LOGIN_FILE {
             loginFile(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.PUSH_MYSELF || message.getCode() == CoreMessage_TMessageCode.PUSH_MYSELF_ACK || message.getCode() == CoreMessage_TMessageCode.PULL_MYSELF {

+ 105 - 7
appbuilder-ios/NexilisLite/NexilisLite/Source/Nexilis.swift

@@ -14,6 +14,7 @@ import UIKit
 import FMDB
 import QuickLook
 import NotificationBannerSwift
+import SDWebImage
 
 public class Nexilis: NSObject {
     public static var sAPIKey = ""
@@ -1839,9 +1840,9 @@ public class Nexilis: NSObject {
 //        })
 //    }
     public static func debugBroadcast(){
-        if(!broadcastList.isEmpty) {
-            let m = broadcastList[0]
-            Nexilis.shared.showBroadcastMessage(m: m)
+        if(Utils.getDebugBC() != nil) {
+            let m = Utils.getDebugBC()
+            Nexilis.shared.showBroadcastMessage(m: m!)
         }
     }
     
@@ -2240,6 +2241,10 @@ extension Nexilis: CallDelegate {
 
 var previewItem : NSURL?
 var listCCIdInv: [String] = []
+var imageGif: UIImageView!
+var posGif = "0"
+var loopGif = "0"
+var timerAnimationGif = Timer()
 
 extension Nexilis: MessageDelegate {
     public func onReceiveComment(message: TMessage) {
@@ -2257,6 +2262,45 @@ extension Nexilis: MessageDelegate {
         UIApplication.shared.open(url)
     }
     
+    private func runAnimationGif() {
+        DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: { [self] in
+            if imageGif != nil {
+                timerAnimationGif.invalidate()
+                timerAnimationGif = Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(animateGif), userInfo: nil, repeats: true)
+            }
+        })
+    }
+    
+    @objc func animateGif() {
+        DispatchQueue.main.async {
+            if posGif == "0" { //left
+                if imageGif.frame.origin.x < (UIScreen.main.bounds.width - imageGif.frame.width) {
+                    imageGif.frame.origin.x+=0.1
+                } else {
+                    timerAnimationGif.invalidate()
+                }
+            } else if posGif == "1" { //right
+                if imageGif.frame.origin.x > 0 {
+                    imageGif.frame.origin.x-=0.1
+                } else {
+                    timerAnimationGif.invalidate()
+                }
+            } else if posGif == "2" { //top
+                if imageGif.frame.origin.y < (UIScreen.main.bounds.height - imageGif.frame.height) {
+                    imageGif.frame.origin.y+=0.1
+                } else {
+                    timerAnimationGif.invalidate()
+                }
+            } else { //bottom
+                if imageGif.frame.origin.y > 20 {
+                    imageGif.frame.origin.y-=0.1
+                } else {
+                    timerAnimationGif.invalidate()
+                }
+            }
+        }
+    }
+    
     func showBroadcastMessage(m: [String: String]) {
         let fileType = m[CoreMessage_TMessageKey.CATEGORY_FLAG]!
         let gifId = m[CoreMessage_TMessageKey.GIF_ID] ?? ""
@@ -2265,10 +2309,63 @@ extension Nexilis: MessageDelegate {
             broadcastVC.modalPresentationStyle = .custom
             viewBroadcast.backgroundColor = .black.withAlphaComponent(0.3)
             if !gifId.isEmpty {
-                let containerView = UIView()
-                viewBroadcast.addSubview(containerView)
-                containerView.anchor(centerX: viewBroadcast.centerXAnchor, centerY: viewBroadcast.centerYAnchor, width: 100, height: 100)
-                containerView.backgroundColor = .yellow.withAlphaComponent(0.5)
+                let urlGif = "\(Utils.getURLBase())filepalio/image/\(gifId)"
+                let scale = m[CoreMessage_TMessageKey.SCALE] ?? "0"
+                posGif = m[CoreMessage_TMessageKey.START_ANIMATION] ?? "0"
+                loopGif = m[CoreMessage_TMessageKey.LOOP_ANIMATION] ?? "0"
+                
+                imageGif = UIImageView()
+                viewBroadcast.addSubview(imageGif)
+                imageGif.isUserInteractionEnabled = true
+                imageGif.contentMode = .scaleAspectFit
+                
+                let buttonClose = UIButton(type: .close)
+                buttonClose.frame.size = CGSize(width: 30, height: 30)
+                buttonClose.layer.cornerRadius = 15.0
+                buttonClose.clipsToBounds = true
+                buttonClose.backgroundColor = .black.withAlphaComponent(0.5)
+                buttonClose.actionHandle(controlEvents: .touchUpInside,
+                 ForAction:{() -> Void in
+                    broadcastVC.dismiss(animated: true, completion: {
+//                        imageGif = nil
+//                        Nexilis.broadcastList.remove(at: 0)
+//                        if Nexilis.broadcastList.count > 0 {
+//                            Nexilis.shared.showBroadcastMessage(m: Nexilis.broadcastList[0])
+//                        }
+                    })
+                 })
+                imageGif.addSubview(buttonClose)
+                buttonClose.anchor(top: imageGif.topAnchor, right: imageGif.rightAnchor, width: 30, height: 30)
+                
+                var xpos: CGFloat = 0
+                var ypos: CGFloat = 0
+                var widthImage: CGFloat = 300
+                var heightImage: CGFloat = 300
+                if scale == "2" { //50%
+                    widthImage = 150
+                    heightImage = 150
+                } else if scale == "1" { //75%
+                    widthImage = 225
+                    heightImage = 225
+                }
+                
+                if posGif == "0" { //left
+                    xpos = 0
+                    ypos = (viewBroadcast.frame.size.height / 2) - (heightImage / 2)
+                } else if posGif == "1" { //right
+                    xpos = viewBroadcast.frame.size.width - widthImage
+                    ypos = (viewBroadcast.frame.size.height / 2) - (heightImage / 2)
+                } else if posGif == "2" { //top
+                    xpos = (viewBroadcast.frame.size.width / 2) - (widthImage / 2)
+                    ypos = 20
+                } else { //bottom
+                    xpos = (viewBroadcast.frame.size.width / 2) - (widthImage / 2)
+                    ypos = viewBroadcast.frame.size.height - heightImage
+                }
+                imageGif.frame = CGRect(x: xpos, y: ypos, width: widthImage, height: heightImage)
+                imageGif.loadImageAsync(with: urlGif, isGif: true)
+                runAnimationGif()
+                
             } else {
                 let stringLink = m[CoreMessage_TMessageKey.LINK] ?? ""
                 
@@ -3262,6 +3359,7 @@ extension Nexilis: MessageDelegate {
         } else if message.getCode() != CoreMessage_TMessageCode.PUSH_CALL_CENTER && message.getCode() != CoreMessage_TMessageCode.ACCEPT_CALL_CENTER && message.getCode() != CoreMessage_TMessageCode.END_CALL_CENTER && message.getCode() != CoreMessage_TMessageCode.TIMEOUT_CONTACT_CENTER && message.getCode() != CoreMessage_TMessageCode.ACCEPT_CONTACT_CENTER && message.getCode() != CoreMessage_TMessageCode.PUSH_MEMBER_ROOM_CONTACT_CENTER && message.getCode() != CoreMessage_TMessageCode.INVITE_END_CONTACT_CENTER && message.getCode() != CoreMessage_TMessageCode.INVITE_EXIT_CONTACT_CENTER || !message.getBody(key: CoreMessage_TMessageKey.MERCHANT_NAME).isEmpty {
             let m = message.mBodies
             if !message.getBody(key: CoreMessage_TMessageKey.MERCHANT_NAME).isEmpty {
+//                Utils.setDebugBC(value: m)
                 DispatchQueue.main.async {
                     if !Nexilis.broadcastList.isEmpty {
                         Nexilis.broadcastList.append(m)

+ 6 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/Utils.swift

@@ -898,6 +898,12 @@ public final class Utils {
     public static func getIconDockSize() -> String {
         UserDefaults.standard.string(forKey: "icon_size") ?? "0"
     }
+    static func setDebugBC(value: [String: String]) {
+        UserDefaults.standard.set(value, forKey: "debugBc")
+    }
+    static func getDebugBC() -> [String: String]? {
+        UserDefaults.standard.object(forKey: "debugBc") as? [String : String]
+    }
 }
 public extension UIImage {
     var jpeg: Data? { jpegData(compressionQuality: 1) }  // QUALITY min = 0 / max = 1