Procházet zdrojové kódy

feat:3.6.12(1)提测

100Years před 2 týdny
rodič
revize
44f476b3cf
30 změnil soubory, kde provedl 620 přidání a 47 odebrání
  1. 10 2
      TSLiveWallpaper.xcodeproj/project.pbxproj
  2. 2 2
      TSLiveWallpaper/Business/TSAIListVC/TSAIExpandImageVC/View/TSAIExpandChangeView.swift
  3. 1 0
      TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/TSAIListStyleMoreVC/TSAIListStyleMoreVC.swift
  4. 6 1
      TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/TSAIListVC.swift
  5. 8 2
      TSLiveWallpaper/Business/TSAIListVC/TSAIPhotoGeneratorVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC+Expand.swift
  6. 4 0
      TSLiveWallpaper/Business/TSAIListVC/TSAIUploadPhotoVC/TSAIPhotoRemoveVC/TSAIPhotoRemoveVC.swift
  7. 6 1
      TSLiveWallpaper/Business/TSAIListVC/TSAIUploadPhotoVC/TSAIPhotoRemoveVC/View/TSAIPhotoRemoveBgView.swift
  8. 3 0
      TSLiveWallpaper/Business/TSAIResultsFrameVC/TSAIUsedPhotoVC/TSAIUsedPhotoVC.swift
  9. 45 0
      TSLiveWallpaper/Business/TSMineVC/TSMineVM.swift
  10. 142 0
      TSLiveWallpaper/Common/Purchase/TSPurchaseBusiness+Limit.swift
  11. 52 9
      TSLiveWallpaper/Common/Purchase/TSPurchaseBusiness.swift
  12. 12 2
      TSLiveWallpaper/Common/Purchase/TSPurchaseManager.swift
  13. 47 13
      TSLiveWallpaper/Common/TSNetWork/TSNetWork+Business.swift
  14. 94 0
      TSLiveWallpaper/Common/Tool/KeychainManager.swift
  15. 13 7
      TSLiveWallpaper/Common/ViewTool/TSPhotoPickerManager/TSPhotoPickerManager.swift
  16. 1 1
      TSLiveWallpaper/Data/Model/TSActionInfoModel.swift
  17. 2 2
      TSLiveWallpaper/Data/OperationQueue/TSGenerateBaseOperation/TSGenerateBasePhotoOperation.swift
  18. 6 4
      TSLiveWallpaper/Data/TSDBManager/TSDBKeyManager.swift
  19. 1 1
      TSLiveWallpaper/Data/TSRealmManager/TSRealmManager.swift
  20. 15 0
      TSLiveWallpaper/ar.lproj/Localizable.strings
  21. 15 0
      TSLiveWallpaper/de.lproj/Localizable.strings
  22. 15 0
      TSLiveWallpaper/en.lproj/Localizable.strings
  23. 15 0
      TSLiveWallpaper/es.lproj/Localizable.strings
  24. 15 0
      TSLiveWallpaper/fr.lproj/Localizable.strings
  25. 15 0
      TSLiveWallpaper/it.lproj/Localizable.strings
  26. 15 0
      TSLiveWallpaper/ja.lproj/Localizable.strings
  27. 15 0
      TSLiveWallpaper/ko.lproj/Localizable.strings
  28. 15 0
      TSLiveWallpaper/pt-BR.lproj/Localizable.strings
  29. 15 0
      TSLiveWallpaper/pt-PT.lproj/Localizable.strings
  30. 15 0
      TSLiveWallpaper/zh-Hant.lproj/Localizable.strings

+ 10 - 2
TSLiveWallpaper.xcodeproj/project.pbxproj

@@ -160,6 +160,8 @@
 		A8F9F2F62E372C49007FE237 /* TSAIExpandImageVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F9F2F52E372C47007FE237 /* TSAIExpandImageVM.swift */; };
 		A8F9F2F82E372CD8007FE237 /* ai_expand_image_style.json in Resources */ = {isa = PBXBuildFile; fileRef = A8F9F2F72E372CD8007FE237 /* ai_expand_image_style.json */; };
 		A8F9F2FA2E375FE7007FE237 /* TSAIPhotoDetailsVC+Expand.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F9F2F92E375FD7007FE237 /* TSAIPhotoDetailsVC+Expand.swift */; };
+		A8F9F30F2E379AA8007FE237 /* TSPurchaseBusiness+Limit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F9F30E2E379AA3007FE237 /* TSPurchaseBusiness+Limit.swift */; };
+		A8F9F3112E379B2D007FE237 /* KeychainManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F9F3102E379B2D007FE237 /* KeychainManager.swift */; };
 		A8FD8F332DFBCB85008CAACF /* ZillaSlab-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A8FD8F322DFBCB85008CAACF /* ZillaSlab-Regular.ttf */; };
 		A8FD8F342DFBCB85008CAACF /* ZillaSlab-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A8FD8F312DFBCB85008CAACF /* ZillaSlab-Medium.ttf */; };
 		A8FD8F352DFBCB85008CAACF /* ZillaSlab-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A8FD8F302DFBCB85008CAACF /* ZillaSlab-BoldItalic.ttf */; };
@@ -357,6 +359,8 @@
 		A8F9F2F52E372C47007FE237 /* TSAIExpandImageVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAIExpandImageVM.swift; sourceTree = "<group>"; };
 		A8F9F2F72E372CD8007FE237 /* ai_expand_image_style.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = ai_expand_image_style.json; sourceTree = "<group>"; };
 		A8F9F2F92E375FD7007FE237 /* TSAIPhotoDetailsVC+Expand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSAIPhotoDetailsVC+Expand.swift"; sourceTree = "<group>"; };
+		A8F9F30E2E379AA3007FE237 /* TSPurchaseBusiness+Limit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSPurchaseBusiness+Limit.swift"; sourceTree = "<group>"; };
+		A8F9F3102E379B2D007FE237 /* KeychainManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainManager.swift; sourceTree = "<group>"; };
 		A8FD8F302DFBCB85008CAACF /* ZillaSlab-BoldItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ZillaSlab-BoldItalic.ttf"; sourceTree = "<group>"; };
 		A8FD8F312DFBCB85008CAACF /* ZillaSlab-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ZillaSlab-Medium.ttf"; sourceTree = "<group>"; };
 		A8FD8F322DFBCB85008CAACF /* ZillaSlab-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ZillaSlab-Regular.ttf"; sourceTree = "<group>"; };
@@ -450,6 +454,7 @@
 		A81CA4882D15840F00A3AAC8 /* Tool */ = {
 			isa = PBXGroup;
 			children = (
+				A8F9F3102E379B2D007FE237 /* KeychainManager.swift */,
 				A83A6A852E1FC9930084197A /* LanguageManager.swift */,
 				A8F8BCDB2E040D9E00EF4AA6 /* TSBusinessFileManager.swift */,
 				A8F8BCD92E040D8100EF4AA6 /* TSDownloadManager.swift */,
@@ -987,6 +992,7 @@
 				A86857992DF9158C0089D222 /* TSPurchaseEnum.swift */,
 				A8F76C3E2D350A9600AA6E93 /* TSPurchaseManager.swift */,
 				A868579D2DF915DD0089D222 /* TSPurchaseBusiness.swift */,
+				A8F9F30E2E379AA3007FE237 /* TSPurchaseBusiness+Limit.swift */,
 			);
 			path = Purchase;
 			sourceTree = "<group>";
@@ -1276,6 +1282,7 @@
 				A83946212D1D61D600ABFF0D /* TSRateUsVC.swift in Sources */,
 				A81F5B4F2D19674600740085 /* AVAsset+Ex.swift in Sources */,
 				A8EE84422E33605200CA04F0 /* TSSelectedPhotoStyleVC.swift in Sources */,
+				A8F9F30F2E379AA8007FE237 /* TSPurchaseBusiness+Limit.swift in Sources */,
 				A8E56BF62D1520EC003C54AF /* AppDelegate.swift in Sources */,
 				A8F778B42D1BB8F600BF55D5 /* PhotoManager.swift in Sources */,
 				A83A6A862E1FC99E0084197A /* LanguageManager.swift in Sources */,
@@ -1296,6 +1303,7 @@
 				A87CF8542E029B450063CB7E /* TSAIPhotoDetailsBrowserCell.swift in Sources */,
 				A81CA4652D15685F00A3AAC8 /* TSLaunchVC.swift in Sources */,
 				A83F28A92E165B1D009A4975 /* TSAIUploadPhotoVC+Style.swift in Sources */,
+				A8F9F3112E379B2D007FE237 /* KeychainManager.swift in Sources */,
 				A8F8BCEB2E0501DC00EF4AA6 /* TSAppUpdateAlertVC.swift in Sources */,
 				A81F5B402D194EA900740085 /* UIDevice+Extension.swift in Sources */,
 				A83946272D1D623800ABFF0D /* TSShareUsVC.swift in Sources */,
@@ -1455,7 +1463,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 3.6.11;
+				MARKETING_VERSION = 3.6.12;
 				PRODUCT_BUNDLE_IDENTIFIER = musicplayer.offline.com;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1496,7 +1504,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 3.6.11;
+				MARKETING_VERSION = 3.6.12;
 				PRODUCT_BUNDLE_IDENTIFIER = musicplayer.offline.com;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE_SPECIFIER = "";

+ 2 - 2
TSLiveWallpaper/Business/TSAIListVC/TSAIExpandImageVC/View/TSAIExpandChangeView.swift

@@ -30,7 +30,7 @@ class TSAIExpandChangeView: TSBaseView {
     
     lazy var expandAreaView: UIView = {
         let view = UIView()
-        
+        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action:#selector(clickAddBgView)))
         view.addSubview(bgImageView)
         bgImageView.snp.makeConstraints { make in
             make.edges.equalToSuperview()
@@ -65,7 +65,7 @@ class TSAIExpandChangeView: TSBaseView {
     
     lazy var uploadImageBgView: UIView = {
         let bgView = UIView()
-        bgView.addGestureRecognizer(UITapGestureRecognizer(target: self, action:#selector(clickAddBgView)))
+
         bgView.addSubview(upLoadView)
         bgView.isHidden = true
         upLoadView.snp.makeConstraints { make in

+ 1 - 0
TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/TSAIListStyleMoreVC/TSAIListStyleMoreVC.swift

@@ -108,6 +108,7 @@ class TSAILIstStyleMoreBaseCell: TSBaseCollectionCell {
 
     lazy var ctxView: UIView = {
         let ctxView = UIView()
+        ctxView.clipsToBounds = true
         return ctxView
     }()
     

+ 6 - 1
TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/TSAIListVC.swift

@@ -205,7 +205,12 @@ extension TSAIListVC {
         layout.itemSize = CGSize(width: itemW, height: itemH)
         layout.minimumInteritemSpacing = 16
         layout.minimumLineSpacing = 16
-        kPushVC(target: self, modelVC:TSAIListStyleMoreVC(titleString:sectionModel.title, models: models, layout: layout, clickBlock: nil))
+        kPushVC(target: self, modelVC:TSAIListStyleMoreVC(titleString:sectionModel.title, models: models, layout: layout, clickBlock:({ [weak self] model in
+            guard let self = self else { return }
+            if let secModel = viewModel.listDatas.safeObj(At:indexPath.section) {
+                Self.clickCell(target: self,indexPath: indexPath, itemModel:model,secModel: secModel)
+            }
+        })))
     }
     
     static func clickCell(target:UIViewController,indexPath:IndexPath,itemModel:TSDiscoverItemModel,secModel:TSDiscoverSectionModel) {

+ 8 - 2
TSLiveWallpaper/Business/TSAIListVC/TSAIPhotoGeneratorVC/TSAIPhotoDetailsVC/TSAIPhotoDetailsVC+Expand.swift

@@ -51,8 +51,14 @@ extension TSAIPhotoDetailsVC {
         expandAreaView.boardView.isHidden = true
         
         if let sizes = self.generatorModel.expandViewSizes {
-            expandAreaView.updateExpandAreaView(width: sizes.0.width, height: sizes.0.height)
-            expandAreaView.updateImageView(width: sizes.1.width, height: sizes.1.height)
+            dePrint("sizes=\(sizes)")
+            
+            let scale = k_ScreenWidth / sizes.0.width
+            expandAreaView.updateExpandAreaView(width: sizes.0.width*scale, height: sizes.0.height*scale)
+            expandAreaView.updateImageView(width: sizes.1.width*scale, height: sizes.1.height*scale)
+
+//            expandAreaView.updateExpandAreaView(width: sizes.0.width, height: sizes.0.height)
+//            expandAreaView.updateImageView(width: sizes.1.width, height: sizes.1.height)
         }
         
         //对比按钮

+ 4 - 0
TSLiveWallpaper/Business/TSAIListVC/TSAIUploadPhotoVC/TSAIPhotoRemoveVC/TSAIPhotoRemoveVC.swift

@@ -65,6 +65,10 @@ class TSAIRemovePhotoBgVC: TSBaseVC {
     lazy var removeView: TSAIPhotoRemoveBgView = {
         let removeView = TSAIPhotoRemoveBgView(targetVC: self)
         removeView.setFrontImage(image: originalImage)
+        removeView.clickImageViewBlock = { [weak self]  in
+            guard let self = self else { return }
+            clickNavRight()
+        }
         return removeView
     }()
     

+ 6 - 1
TSLiveWallpaper/Business/TSAIListVC/TSAIUploadPhotoVC/TSAIPhotoRemoveVC/View/TSAIPhotoRemoveBgView.swift

@@ -7,7 +7,7 @@
 
 
 class TSAIPhotoRemoveBgView: TSBaseView {
-    
+    var clickImageViewBlock:(()->Void)?
     var targetVC:UIViewController
     
     init(targetVC: UIViewController) {
@@ -44,6 +44,11 @@ class TSAIPhotoRemoveBgView: TSBaseView {
     lazy var frontImageView: UIImageView = UIImageView.createImageView()//图片生成
     lazy var topContentView: UIView = {
         let topContentView = UIView()
+        
+        topContentView.addTapAction { [weak self]  in
+            guard let self = self else { return }
+            clickImageViewBlock?()
+        }
 
         topContentView.addSubview(bgImageView)
         bgImageView.addSubview(frontImageView)

+ 3 - 0
TSLiveWallpaper/Business/TSAIResultsFrameVC/TSAIUsedPhotoVC/TSAIUsedPhotoVC.swift

@@ -106,6 +106,9 @@ class TSAIUsedPhotoVC: TSBaseVC {
     
     override func dealThings() {
         updateDataView()
+        NotificationCenter.default.addObserver(forName: .kPhotoPickerCompleted, object: nil, queue: .main) { [weak self] _ in
+            self?.updateDataView()
+        }
     }
     
     @objc func updateDataView(){

+ 45 - 0
TSLiveWallpaper/Business/TSMineVC/TSMineVM.swift

@@ -116,6 +116,51 @@ class TSMineVM {
                 }))
 
         sectionModel.addSubItemModel(updateItem)
+        
+        
+#if DEBUG
+        sectionModel.addSubItemModel(
+            createItemModel(
+                leftImage: .mineCellPrivacy,
+                leftTitle: "清除钥匙串".localized,
+                tapBlock: { [weak self] _, _, _ in
+                    guard let self = self else { return }
+                    KeychainManager.clearAll()
+                    UIAlertView(title: "", message: "清除了钥匙串", delegate: nil, cancelButtonTitle: "OK").show()
+                    
+                    UserDefaults.standard.set(nil, forKey: kVipGeneratedNumKey)
+                    UserDefaults.standard.set(nil, forKey: kDayGeneratedNumKey)
+                    UserDefaults.standard.synchronize()
+                }))
+        
+        sectionModel.addSubItemModel(
+            createItemModel(
+                leftImage: .mineCellPrivacy,
+                leftTitle: "查询会员次数".localized,
+                tapBlock: { [weak self] _, _, _ in
+                    guard let self = self else { return }
+                    
+                    var string = ""
+                    if var saveDict = UserDefaults.standard.dictionary(forKey: kFreeNumKey) as? [String: Int]{
+                        string = string + "新用户免费次数" + (saveDict.toJSONString() ?? "")
+                    }
+                    string = string + "\n"
+            
+                    if var saveDict = UserDefaults.standard.dictionary(forKey: kDayGeneratedNumKey) as? [String: [String: Any]]{
+                        string = string + "每日的次数" + (saveDict.toJSONString() ?? "")
+                    }
+                    string = string + "\n"
+                    if var saveDict = UserDefaults.standard.dictionary(forKey: kVipGeneratedNumKey) as? [String: [String: Any]]{
+                        string = string + "会员的总次"  + (saveDict.toJSONString() ?? "")
+                    }
+                    string = string + "\n当前会员过期时间戳:\(kPurchaseBusiness.expireTime)"
+                    UIAlertView(title: "", message: string, delegate: nil, cancelButtonTitle: "OK").show()
+                    dePrint("所有的会员次数=\(string)")
+                    
+                }))
+#endif
+        
+        
         return dataArray
     }()
     

+ 142 - 0
TSLiveWallpaper/Common/Purchase/TSPurchaseBusiness+Limit.swift

@@ -0,0 +1,142 @@
+//
+//  TSPurchaseBusiness+Limit.swift
+//  TSLiveWallpaper
+//
+//  Created by 100Years on 2025/7/28.
+//
+
+extension PremiumPeriod {
+    
+    /// 对应vip类型,可以每天限额次数
+    var dayGeneratedNumber: Int {
+        return 3//50
+    }
+    /// 对应vip类型,可以每天限额视频次数
+    var dayGeneratedVideoNumber: Int {
+        return 3//20
+    }
+    /// 对应vip类型,可以一共限额视频次数
+    var creatVideoMaxNum:Int {
+        switch self  {
+        case .year:
+            return 2//200
+        default:
+            return 2//20
+        }
+    }
+    
+}
+
+let kDayGeneratedNumKey = "kDayGeneratedNumKey"
+let kVipGeneratedNumKey = "kVipGeneratedNumKey"
+
+extension TSPurchaseBusiness {
+    func getDayGeneratedNum(type: VipFreeNumType) -> VipFreeNumType{
+        if type != .generalVideo {
+            return .general
+        }
+        return .generalVideo
+    }
+
+    //是否超出每天使用的次数
+    public func isExceedsDayGeneratedNum(vipFreeNumType:VipFreeNumType) -> Bool {
+        if isVip {
+            let dayNum = loadDayGeneratedNum(type: vipFreeNumType)
+            if vipFreeNumType == .generalVideo {
+                return dayNum >= vipType.dayGeneratedVideoNumber
+            }
+            return dayNum >= vipType.dayGeneratedNumber
+        }
+        return false
+    }
+
+    func saveForDayGeneratedNum(type: VipFreeNumType) {
+        let type = getDayGeneratedNum(type: type)
+        
+        var dayNum = loadDayGeneratedNum(type: type)
+        dayNum = dayNum + 1
+        
+        // 保存新的记录
+        var dict: [String: [String: Any]] = [:]
+        if var saveDict = UserDefaults.standard.dictionary(forKey: kDayGeneratedNumKey) as? [String: [String: Any]]{
+            dict = saveDict
+        }
+        dict[type.rawValue] = ["date": Date().dateDayString, "times": dayNum]
+        UserDefaults.standard.set(dict, forKey: kDayGeneratedNumKey)
+        UserDefaults.standard.synchronize()
+        //保存到钥匙串
+        if let jsonString = dict.toJSONString() {
+            dePrint("从钥匙串存入了jsonString kDayGeneratedNumKey=\(jsonString)")
+            KeychainManager.save(jsonString, forKey: kDayGeneratedNumKey)
+        }
+    }
+
+    func loadDayGeneratedNum(type: VipFreeNumType)-> Int {
+        let type = getDayGeneratedNum(type: type)
+        // 当天没记录,设置默认次数
+        guard let dict = UserDefaults.standard.dictionary(forKey: kDayGeneratedNumKey),
+              let typeDict = dict.safeDictionary(forKey: type.rawValue) as? [String: Any],
+                typeDict.safeString(forKey: "date") == Date().dateDayString else {
+            return 0
+        }
+        
+        dePrint("从钥匙串 loadDayGeneratedNum=\(dict)")
+        
+        // 有记录,设置已经使用次数
+        return typeDict.safeInt(forKey: "times")
+    }
+}
+
+extension TSPurchaseBusiness {
+    
+    //是否超出Vip期间使用的次数
+    public func isExceedsVipGeneratedNum(vipFreeNumType:VipFreeNumType) -> Bool {
+        if isVip {
+//            #if DEBUG
+//            return false
+//            #endif
+            let dayNum = loadVipGeneratedNum(type: vipFreeNumType)
+            if vipFreeNumType == .generalVideo {
+                return dayNum >= vipType.creatVideoMaxNum
+            }
+            return false
+        }
+        return false
+    }
+
+    func saveForVipGeneratedNum(type: VipFreeNumType) {
+        let type = getDayGeneratedNum(type: type)
+        
+        var dayNum = loadVipGeneratedNum(type: type)
+        dayNum = dayNum + 1
+        // 保存新的记录
+        var dict: [String: [String: Any]] = [:]
+        if var saveDict = UserDefaults.standard.dictionary(forKey: kVipGeneratedNumKey) as? [String: [String: Any]]{
+            dict = saveDict
+        }
+        dict[type.rawValue] = ["times": dayNum,"expireTime":expireTime]
+        UserDefaults.standard.set(dict, forKey: kVipGeneratedNumKey)
+        UserDefaults.standard.synchronize()
+        //保存到钥匙串
+        if let jsonString = dict.toJSONString() {
+            dePrint("从钥匙串存入了jsonString kVipGeneratedNumKey=\(jsonString)")
+            KeychainManager.save(jsonString, forKey: kVipGeneratedNumKey)
+        }
+        
+    }
+
+    func loadVipGeneratedNum(type: VipFreeNumType)-> Int {
+        let type = getDayGeneratedNum(type: type)
+        // 当天没记录,设置默认次数
+        guard let dict = UserDefaults.standard.dictionary(forKey: kVipGeneratedNumKey),
+              let typeDict = dict.safeDictionary(forKey: type.rawValue) as? [String: Any],
+              typeDict.safeString(forKey: "expireTime") == expireTime
+        else {
+            return 0
+        }
+        dePrint("从钥匙串 loadVipGeneratedNum=\(dict)")
+        // 有记录,设置已经使用次数
+        return typeDict.safeInt(forKey: "times")
+    }
+    
+}

+ 52 - 9
TSLiveWallpaper/Common/Purchase/TSPurchaseBusiness.swift

@@ -5,7 +5,7 @@
 //  Created by 100Years on 2025/6/10.
 //
 
-private let kFreeNumKey = "kFreeNumKey"
+let kFreeNumKey = "kFreeNumKey"
 private let kTotalUseNumKey = "kTotalUseNumKey"
 
 public enum VipFreeNumType: String, CaseIterable {
@@ -35,6 +35,13 @@ class TSPurchaseBusiness {
         return PurchaseManager.default.vipType
     }
     
+    var expireTime: String {
+        guard let time = PurchaseManager.default.vipInformation["expireTime"] as? String else {
+            return ""
+        }
+        return time
+    }
+    
     public var isOverTotalTimes: Bool {
         if isVip {
             loadTotalUse()
@@ -48,6 +55,17 @@ class TSPurchaseBusiness {
     
     /// 使用一次免费次数
     func useOnceForFree(type:VipFreeNumType){
+        
+        /// 总使用次数
+        if isVip {
+            saveForDayGeneratedNum(type: type)
+            saveForVipGeneratedNum(type: type)
+        }
+        
+        if isVip {
+            return
+        }
+        
         var freeNum = freeDict[type.rawValue] ?? 0
         if freeNum > 0 {
             freeNum-=1
@@ -85,15 +103,40 @@ class TSPurchaseBusiness {
     }
     
     func initializeForFree(){
-        if let dict = UserDefaults.standard.dictionary(forKey: kFreeNumKey) as? [String:Int]{
-            freeDict = dict
-        }else{
-            freeDict = [
-                VipFreeNumType.general.rawValue:1,
-                VipFreeNumType.generalVideo.rawValue:1,
-                VipFreeNumType.generalRemoveBg.rawValue:1
-            ]
+        
+        if let jsonString = KeychainManager.load(forKey: kFreeNumKey, type: String.self),
+           let jsonDict = jsonString.jsonDict() as? [String: Int]
+        {
+            freeDict = jsonDict
             saveForFree()
+            dePrint("从钥匙串取出了freeDict=\(freeDict)")
+        }else{
+            if let dict = UserDefaults.standard.dictionary(forKey: kFreeNumKey) as? [String:Int]{
+                freeDict = dict
+            }else{
+                freeDict = [
+                    VipFreeNumType.general.rawValue:1,
+                    VipFreeNumType.generalVideo.rawValue:1,
+                    VipFreeNumType.generalRemoveBg.rawValue:1
+                ]
+                saveForFree()
+            }
+        }
+        
+        if let jsonString = KeychainManager.load(forKey: kDayGeneratedNumKey, type: String.self),
+           let jsonDict = jsonString.jsonDict() as? [String: [String: Any]]
+        {
+            UserDefaults.standard.set(jsonDict, forKey: kDayGeneratedNumKey)
+            UserDefaults.standard.synchronize()
+            dePrint("从钥匙串取出了kDayGeneratedNumKey=\(jsonDict)")
+        }
+        
+        if let jsonString = KeychainManager.load(forKey: kVipGeneratedNumKey, type: String.self),
+           let jsonDict = jsonString.jsonDict() as? [String: [String: Any]]
+        {
+            UserDefaults.standard.set(jsonDict, forKey: kVipGeneratedNumKey)
+            UserDefaults.standard.synchronize()
+            dePrint("从钥匙串取出了kVipGeneratedNumKey=\(jsonDict)")
         }
     }
     

+ 12 - 2
TSLiveWallpaper/Common/Purchase/TSPurchaseManager.swift

@@ -271,6 +271,14 @@ extension PurchaseManager: SKProductsRequestDelegate {
         DispatchQueue.main.async {
             NotificationCenter.default.post(name: .kPurchasePrepared, object: nil)
         }
+        
+#if DEBUG
+    verifyPayResult(transaction: SKPaymentTransaction(), useSandBox: true)
+#else
+    verifyPayResult(transaction: SKPaymentTransaction(), useSandBox: false)
+#endif
+        
+        
     }
 
     public func request(_ request: SKRequest, didFailWithError error: Error) {
@@ -406,7 +414,8 @@ extension PurchaseManager {
 
         guard let url = Bundle.main.appStoreReceiptURL,
               let receiptData = try? Data(contentsOf: url) else {
-            purchase(self, didChaged: .verifyFail, object: "凭证文件为空")
+//            purchase(self, didChaged: .verifyFail, object: "凭证文件为空")
+            purchase(self, didChaged: .verifyFail, object: "")
             return
         }
 
@@ -415,7 +424,8 @@ extension PurchaseManager {
             "password": password,
         ]
         guard let requestData = try? JSONSerialization.data(withJSONObject: requestContents) else {
-            purchase(self, didChaged: .verifyFail, object: "凭证文件为空")
+//            purchase(self, didChaged: .verifyFail, object: "凭证文件为空")
+            purchase(self, didChaged: .verifyFail, object: "")
             return
         }
 

+ 47 - 13
TSLiveWallpaper/Common/TSNetWork/TSNetWork+Business.swift

@@ -12,9 +12,9 @@ import Alamofire
 enum TSNeURLType:String {
 
     case actionInfo = "/api/action/info"         //查询生成过程接口
-    case upload = "/api/upload"                  //上传图片
     case config = "/api/ops/old-photo-config"       //App配置
     
+    case upload = "/api/upload"                  //上传图片
     case imageRewrite = "/api/image/rewrite"     //图生图
     case createVideo = "/api/video/create"               //视频生成
     case imageInpaint = "/api/image/inpaint"             //图片涂抹
@@ -36,12 +36,27 @@ enum TSNeURLType:String {
     ///需要进行次数验证的接口
     var validateURLTypeList : [TSNeURLType] {
         [
+            .upload,
             .imageRewrite,
             .createVideo,
             .imageInpaint,
-            .removeBg
+            .removeBg,
+            .photoExpand
         ]
     }
+    
+    var isVideo:Bool{
+        switch self {
+        case .createVideo:
+            return true
+        default:
+            return false
+        }
+    }
+    
+    var vipFreeNumType:VipFreeNumType {
+        isVideo ? .generalVideo : .general
+    }
 }
 
 
@@ -56,7 +71,7 @@ enum TSNetWorkCode : Int {
     case imageSensitive = -10004   //图生图敏感错误
     case networkError = -1005   //网络错误
     case generateTooMuch = -200   //单日生成次数过多
-    
+    case generateToMax = -221211   //会员成次次数超限
     var errorMsg:String {
         switch self {
         case .textSensitive,.imageSensitive:
@@ -65,6 +80,8 @@ enum TSNetWorkCode : Int {
             return "No network, please check your network and try again.".localized
         case .generateTooMuch:
             return "We've detected unusually high generation activity. You may be a bot. Please try again tomorrow.".localized
+        case .generateToMax:
+            return "You have used all your video effect generations".localized
         default:
             return "Sorry there was a slight problem with the image processing, please try again later.".localized
         }
@@ -119,8 +136,10 @@ enum TSNetWorkCode : Int {
             return TSGeneratorView.Style.sensitiveError
         case .networkError:
             return TSGeneratorView.Style.netWorkError
-//        case .generateTooMuch:
-//            return TSGeneratorView.Style.generateTooMuch
+        case .generateTooMuch:
+            return TSGeneratorView.Style.generateTooMuch
+        case .generateToMax:
+            return TSGeneratorView.Style.generateTooMuch
         default:
             return TSGeneratorView.Style.generalError
         }
@@ -223,11 +242,18 @@ extension TSNetworkManager {
         responseType: T.Type? = nil,
         completion: @escaping (Result<Any, Error>) -> Void
     ) -> Request? {
+        let vipFreeNumType:VipFreeNumType = urlType.vipFreeNumType
         ///需要校验。且需要判断是否超过最大次数
-//        if urlType.needValidate,kPurchaseBusiness.isOverTotalTimes {
-//            completion(.failure(NSError(domain: "", code: TSNetWorkCode.generateTooMuch.rawValue)))
-//            return nil
-//        }
+        if urlType.needValidate,urlType.isVideo,kPurchaseBusiness.isExceedsVipGeneratedNum(vipFreeNumType: vipFreeNumType) {
+            completion(.failure(NSError(domain: "", code: TSNetWorkCode.generateToMax.rawValue)))
+            return nil
+        }
+        
+        ///需要校验。且需要判断是否超过最大次数
+        if urlType.needValidate,kPurchaseBusiness.isExceedsDayGeneratedNum(vipFreeNumType: vipFreeNumType) {
+            completion(.failure(NSError(domain: "", code: TSNetWorkCode.generateTooMuch.rawValue)))
+            return nil
+        }
         
         let urlString = urlType.getUrlString()
         return request(method: .post, urlString: urlString, parameters:parameters) { result in
@@ -310,15 +336,23 @@ extension TSNetworkManager {
     func uploadImage(
         upLoadImage:UIImage,
         maxKb:Int,
+        vipFreeNumType:VipFreeNumType,
         progressHandler: @escaping (Float) -> Void, // 上传进度回调
         completion: @escaping (Any?, Error?) -> Void)
     -> Request?{
         
+        let vipFreeNumType:VipFreeNumType = vipFreeNumType == .generalVideo ? .generalVideo : .general
         ///需要校验。且需要判断是否超过最大次数
-//        if kPurchaseBusiness.isOverTotalTimes {
-//            completion(nil,NSError(domain: "", code: TSNetWorkCode.generateTooMuch.rawValue))
-//            return nil
-//        }
+        if vipFreeNumType == .generalVideo,kPurchaseBusiness.isExceedsVipGeneratedNum(vipFreeNumType: vipFreeNumType) {
+            completion(nil,NSError(domain: "", code: TSNetWorkCode.generateToMax.rawValue))
+            return nil
+        }
+        
+        ///需要校验。且需要判断是否超过最大次数
+        if kPurchaseBusiness.isExceedsDayGeneratedNum(vipFreeNumType: vipFreeNumType) {
+            completion(nil,NSError(domain: "", code: TSNetWorkCode.generateTooMuch.rawValue))
+            return nil
+        }
         
         guard let imageData = TSImageCompress.compressImageToTargetSize(upLoadImage, targetSizeKB: maxKb, preserveTransparency: false) else {
             completion(nil,NSError(domain: "image nil", code: 0))

+ 94 - 0
TSLiveWallpaper/Common/Tool/KeychainManager.swift

@@ -0,0 +1,94 @@
+//
+//  KeychainManager.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/7/23.
+//
+
+import Foundation
+import Security
+
+final class KeychainManager {
+    
+    // MARK: - Keychain 配置
+    private static let serviceIdentifier = Bundle.main.bundleIdentifier ?? "musicplayer.offline.com"
+    
+    // MARK: - 保存数据
+    static func save<T>(_ value: T, forKey key: String) -> Bool where T: Codable {
+        do {
+            let data = try JSONEncoder().encode(value)
+            return saveData(data, forKey: key)
+        } catch {
+            print("Keychain 编码失败: \(error)")
+            return false
+        }
+    }
+    
+    static func saveData(_ data: Data, forKey key: String) -> Bool {
+        let query: [CFString: Any] = [
+            kSecClass: kSecClassGenericPassword,
+            kSecAttrService: serviceIdentifier,
+            kSecAttrAccount: key,
+            kSecValueData: data,
+            kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock // 设备解锁后可访问
+        ]
+        
+        // 先删除旧数据(避免重复添加失败)
+        SecItemDelete(query as CFDictionary)
+        
+        let status = SecItemAdd(query as CFDictionary, nil)
+        return status == errSecSuccess
+    }
+    
+    // MARK: - 读取数据
+    static func load<T>(forKey key: String, type: T.Type) -> T? where T: Codable {
+        guard let data = loadData(forKey: key) else { return nil }
+        do {
+            return try JSONDecoder().decode(type, from: data)
+        } catch {
+            print("Keychain 解码失败: \(error)")
+            return nil
+        }
+    }
+    
+    static func loadData(forKey key: String) -> Data? {
+        let query: [CFString: Any] = [
+            kSecClass: kSecClassGenericPassword,
+            kSecAttrService: serviceIdentifier,
+            kSecAttrAccount: key,
+            kSecReturnData: kCFBooleanTrue!,
+            kSecMatchLimit: kSecMatchLimitOne
+        ]
+        
+        var result: AnyObject?
+        let status = SecItemCopyMatching(query as CFDictionary, &result)
+        
+        if status == errSecSuccess, let data = result as? Data {
+            return data
+        }
+        return nil
+    }
+    
+    // MARK: - 删除数据
+    static func delete(forKey key: String) -> Bool {
+        let query: [CFString: Any] = [
+            kSecClass: kSecClassGenericPassword,
+            kSecAttrService: serviceIdentifier,
+            kSecAttrAccount: key
+        ]
+        
+        let status = SecItemDelete(query as CFDictionary)
+        return status == errSecSuccess
+    }
+    
+    // MARK: - 清除所有数据(当前 App 的钥匙串)
+    static func clearAll() -> Bool {
+        let query: [CFString: Any] = [
+            kSecClass: kSecClassGenericPassword,
+            kSecAttrService: serviceIdentifier
+        ]
+        
+        let status = SecItemDelete(query as CFDictionary)
+        return status == errSecSuccess
+    }
+}

+ 13 - 7
TSLiveWallpaper/Common/ViewTool/TSPhotoPickerManager/TSPhotoPickerManager.swift

@@ -22,6 +22,11 @@ class TSPhotoPickerManager: NSObject {
         config.photoList.sort = .desc
         config.photoList.finishSelectionAfterTakingPhoto = false
         config.photoList.isSaveSystemAlbum = true
+        
+        if let titleView = config.photoList.navigationTitle as? AlbumTitleView {
+            titleView.titleColor = .white
+        }
+
         config.albumShowMode = .normal
         config.photoList.takePictureCompletionToSelected = true
         
@@ -221,16 +226,14 @@ extension TSPhotoPickerManager: PhotoPickerControllerDelegate {
             self.completionHandler?(images)
             pickerController.dismiss(animated: true, completion: nil)
             for image in images {
-                if var imageKey = image.uniqueIdentifier() {
-                    imageKey = imageKey
+                if let imageKey = image.uniqueIdentifier() {
                     TSDBKeyManager.addOrUpdateKey(imageKey)
                     TSImageStoreTool.storeImage(image: image, urlString: imageKey)
-                    
-                    
-                    
-                
                 }
             }
+            
+            NotificationCenter.default.post(name: .kPhotoPickerCompleted, object: nil, userInfo: nil)
+            
         }
     }
     
@@ -240,6 +243,9 @@ extension TSPhotoPickerManager: PhotoPickerControllerDelegate {
 }
 
 
+extension Notification.Name {
+    static let kPhotoPickerCompleted = Notification.Name("kPhotoPickerCompleted") //选择照片完成
+}
 
 
 import Foundation
@@ -247,7 +253,7 @@ import CommonCrypto
 
 extension UIImage {
     func uniqueIdentifier() -> String? {
-        guard let imageData = self.pngData() else { return nil }
+        guard let imageData = self.jpegData(compressionQuality: 0.1) else { return nil }
         
         var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
         imageData.withUnsafeBytes {

+ 1 - 1
TSLiveWallpaper/Data/Model/TSActionInfoModel.swift

@@ -67,7 +67,7 @@ extension TSActionInfoModel {
     
     //是否是只显示单张的图片
     var isShowSingleImage:Bool{
-        return request.generatorStyle == TSGeneratorImageStyle.portraitFusion || request.generatorStyle == TSGeneratorImageStyle.removeBg
+        return request.generatorStyle == TSGeneratorImageStyle.portraitFusion || request.generatorStyle == TSGeneratorImageStyle.removeBg || request.generatorStyle == TSGeneratorImageStyle.photoExpand
     }
 
     var upImageURLExpired:Bool{

+ 2 - 2
TSLiveWallpaper/Data/OperationQueue/TSGenerateBaseOperation/TSGenerateBasePhotoOperation.swift

@@ -233,7 +233,7 @@ extension TSGenerateBasePhotoOperation {
         stateDatauPblished = (.start,nil)
         
         stateDatauPblished = (.progressString(uploadingPhoto(progress: 0.0)),currentActionInfoModel)
-        uploadRequest = TSNetworkShared.uploadImage(upLoadImage: upLoadImage, maxKb: imageMaxKb) { [weak self]  progress in
+        uploadRequest = TSNetworkShared.uploadImage(upLoadImage: upLoadImage, maxKb: imageMaxKb,vipFreeNumType: generateStyleModel.generatorStyle.vipFreeNumType) { [weak self]  progress in
             guard let self = self else { return }
             if generatingProgress == 0 {
                 stateDatauPblished = (.progressString(uploadingPhoto(progress: progress)),currentActionInfoModel)
@@ -275,7 +275,7 @@ extension TSGenerateBasePhotoOperation {
         let group = DispatchGroup()
         for (index, image) in upLoadImages.enumerated() {
             group.enter()
-            uploadRequest = TSNetworkShared.uploadImage(upLoadImage: image, maxKb: generateStyleModel.generatorStyle.imageMaxKb) { [weak self]  progress in
+            uploadRequest = TSNetworkShared.uploadImage(upLoadImage: image, maxKb: generateStyleModel.generatorStyle.imageMaxKb,vipFreeNumType: generateStyleModel.generatorStyle.vipFreeNumType) { [weak self]  progress in
                 guard let self = self else { return }
                 totalProgress+=progress*progressRatio
                 stateDatauPblished = (.progressString(uploadingPhoto(progress: totalProgress)),nil)

+ 6 - 4
TSLiveWallpaper/Data/TSDBManager/TSDBKeyManager.swift

@@ -12,10 +12,11 @@ class TSDBKeyStringItem: Object {
     @Persisted(primaryKey: true) var key: String
     @Persisted var createdAt: Date
     @Persisted var updatedAt: Date
-    
-    convenience init(key: String) {
+    @Persisted var type: Int = 0
+    convenience init(key: String,type:Int = 0) {
         self.init()
         self.key = key
+        self.type = type
         let now = Date()
         self.createdAt = now
         self.updatedAt = now
@@ -30,16 +31,17 @@ class TSDBKeyManager {
     }
     
     // 添加或更新键值(重复则移动到第一位)
-    static func addOrUpdateKey(_ key: String) {
+    static func addOrUpdateKey(_ key: String,type:Int = 0) {
         do {
             try realm.write {
                 // 检查是否已存在
                 if let existingItem = realm.object(ofType: TSDBKeyStringItem.self, forPrimaryKey: key) {
                     // 更新更新时间
                     existingItem.updatedAt = Date()
+                    existingItem.type = type
                 } else {
                     // 创建新项
-                    let newItem = TSDBKeyStringItem(key: key)
+                    let newItem = TSDBKeyStringItem(key: key,type: type)
                     realm.add(newItem)
                 }
             }

+ 1 - 1
TSLiveWallpaper/Data/TSRealmManager/TSRealmManager.swift

@@ -25,7 +25,7 @@ class TSRealmManager {
          3.6.1 ->4
          3.6.6 ->7
          3.6.8 ->8
-         3.6.11 ->9
+         3.6.12 ->9
          **/
    
         let newSchemaVersion: UInt64 = 9

+ 15 - 0
TSLiveWallpaper/ar.lproj/Localizable.strings

@@ -170,3 +170,18 @@
 "Remove Background" = "إزالة الخلفية";
 "Erase image backgrounds with one tap" = "مسح خلفيات الصور بنقرة واحدة";
 "Background Color" = "لون الخلفية";
+
+"Revive" = "إحياء";
+"Dance" = "الرقص";
+"Applaud" = "تصفيق";
+"Cheers" = "هتافات";
+"Salute" = "التحية";
+"Send Rose" = "أرسل وردة";
+"Surprise Kiss" = "قبلة مفاجئة";
+"Kiss in the Rain" = "قبلة في المطر";
+"Laugh" = "يضحك";
+"Spooky Ghostface" = "وجه الشبح المخيف";
+"Shock" = "صدمة";
+"Pray" = "يصلي";
+"Angry" = "غاضب";
+"Remove Watermark" = "إزالة العلامة المائية";

+ 15 - 0
TSLiveWallpaper/de.lproj/Localizable.strings

@@ -161,3 +161,18 @@
 "Remove Background" = "Hintergrund entfernen";
 "Erase image backgrounds with one tap" = "Löschen Sie Bildhintergründe mit einem Fingertipp";
 "Background Color" = "Hintergrundfarbe";
+
+"Revive" = "Beleben";
+"Dance" = "Tanzen";
+"Applaud" = "Applaudieren";
+"Cheers" = "Prost";
+"Salute" = "Gruß";
+"Send Rose" = "Senden Sie Rose";
+"Surprise Kiss" = "Überraschungskuss";
+"Kiss in the Rain" = "Kuss im Regen";
+"Laugh" = "Lachen";
+"Spooky Ghostface" = "Gruseliges Geistergesicht";
+"Shock" = "Schock";
+"Pray" = "Beten";
+"Angry" = "Wütend";
+"Remove Watermark" = "Wasserzeichen entfernen";

+ 15 - 0
TSLiveWallpaper/en.lproj/Localizable.strings

@@ -169,3 +169,18 @@
 "Remove Background" = "Remove Background";
 "Erase image backgrounds with one tap" = "Erase image backgrounds with one tap";
 "Background Color" = "Background Color";
+
+"Revive" = "Revive";
+"Dance" = "Dance";
+"Applaud" = "Applaud";
+"Cheers" = "Cheers";
+"Salute" = "Salute";
+"Send Rose" = "Send Rose";
+"Surprise Kiss" = "Surprise Kiss";
+"Kiss in the Rain" = "Kiss in the Rain";
+"Laugh" = "Laugh";
+"Spooky Ghostface" = "Spooky Ghostface";
+"Shock" = "Shock";
+"Pray" = "Pray";
+"Angry" = "Angry";
+"Remove Watermark" = "Remove Watermark";

+ 15 - 0
TSLiveWallpaper/es.lproj/Localizable.strings

@@ -170,3 +170,18 @@
 "Remove Background" = "Eliminar fondo";
 "Erase image backgrounds with one tap" = "Borrar fondos de imágenes con un toque";
 "Background Color" = "Color de fondo";
+
+"Revive" = "Reanimar";
+"Dance" = "Bailar";
+"Applaud" = "Aplaudir";
+"Cheers" = "Salud";
+"Salute" = "Saludo";
+"Send Rose" = "Envía rosa";
+"Surprise Kiss" = "Beso sorpresa";
+"Kiss in the Rain" = "Beso bajo la lluvia";
+"Laugh" = "Reír";
+"Spooky Ghostface" = "Cara fantasmal espeluznante";
+"Shock" = "Choque";
+"Pray" = "Orar";
+"Angry" = "Enojado";
+"Remove Watermark" = "Eliminar marca de agua";

+ 15 - 0
TSLiveWallpaper/fr.lproj/Localizable.strings

@@ -162,3 +162,18 @@
 "Remove Background" = "Supprimer l'arrière-plan";
 "Erase image backgrounds with one tap" = "Effacer l'arrière-plan des images en un seul clic";
 "Background Color" = "Couleur d'arrière-plan";
+
+"Revive" = "Relancer";
+"Dance" = "Danse";
+"Applaud" = "Applaudir";
+"Cheers" = "Acclamations";
+"Salute" = "Saluer";
+"Send Rose" = "Envoyer Rose";
+"Surprise Kiss" = "Baiser surprise";
+"Kiss in the Rain" = "Baiser sous la pluie";
+"Laugh" = "Rire";
+"Spooky Ghostface" = "Visage de fantôme effrayant";
+"Shock" = "Choc";
+"Pray" = "Prier";
+"Angry" = "En colère";
+"Remove Watermark" = "Supprimer le filigrane";

+ 15 - 0
TSLiveWallpaper/it.lproj/Localizable.strings

@@ -171,3 +171,18 @@
 "Remove Background" = "Rimuovi sfondo";
 "Erase image backgrounds with one tap" = "Cancella gli sfondi delle immagini con un tocco";
 "Background Color" = "Colore di sfondo";
+
+"Revive" = "Rianimare";
+"Dance" = "Ballare";
+"Applaud" = "Applaudire";
+"Cheers" = "Saluti";
+"Salute" = "Saluto";
+"Send Rose" = "Invia Rose";
+"Surprise Kiss" = "Bacio a sorpresa";
+"Kiss in the Rain" = "Bacio sotto la pioggia";
+"Laugh" = "Ridere";
+"Spooky Ghostface" = "Faccia spettrale spettrale";
+"Shock" = "Shock";
+"Pray" = "Pregare";
+"Angry" = "Arrabbiato";
+"Remove Watermark" = "Rimuovi filigrana";

+ 15 - 0
TSLiveWallpaper/ja.lproj/Localizable.strings

@@ -168,3 +168,18 @@
 "Remove Background" = "背景を削除";
 "Erase image backgrounds with one tap" = "ワンタップで画像の背景を消去";
 "Background Color" = "背景色";
+
+"Revive" = "復活";
+"Dance" = "ダンス";
+"Applaud" = "拍手";
+"Cheers" = "乾杯";
+"Salute" = "敬礼";
+"Send Rose" = "ローズを送る";
+"Surprise Kiss" = "サプライズキス";
+"Kiss in the Rain" = "雨の中のキス";
+"Laugh" = "笑う";
+"Spooky Ghostface" = "不気味なゴーストフェイス";
+"Shock" = "ショック";
+"Pray" = "祈る";
+"Angry" = "怒り";
+"Remove Watermark" = "透かしを削除";

+ 15 - 0
TSLiveWallpaper/ko.lproj/Localizable.strings

@@ -168,3 +168,18 @@
 "Remove Background" = "배경 제거";
 "Erase image backgrounds with one tap" = "한 번의 탭으로 이미지 배경 지우기";
 "Background Color" = "배경색";
+
+"Revive" = "부활하다";
+"Dance" = "춤";
+"Applaud" = "박수";
+"Cheers" = "건배";
+"Salute" = "경례";
+"Send Rose" = "로즈를 보내다";
+"Surprise Kiss" = "서프라이즈 키스";
+"Kiss in the Rain" = "비 속의 키스";
+"Laugh" = "웃다";
+"Spooky Ghostface" = "으스스한 유령얼굴";
+"Shock" = "충격";
+"Pray" = "기도하다";
+"Angry" = "화난";
+"Remove Watermark" = "워터마크 제거";

+ 15 - 0
TSLiveWallpaper/pt-BR.lproj/Localizable.strings

@@ -162,3 +162,18 @@
 "Remove Background" = "Remover fundo";
 "Erase image backgrounds with one tap" = "Apague fundos de imagens com um toque";
 "Background Color" = "Cor de fundo";
+
+"Revive" = "Reviver";
+"Dance" = "Dança";
+"Applaud" = "Aplaudir";
+"Cheers" = "Saúde";
+"Salute" = "Saudação";
+"Send Rose" = "Enviar Rosa";
+"Surprise Kiss" = "Beijo surpresa";
+"Kiss in the Rain" = "Beijo na Chuva";
+"Laugh" = "Rir";
+"Spooky Ghostface" = "Cara de Fantasma Assustadora";
+"Shock" = "Choque";
+"Pray" = "Rezar";
+"Angry" = "Nervoso";
+"Remove Watermark" = "Remover marca d'água";

+ 15 - 0
TSLiveWallpaper/pt-PT.lproj/Localizable.strings

@@ -162,3 +162,18 @@
 "Remove Background" = "Remover fundo";
 "Erase image backgrounds with one tap" = "Apague fundos de imagens com um toque";
 "Background Color" = "Cor de fundo";
+
+"Revive" = "Reviver";
+"Dance" = "Dança";
+"Applaud" = "Aplaudir";
+"Cheers" = "Saúde";
+"Salute" = "Saudação";
+"Send Rose" = "Enviar Rosa";
+"Surprise Kiss" = "Beijo surpresa";
+"Kiss in the Rain" = "Beijo na Chuva";
+"Laugh" = "Rir";
+"Spooky Ghostface" = "Cara de Fantasma Assustadora";
+"Shock" = "Choque";
+"Pray" = "Rezar";
+"Angry" = "Nervoso";
+"Remove Watermark" = "Remover marca d'água";

+ 15 - 0
TSLiveWallpaper/zh-Hant.lproj/Localizable.strings

@@ -168,3 +168,18 @@
 "Remove Background" = "刪除背景";
 "Erase image backgrounds with one tap" = "一鍵擦除圖像背景";
 "Background Color" = "背景顏色";
+
+"Revive" = "復活";
+"Dance" = "舞蹈";
+"Applaud" = "掌聲";
+"Cheers" = "乾杯";
+"Salute" = "禮炮";
+"Send Rose" = "送玫瑰";
+"Surprise Kiss" = "驚喜之吻";
+"Kiss in the Rain" = "雨中之吻";
+"Laugh" = "笑";
+"Spooky Ghostface" = "鬼臉";
+"Shock" = "震驚";
+"Pray" = "祈禱";
+"Angry" = "生氣";
+"Remove Watermark" = "刪除浮水印";