Kaynağa Gözat

[feat]完成ios键盘1.0版本开发

Destiny 6 ay önce
ebeveyn
işleme
a4fb148081
34 değiştirilmiş dosya ile 757 ekleme ve 144 silme
  1. 4 4
      ios/AiKeyboard/Info.plist
  2. 191 10
      ios/AiKeyboard/KeyboardViewController.swift
  3. 22 0
      ios/AiKeyboard/Media.xcassets/keyboard_close_btn.imageset/Contents.json
  4. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_close_btn.imageset/keyboard_close_btn@2x.png
  5. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_close_btn.imageset/keyboard_close_btn@3x.png
  6. 22 0
      ios/AiKeyboard/Media.xcassets/keyboard_paste_tip_close_icon.imageset/Contents.json
  7. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_paste_tip_close_icon.imageset/keyboard_paste_tip_close_icon@2x.png
  8. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_paste_tip_close_icon.imageset/keyboard_paste_tip_close_icon@3x.png
  9. 22 0
      ios/AiKeyboard/Media.xcassets/keyboard_tip_settings.imageset/Contents.json
  10. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_tip_settings.imageset/keyboard_tip_settings@2x.png
  11. BIN
      ios/AiKeyboard/Media.xcassets/keyboard_tip_settings.imageset/keyboard_tip_settings@3x.png
  12. 17 4
      ios/AiKeyboard/Network/KeyboardApi.swift
  13. 71 2
      ios/AiKeyboard/View/MainView/KeyboardBaseView.swift
  14. 6 4
      ios/AiKeyboard/View/MainView/KeyboardHelpView.swift
  15. 26 15
      ios/AiKeyboard/View/MainView/KeyboardPrologueView.swift
  16. 22 14
      ios/AiKeyboard/View/MainView/KeyboardTeachView.swift
  17. 1 1
      ios/AiKeyboard/View/PopView/KeyboardExchangeView.swift
  18. 10 1
      ios/AiKeyboard/View/PopView/KeyboardLoginTipView.swift
  19. 14 0
      ios/AiKeyboard/View/PopView/KeyboardMenuView.swift
  20. 136 0
      ios/AiKeyboard/View/PopView/KeyboardTipsPopView.swift
  21. 22 3
      ios/AiKeyboard/View/PopView/KeyboardVipTipView.swift
  22. 19 7
      ios/KeyboardSharedDataManager.swift
  23. 2 2
      ios/Podfile.lock
  24. 14 4
      ios/Runner.xcodeproj/project.pbxproj
  25. 6 0
      ios/Runner/AppDelegate.swift
  26. 17 1
      ios/Runner/FlutterMethodChannelManager.swift
  27. 25 0
      ios/Runner/Settings.bundle/Root.plist
  28. BIN
      ios/Runner/Settings.bundle/en.lproj/Root.strings
  29. 0 0
      ios/tbmct.ttf
  30. 3 38
      lib/data/repository/account_repository.dart
  31. 4 0
      lib/device/atmob_platform_info.dart
  32. 2 2
      lib/main.dart
  33. 79 0
      lib/utils/method_chanel_ios_util.dart
  34. 0 32
      lib/utils/navitigation_handler.dart

+ 4 - 4
ios/AiKeyboard/Info.plist

@@ -2,12 +2,12 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>ITSAppUsesNonExemptEncryption</key>
+	<false/>
 	<key>LSApplicationQueriesSchemes</key>
 	<array>
 		<string>com.qihuan.zhuiaijianpan</string>
 	</array>
-	<key>ITSAppUsesNonExemptEncryption</key>
-	<false/>
 	<key>NSAppTransportSecurity</key>
 	<dict>
 		<key>NSAllowsArbitraryLoads</key>
@@ -22,7 +22,7 @@
 			<key>PrefersRightToLeft</key>
 			<false/>
 			<key>PrimaryLanguage</key>
-			<string>en-US</string>
+			<string>zh-CN</string>
 			<key>RequestsOpenAccess</key>
 			<true/>
 		</dict>
@@ -33,7 +33,7 @@
 	</dict>
 	<key>UIAppFonts</key>
 	<array>
-		<string>淘宝买菜体.ttf</string>
+		<string>tbmct.ttf</string>
 	</array>
 </dict>
 </plist>

+ 191 - 10
ios/AiKeyboard/KeyboardViewController.swift

@@ -19,7 +19,7 @@ enum KeyboardType: Int {
 class KeyboardViewController: UIInputViewController {
     
     struct UX {
-        static let keyboardHeight = 292.0
+        static let keyboardHeight = 318.0
     }
     
     // 选择的键盘类型
@@ -63,6 +63,11 @@ class KeyboardViewController: UIInputViewController {
         let animate = LottieAnimationView(name: "heart")
         animate.loopMode = .loop
         animate.backgroundColor = .clear
+        
+        animate.isUserInteractionEnabled = true
+        let tap = UITapGestureRecognizer(target: self, action: #selector(heartBtnClickAction))
+        animate.addGestureRecognizer(tap)
+        
         return animate
     }()
     
@@ -72,6 +77,11 @@ class KeyboardViewController: UIInputViewController {
         label.text = "30%"
         label.font = .boldSystemFont(ofSize: 10)
         label.textColor = .white
+        
+        label.isUserInteractionEnabled = true
+        let tap = UITapGestureRecognizer(target: self, action: #selector(heartBtnClickAction))
+        label.addGestureRecognizer(tap)
+        
         return label
     }()
     
@@ -79,6 +89,7 @@ class KeyboardViewController: UIInputViewController {
         
         let button = UIButton()
         button.setImage(UIImage(named: "keyboard_exchange_btn"), for: .normal)
+        button.addTarget(self, action: #selector(exchangeBtnClickAction), for: .touchUpInside)
         return button
     }()
     
@@ -205,6 +216,26 @@ class KeyboardViewController: UIInputViewController {
         view.loginBtnClosure = {
             self.navigateToLogin()
         }
+        view.backBtnClosure = {
+            self.clearPopView()
+            self.loginView.isHidden = true
+            self.nowShowView?.isHidden = false
+        }
+        return view
+    }()
+    
+    lazy var vipView: KeyboardVipTipView = {
+       
+        let view = KeyboardVipTipView()
+        view.isHidden = true
+        view.unlockBtnClosure = {
+            self.navigateToMembership()
+        }
+        view.backBtnClosure = {
+            self.clearPopView()
+            self.vipView.isHidden = true
+            self.nowShowView?.isHidden = false
+        }
         return view
     }()
     
@@ -219,6 +250,8 @@ class KeyboardViewController: UIInputViewController {
         super.viewDidLoad()
         
         setUI()
+
+        checkFullAccess()
         
         KeyboardApi.initParams()
         requestKeyboardList()
@@ -315,7 +348,7 @@ extension KeyboardViewController {
         }
     }
     
-    // 获取键盘人设列表
+    // 获取键盘开场白列表
     func requestPrologueList() {
         
         KeyboardNetworkManager.request(.prologue_list(params: [:])) { response in
@@ -368,7 +401,11 @@ extension KeyboardViewController {
             complete("")
             
             if code == 1006 {
+                // 弹出登录页
                 self.popLoginView()
+            } else if code == 1008 {
+                // 弹出会员页
+                self.popVipView()
             }
         }
     }
@@ -394,7 +431,11 @@ extension KeyboardViewController {
             complete([String]())
             
             if code == 1006 {
+                // 弹出登录页
                 self.popLoginView()
+            } else if code == 1008 {
+                // 弹出会员页
+                self.popVipView()
             }
         }
     }
@@ -418,7 +459,11 @@ extension KeyboardViewController {
             complete([String]())
             
             if code == 1006 {
+                // 弹出登录页
                 self.popLoginView()
+            } else if code == 1008 {
+                // 弹出会员页
+                self.popVipView()
             }
         }
     }
@@ -426,6 +471,19 @@ extension KeyboardViewController {
 
 // MARK: - 点击事件
 extension KeyboardViewController: KeyboardMenuViewDelegate, KeyboardExchangeViewDelegate {
+        
+    // 亲密度按钮点击
+    @objc func heartBtnClickAction() {
+        
+        let url = URL(string: "com.qihuan.zhuiaijianpan:///intimacy")!
+        openURL(url)
+    }
+    
+    // 切换系统键盘
+    @objc func exchangeBtnClickAction() {
+        
+        self.advanceToNextInputMode()
+    }
     
     // 功能按钮选择点击
     @objc func functionBtnClickAction() {
@@ -455,6 +513,20 @@ extension KeyboardViewController: KeyboardMenuViewDelegate, KeyboardExchangeView
         navigateToMembership()
     }
     
+    // 定制人设
+    func diyBtnClickAction() {
+        
+        let url = URL(string: "com.qihuan.zhuiaijianpan:///character/custom")!
+        openURL(url)
+    }
+    
+    // 人设市场
+    func marketBtnClickAction() {
+        
+        let url = URL(string: "com.qihuan.zhuiaijianpan:///character/market")!
+        openURL(url)
+    }
+    
     // 跳转到主应用的登录页面
     @objc func navigateToLogin() {
         let url = URL(string: "com.qihuan.zhuiaijianpan:///login")!
@@ -530,19 +602,37 @@ extension KeyboardViewController: KeyboardMenuViewDelegate, KeyboardExchangeView
 extension KeyboardViewController: KeyboardHelpViewDelegate, KeyboardTeachViewDelegate, KeyboardPrologueViewDelegate {
 
     // 帮聊点击cell
-    func helpCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping (() -> ())) {
+    func helpCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping ((Bool) -> ())) {
+        
+        // 判断是否登录
+        let isLogin = KeyboardSharedDataManager.shared.isLoggedIn()
+        if !isLogin {
+            popLoginView()
+            complete(false)
+            return
+        }
         
         self.requestSuperReply(characterId: characterId, content: content) { text in
             self.insertText(text)
-            complete()
+            complete(true)
         }
     }
     
     // 教你说点击cell
-    func teachCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping (([String]) -> ())) {
+    func teachCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping (([String], Bool, Bool) -> ())) {
         
+        // 判断是否登录
+        let isLogin = KeyboardSharedDataManager.shared.isLoggedIn()
+        if !isLogin {
+            popLoginView()
+            complete([String](), false, false)
+            return
+        } else {
+            complete([String](), true, false)
+        }
+
         self.requestSuperSpeak(characterId: characterId, content: content) { list in
-            complete(list)
+            complete(list, true, true)
         }
     }
     
@@ -553,10 +643,20 @@ extension KeyboardViewController: KeyboardHelpViewDelegate, KeyboardTeachViewDel
     }
     
     // 开场白点击cell
-    func prologueCollectionViewDidSelectItem(name: String, complete: @escaping (([String]) -> ())) {
+    func prologueCollectionViewDidSelectItem(name: String, complete: @escaping (([String], Bool, Bool) -> ())) {
+        
+        // 判断是否登录
+        let isLogin = KeyboardSharedDataManager.shared.isLoggedIn()
+        if !isLogin {
+            popLoginView()
+            complete([String](), false, false)
+            return
+        } else {
+            complete([String](), true, false)
+        }
         
         self.requestSuperPrologue(name: name) { list in
-            complete(list)
+            complete(list, true, true)
         }
     }
     
@@ -584,6 +684,12 @@ extension KeyboardViewController: KeyboardBaseViewDelegate {
             if isShowing {
                 print("系统正在显示剪贴板权限弹窗")
                 self.view.toast(text: "请允许访问剪贴板")
+//                self.teachView.topView.isHidden = false
+                self.helpView.topView.isHidden = false
+                self.helpView.topView.snp.updateConstraints { make in
+                    make.height.equalTo(32)
+                }
+//                self.teachView.topView.isHidden = false
             }
             if let content = self.getPasteboardContent() {
                 print("获取到剪贴板内容: \(content)")
@@ -673,6 +779,9 @@ extension KeyboardViewController: KeyboardBaseViewDelegate {
             
             let currentChangeCount = UIPasteboard.general.changeCount
             if currentChangeCount != initialChangeCount {
+                
+                // 记录访问前的时间
+                let startTime = Date()
                 // 剪贴板计数已变化,检查内容是否真的变化
                 if let copiedString = UIPasteboard.general.string {
                     // 只有当内容真的不同时才处理
@@ -683,10 +792,22 @@ extension KeyboardViewController: KeyboardBaseViewDelegate {
                         initialChangeCount = currentChangeCount
                         // 处理复制的内容
                         self.handleCopiedContent(copiedString)
+                        
+                        // 检查访问后的时间延迟
+                        let timeDelay = Date().timeIntervalSince(startTime)
+                        
+                        // 如果访问剪贴板的时间超过一定阈值,可能是因为系统弹出了权限弹窗
+                        // 通常正常访问剪贴板的时间应该很短,如果超过100毫秒,可能是弹出了弹窗
+                        if timeDelay > 0.1 {
+                            self.openPasteTipView()
+                        } else {
+                            
+                        }
                     } else {
                         // 内容相同或为空,只更新计数
                         initialChangeCount = currentChangeCount
                         print("剪贴板计数变化但内容未变或为空")
+                        self.openPasteTipView()
                     }
                 } else {
                     // 剪贴板内容为nil,更新计数
@@ -759,6 +880,50 @@ extension KeyboardViewController: KeyboardBaseViewDelegate {
 
 extension KeyboardViewController {
     
+    // 打开弹窗
+    func openPasteTipView() {
+        
+        self.helpView.topView.isHidden = false
+        self.helpView.topView.snp.updateConstraints { make in
+            make.height.equalTo(32)
+        }
+        
+        self.teachView.topView.isHidden = false
+        self.teachView.topView.snp.updateConstraints { make in
+            make.height.equalTo(32)
+        }
+        
+        self.prologueView.topView.isHidden = false
+        self.prologueView.topView.snp.updateConstraints { make in
+            make.height.equalTo(32)
+        }
+    }
+    
+    // 检查是否有完全访问权限
+    func checkFullAccess() {
+        
+        let isFullAccess = self.hasFullAccess
+        
+        if !isFullAccess {
+            
+            let tipView = KeyboardTipsPopView()
+            tipView.settingBtnClosure = {
+                
+                if let url = URL(string: UIApplication.openSettingsURLString) {
+                    self.openURL(url)
+                }
+            }
+            tipView.backBtnClosure = {
+                
+            }
+            self.view.addSubview(tipView)
+            tipView.snp.makeConstraints { make in
+                make.left.right.bottom.equalTo(0)
+                make.top.equalTo(56)
+            }
+        }
+    }
+    
     // 跳转登录页
     func popLoginView() {
         
@@ -766,9 +931,18 @@ extension KeyboardViewController {
         self.loginView.isHidden = false
     }
     
+    // 弹出vip页面
+    func popVipView() {
+        
+        self.clearPopView()
+        self.vipView.isHidden = false
+    }
+    
     // 关闭其他弹出页
     func clearPopView() {
         
+        self.vipView.isHidden = true
+        self.loginView.isHidden = true
         self.menuView.isHidden = true
         self.exchangeView.isHidden = true
         self.helpView.isHidden = true
@@ -835,6 +1009,7 @@ extension KeyboardViewController {
         
         if let chooseKeyboard = self.chooseKeyboard {
             self.userChangeLabel.text = chooseKeyboard.name
+            self.heartLabel.text = "\(chooseKeyboard.intimacy ?? 0)%"
         }
     }
     
@@ -861,7 +1036,8 @@ extension KeyboardViewController {
         
         self.view.addSubview(heartLabel)
         heartLabel.snp.makeConstraints { make in
-            make.center.equalTo(heartAnimation.center)
+            make.centerY.equalTo(heartAnimation.snp.centerY).offset(-2)
+            make.centerX.equalTo(heartAnimation.snp.centerX)
         }
         
         self.view.addSubview(exchangeBtn)
@@ -886,7 +1062,7 @@ extension KeyboardViewController {
         self.view.addSubview(prologueView)
         prologueView.snp.makeConstraints { make in
             make.left.right.bottom.equalTo(0)
-            make.top.equalTo(menuBtn.snp.bottom)
+            make.top.equalTo(60)
         }
         
         self.view.addSubview(chooseView)
@@ -914,6 +1090,11 @@ extension KeyboardViewController {
             make.top.left.right.bottom.equalTo(0)
         }
         
+        self.view.addSubview(vipView)
+        vipView.snp.makeConstraints { make in
+            make.top.left.right.bottom.equalTo(0)
+        }
+        
         chooseView.addSubview(functionView)
         functionView.snp.makeConstraints { make in
             make.top.left.equalTo(1)

+ 22 - 0
ios/AiKeyboard/Media.xcassets/keyboard_close_btn.imageset/Contents.json

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

BIN
ios/AiKeyboard/Media.xcassets/keyboard_close_btn.imageset/keyboard_close_btn@2x.png


BIN
ios/AiKeyboard/Media.xcassets/keyboard_close_btn.imageset/keyboard_close_btn@3x.png


+ 22 - 0
ios/AiKeyboard/Media.xcassets/keyboard_paste_tip_close_icon.imageset/Contents.json

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

BIN
ios/AiKeyboard/Media.xcassets/keyboard_paste_tip_close_icon.imageset/keyboard_paste_tip_close_icon@2x.png


BIN
ios/AiKeyboard/Media.xcassets/keyboard_paste_tip_close_icon.imageset/keyboard_paste_tip_close_icon@3x.png


+ 22 - 0
ios/AiKeyboard/Media.xcassets/keyboard_tip_settings.imageset/Contents.json

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

BIN
ios/AiKeyboard/Media.xcassets/keyboard_tip_settings.imageset/keyboard_tip_settings@2x.png


BIN
ios/AiKeyboard/Media.xcassets/keyboard_tip_settings.imageset/keyboard_tip_settings@3x.png


+ 17 - 4
ios/AiKeyboard/Network/KeyboardApi.swift

@@ -91,15 +91,28 @@ struct KeyboardApi {
         }
         
         // 获取 设置uuid 并更新
-        if let uuid = UIDevice.current.identifierForVendor?.uuidString {
-            KeyboardApi.updateIDFV(idfv: uuid)
-            debugPrint("IDFV ID: \(uuid)")
+//        if let uuid = UIDevice.current.identifierForVendor?.uuidString {
+//            KeyboardApi.updateIDFV(idfv: uuid)
+//            debugPrint("IDFV ID: \(uuid)")
+//        } else {
+//            debugPrint("获取 IDFV ID 参数失败")
+//        }
+        
+        if let idfv = KeyboardSharedDataManager.shared.getInitIDFV() {
+            KeyboardApi.updateIDFV(idfv: idfv)
+            debugPrint("IDFV ID: \(idfv)")
         } else {
             debugPrint("获取 IDFV ID 参数失败")
         }
         
         // 获取idfa
-        initIDFA()
+//        initIDFA()
+        if let idfa = KeyboardSharedDataManager.shared.getInitIDFA() {
+            KeyboardApi.updateIDFA(idfa: idfa)
+            debugPrint("IDFA ID: \(idfa)")
+        } else {
+            debugPrint("获取 IDFA ID 参数失败")
+        }
         
         // 获取token
         if let authToken = KeyboardSharedDataManager.shared.getToken() {

+ 71 - 2
ios/AiKeyboard/View/MainView/KeyboardBaseView.swift

@@ -26,6 +26,39 @@ class KeyboardBaseView: UIView {
     
     weak var delegate: KeyboardBaseViewDelegate?
     
+    lazy var topView: UIView = {
+       
+        let view = UIView()
+        view.isHidden = true
+        view.backgroundColor = .clear
+        return view
+    }()
+    
+    lazy var allowView: UIView = {
+       
+        let view = UIView()
+        view.backgroundColor = .hexStringColor(hexString: "#FFF8EF")
+        view.layer.cornerRadius = 9
+        return view
+    }()
+    
+    lazy var allowTitleLabel: UILabel = {
+        
+        let label = UILabel()
+        label.text = "“允许”【从其他app粘贴】,不再弹出询问弹窗>"
+        label.font = .systemFont(ofSize: 11)
+        label.textColor = .hexStringColor(hexString: "#FFA616")
+        return label
+    }()
+    
+    lazy var allowCloseBtn: UIButton = {
+       
+        let button = UIButton()
+        button.setBackgroundImage(UIImage(named: "keyboard_paste_tip_close_icon"), for: .normal)
+        button.addTarget(self, action: #selector(closeBtnClickAction), for: .touchUpInside)
+        return button
+    }()
+    
     lazy var pasteBtn: UIButton = {
         
         let button = UIButton()
@@ -191,17 +224,53 @@ extension KeyboardBaseView {
         
         delegate?.sendBtnClickAction()
     }
+    
+    // 关闭提示框
+    @objc func closeBtnClickAction() {
+        
+        self.topView.snp.updateConstraints { make in
+            make.height.equalTo(0)
+        }
+        self.topView.isHidden = true
+    }
 }
 
 extension KeyboardBaseView {
     
     func setUI() {
         
+        self.addSubview(topView)
+        topView.snp.makeConstraints { make in
+            make.left.equalTo(12)
+            make.right.equalTo(-12)
+            make.top.equalTo(0)
+            make.height.equalTo(0)
+        }
+        
+        self.topView.addSubview(allowView)
+        allowView.snp.makeConstraints { make in
+            make.top.leading.right.equalTo(0)
+            make.height.equalTo(24)
+        }
+        
+        allowView.addSubview(allowTitleLabel)
+        allowTitleLabel.snp.makeConstraints { make in
+            make.left.equalTo(6)
+            make.centerY.equalToSuperview()
+        }
+        
+        allowView.addSubview(allowCloseBtn)
+        allowCloseBtn.snp.makeConstraints { make in
+            make.size.equalTo(CGSize(width: 10, height: 10))
+            make.right.equalTo(-8)
+            make.centerY.equalToSuperview()
+        }
+        
         self.addSubview(pasteBtn)
         pasteBtn.snp.makeConstraints { make in
             make.size.equalTo(CGSize(width: 58, height: 46))
             make.right.equalTo(-10)
-            make.top.equalTo(0)
+            make.top.equalTo(topView.snp.bottom)
         }
         
         self.addSubview(deleteBtn)
@@ -228,7 +297,7 @@ extension KeyboardBaseView {
         self.addSubview(dialogueView)
         dialogueView.snp.makeConstraints { make in
             make.height.equalTo(46)
-            make.top.equalTo(0)
+            make.top.equalTo(topView.snp.bottom)
             make.left.equalTo(10)
             make.right.equalTo(pasteBtn.snp.left).offset(-6)
         }

+ 6 - 4
ios/AiKeyboard/View/MainView/KeyboardHelpView.swift

@@ -9,7 +9,7 @@ import UIKit
 
 protocol KeyboardHelpViewDelegate: NSObjectProtocol {
     
-    func helpCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping (() -> ()))
+    func helpCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping ((Bool) -> ()))
 }
 
 class KeyboardHelpView: KeyboardBaseView {
@@ -91,9 +91,11 @@ extension KeyboardHelpView: UICollectionViewDelegate, UICollectionViewDataSource
         let cell = collectionView.cellForItem(at: indexPath) as! KeyboardCharacterCell
         cell.setAnimate(play: true)
         if let model = self.characterList?[indexPath.row], let characterId = model.id {
-            helpDelegate?.helpCollectionViewDidSelectItem(characterId: characterId, content: self.dialogueLabel.text ?? "") {
+            helpDelegate?.helpCollectionViewDidSelectItem(characterId: characterId, content: self.dialogueLabel.text ?? "") { isSuccess in
                 cell.setAnimate(play: false)
-                self.dialogueClearBtnAction()
+                if isSuccess {
+                    self.dialogueClearBtnAction()
+                }
             }
         }
     }
@@ -121,7 +123,7 @@ extension KeyboardHelpView {
             make.left.equalTo(10)
             make.right.equalTo(deleteBtn.snp.left).offset(-6)
             make.top.equalTo(dialogueView.snp.bottom).offset(6)
-            make.bottom.equalTo(self.sendBtn.snp.bottom).offset(3)
+            make.bottom.equalTo(0)
         }
     }
 }

+ 26 - 15
ios/AiKeyboard/View/MainView/KeyboardPrologueView.swift

@@ -10,7 +10,7 @@ import JXSegmentedView
 
 protocol KeyboardPrologueViewDelegate: NSObjectProtocol {
     
-    func prologueCollectionViewDidSelectItem(name: String, complete: @escaping (([String]) -> ()))
+    func prologueCollectionViewDidSelectItem(name: String, complete: @escaping (([String], Bool, Bool) -> ()))
     
     func prologueTableViewDidSelectItem(content: String)
 }
@@ -191,9 +191,11 @@ extension KeyboardPrologueView {
         
         if let selectName = self.selectName {
             
-            prologueDelegate?.prologueCollectionViewDidSelectItem(name: selectName) { list in
-                self.isResultLoading = false
-                self.resultList = list
+            prologueDelegate?.prologueCollectionViewDidSelectItem(name: selectName) { list, isLogin, isLoaded in
+                if isLoaded {
+                    self.isResultLoading = false
+                    self.resultList = list
+                }
             }
         }
     }
@@ -233,18 +235,27 @@ extension KeyboardPrologueView: UICollectionViewDelegate, UICollectionViewDataSo
     
     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
         
+        self.selectName = ""
+        
         if let selectTopics = self.selectTopics, selectTopics.count > 0 {
-            
-            // 清空回答列表
-            self.resultList?.removeAll()
-            self.collectionView.isHidden = true
-            self.resultView.isHidden = false
-            self.tableView.reloadData()
-            
+        
             let name = selectTopics[indexPath.row].name
-            prologueDelegate?.prologueCollectionViewDidSelectItem(name: name ?? "") { list in
-                self.isResultLoading = false
-                self.resultList = list
+            self.selectName = name
+            prologueDelegate?.prologueCollectionViewDidSelectItem(name: name ?? "") { list, isLogin, isLoaded in
+        
+                if isLogin {
+                    // 清空回答列表
+                    self.resultList?.removeAll()
+                    self.collectionView.isHidden = true
+                    self.resultView.isHidden = false
+                    self.tableView.reloadData()
+                }
+                
+                if isLoaded {
+                    
+                    self.isResultLoading = false
+                    self.resultList = list
+                }
             }
         }
     }
@@ -376,7 +387,7 @@ extension KeyboardPrologueView {
             make.left.equalTo(10)
             make.right.equalTo(-10)
             make.height.equalTo(34)
-            make.top.equalTo(15)
+            make.top.equalTo(self.topView.snp.bottom)
         }
         
         self.addSubview(collectionView)

+ 22 - 14
ios/AiKeyboard/View/MainView/KeyboardTeachView.swift

@@ -9,7 +9,7 @@ import UIKit
 
 protocol KeyboardTeachViewDelegate: NSObjectProtocol {
     
-    func teachCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping (([String]) -> ()))
+    func teachCollectionViewDidSelectItem(characterId: String, content: String, complete: @escaping (([String], Bool, Bool) -> ()))
     
     func teachTableViewDidSelectItem(content: String)
 }
@@ -145,9 +145,11 @@ extension KeyboardTeachView {
         
         if let selectCharacter = self.selectCharacter, let characterId = selectCharacter.id {
             
-            teachDelegate?.teachCollectionViewDidSelectItem(characterId: characterId, content: self.dialogueLabel.text ?? "") { list in
-                self.isResultLoading = false
-                self.resultList = list
+            teachDelegate?.teachCollectionViewDidSelectItem(characterId: characterId, content: self.dialogueLabel.text ?? "") { list, isLogin, isLoaded in
+                if isLoaded {
+                    self.isResultLoading = false
+                    self.resultList = list
+                }
             }
         }
     }
@@ -186,16 +188,22 @@ extension KeyboardTeachView: UICollectionViewDelegate, UICollectionViewDataSourc
         
         if let model = self.characterList?[indexPath.row], let characterId = model.id {
             
-            // 清空回答列表
-            self.resultList?.removeAll()
-            self.collectionView.isHidden = true
-            self.resultView.isHidden = false
-            self.tableView.reloadData()
-            
             self.selectCharacter = model
-            teachDelegate?.teachCollectionViewDidSelectItem(characterId: characterId, content: self.dialogueLabel.text ?? "") { list in
-                self.isResultLoading = false
-                self.resultList = list
+            teachDelegate?.teachCollectionViewDidSelectItem(characterId: characterId, content: self.dialogueLabel.text ?? "") { list, isLogin, isLoaded in
+                
+                if isLogin {
+                    // 清空回答列表
+                    self.resultList?.removeAll()
+                    self.collectionView.isHidden = true
+                    self.resultView.isHidden = false
+                    self.tableView.reloadData()
+                }
+                
+                if isLoaded {
+                    
+                    self.isResultLoading = false
+                    self.resultList = list
+                }
             }
         }
     }
@@ -310,7 +318,7 @@ extension KeyboardTeachView {
             make.left.equalTo(10)
             make.right.equalTo(deleteBtn.snp.left).offset(-6)
             make.top.equalTo(dialogueView.snp.bottom).offset(6)
-            make.bottom.equalTo(self.sendBtn.snp.bottom).offset(3)
+            make.bottom.equalTo(0)
         }
         
         self.addSubview(resultView)

+ 1 - 1
ios/AiKeyboard/View/PopView/KeyboardExchangeView.swift

@@ -143,7 +143,7 @@ extension KeyboardExchangeView: UICollectionViewDelegate, UICollectionViewDataSo
     }
     
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
-        return CGSize(width: KeyboardConst.kb_kScreenW, height: 45)
+        return CGSize(width: KeyboardConst.kb_kScreenW, height: 60)
     }
 }
 

+ 10 - 1
ios/AiKeyboard/View/PopView/KeyboardLoginTipView.swift

@@ -11,6 +11,8 @@ class KeyboardLoginTipView: UIView {
     
     var loginBtnClosure: (() -> ())?
     
+    var backBtnClosure: (() -> ())?
+    
     lazy var bgImageView: UIImageView = {
        
         let imageView = UIImageView()
@@ -25,6 +27,7 @@ class KeyboardLoginTipView: UIView {
         btn.layer.shadowOffset = CGSize(width: 0, height: 0)
         btn.layer.shadowColor = UIColor.hexStringColor(hexString: "#000000", alpha: 0.12).cgColor
         btn.layer.shadowRadius = 6.1
+        btn.addTarget(self, action: #selector(backBtnClickAction), for: .touchUpInside)
         return btn
     }()
     
@@ -47,10 +50,11 @@ class KeyboardLoginTipView: UIView {
     lazy var loginBtn: UIButton = {
        
         let btn = UIButton()
+        btn.layer.cornerRadius = 24
         btn.setTitle("去登录", for: .normal)
         btn.setTitleColor(.white, for: .normal)
         
-        if let image = UIImage.gradient([UIColor.hexStringColor(hexString: "#7D46FC"), UIColor.hexStringColor(hexString: "#F5FAFF")], size: CGSize(width: 300, height: 48), locations: [0, 1], direction: .horizontal) {
+        if let image = UIImage.gradient([UIColor.hexStringColor(hexString: "#7D46FC"), UIColor.hexStringColor(hexString: "#BC87FF")], size: CGSize(width: 300, height: 48), locations: [0, 1], direction: .horizontal) {
             
             btn.backgroundColor = UIColor(patternImage: image)
         }
@@ -72,6 +76,11 @@ class KeyboardLoginTipView: UIView {
         
         loginBtnClosure?()
     }
+    
+    @objc func backBtnClickAction() {
+        
+        backBtnClosure?()
+    }
 }
 
 extension KeyboardLoginTipView {

+ 14 - 0
ios/AiKeyboard/View/PopView/KeyboardMenuView.swift

@@ -12,6 +12,10 @@ protocol KeyboardMenuViewDelegate: NSObjectProtocol {
     func menuBackBtnClickAction()
     
     func vipBtnClickAction()
+    
+    func diyBtnClickAction()
+    
+    func marketBtnClickAction()
 }
 
 class KeyboardMenuView: UIView {
@@ -45,6 +49,7 @@ class KeyboardMenuView: UIView {
         btn.titleLabel?.textAlignment = .center
         btn.setImage(UIImage(named: "keyboard_menu_diy"), for: .normal)
         btn.setImageTitleLayout(.imgTop, spacing: 8)
+        btn.addTarget(self, action: #selector(diyBtnClickAction), for: .touchUpInside)
         return btn
     }()
     
@@ -59,6 +64,7 @@ class KeyboardMenuView: UIView {
         btn.titleLabel?.textAlignment = .center
         btn.setImage(UIImage(named: "keyboard_menu_market"), for: .normal)
         btn.setImageTitleLayout(.imgTop, spacing: 8)
+        btn.addTarget(self, action: #selector(marketBtnClickAction), for: .touchUpInside)
         return btn
     }()
     
@@ -96,6 +102,14 @@ extension KeyboardMenuView {
     @objc func vipBtnClickAction() {
         delegate?.vipBtnClickAction()
     }
+    
+    @objc func marketBtnClickAction() {
+        delegate?.marketBtnClickAction()
+    }
+    
+    @objc func diyBtnClickAction() {
+        delegate?.diyBtnClickAction()
+    }
 }
 
 extension KeyboardMenuView {

+ 136 - 0
ios/AiKeyboard/View/PopView/KeyboardTipsPopView.swift

@@ -0,0 +1,136 @@
+//
+//  KeyboardTipsPopView.swift
+//  AiKeyboard
+//
+//  Created by Destiny on 2025/5/12.
+//
+
+import UIKit
+
+class KeyboardTipsPopView: UIView {
+    
+    var settingBtnClosure: (() -> ())?
+    
+    var backBtnClosure: (() -> ())?
+    
+    lazy var bgView: UIView = {
+       
+        let view = UIView()
+        view.backgroundColor = .hexStringColor(hexString: "#000000").withAlphaComponent(0.8)
+        return view
+    }()
+    
+    lazy var mainView: UIView = {
+       
+        let view = UIView()
+        view.layer.cornerRadius = 18
+        view.backgroundColor = .white
+        return view
+    }()
+    
+    lazy var closeBtn: UIButton = {
+        
+        let btn = UIButton()
+        btn.setImage(UIImage(named: "keyboard_close_btn"), for: .normal)
+        btn.addTarget(self, action: #selector(closeBtnClickAction), for: .touchUpInside)
+        return btn
+    }()
+    
+    lazy var titleLabel: UILabel = {
+       
+        let label = UILabel()
+        label.text = "开启【追爱小键盘】,并【允许完全访问】"
+        label.font = .systemFont(ofSize: 13, weight: .medium)
+        label.textColor = .hexStringColor(hexString: "#000000", alpha: 0.8)
+        return label
+    }()
+    
+    lazy var mainImageView: UIImageView = {
+       
+        let imageView = UIImageView()
+        imageView.image = UIImage(named: "keyboard_tip_settings")
+        return imageView
+    }()
+    
+    lazy var settingBtn: UIButton = {
+       
+        let btn = UIButton()
+        btn.layer.cornerRadius = 17
+        btn.setTitle("去设置", for: .normal)
+        btn.setTitleColor(.white, for: .normal)
+        btn.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium)
+        
+        if let image = UIImage.gradient([UIColor.hexStringColor(hexString: "#7D46FC"), UIColor.hexStringColor(hexString: "#BC87FF")], size: CGSize(width: 103, height: 34), locations: [0, 1], direction: .horizontal) {
+            
+            btn.backgroundColor = UIColor(patternImage: image)
+        }
+        
+        btn.addTarget(self, action: #selector(settingBtnClickAction), for: .touchUpInside)
+        return btn
+    }()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        initUI()
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    @objc func settingBtnClickAction() {
+        
+        settingBtnClosure?()
+    }
+    
+    @objc func closeBtnClickAction() {
+        
+        self.removeFromSuperview()
+        backBtnClosure?()
+    }
+}
+
+extension KeyboardTipsPopView {
+    
+    func initUI() {
+        
+        self.addSubview(bgView)
+        bgView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+        
+        self.addSubview(mainView)
+        mainView.snp.makeConstraints { make in
+            make.size.equalTo(CGSize(width: 296, height: 209))
+            make.centerX.equalToSuperview()
+            make.top.equalTo(14)
+        }
+        
+        self.mainView.addSubview(closeBtn)
+        closeBtn.snp.makeConstraints { make in
+            make.size.equalTo(CGSize(width: 24, height: 24))
+            make.right.equalTo(-13)
+            make.top.equalTo(12)
+        }
+        
+        self.mainView.addSubview(titleLabel)
+        titleLabel.snp.makeConstraints { make in
+            make.centerX.equalToSuperview()
+            make.top.equalTo(38)
+        }
+        
+        self.mainView.addSubview(mainImageView)
+        mainImageView.snp.makeConstraints { make in
+            make.size.equalTo(CGSize(width: 254, height: 77))
+            make.top.equalTo(titleLabel.snp.bottom).offset(14)
+            make.centerX.equalToSuperview()
+        }
+        
+        self.mainView.addSubview(settingBtn)
+        settingBtn.snp.makeConstraints { make in
+            make.size.equalTo(CGSize(width: 103, height: 34))
+            make.top.equalTo(mainImageView.snp.bottom).offset(14)
+            make.centerX.equalToSuperview()
+        }
+    }
+}

+ 22 - 3
ios/AiKeyboard/View/PopView/KeyboardVipTipView.swift

@@ -10,6 +10,10 @@ import Lottie
 
 class KeyboardVipTipView: UIView {
     
+    var unlockBtnClosure: (() -> ())?
+    
+    var backBtnClosure: (() -> ())?
+    
     lazy var bgImageView: UIImageView = {
         
         let imageView = UIImageView()
@@ -50,6 +54,7 @@ class KeyboardVipTipView: UIView {
         label.text = "您的免费提问次数已用完\n可订阅会员解锁无线提问"
         label.font = .systemFont(ofSize: 14, weight: .medium)
         label.textColor = .hexStringColor(hexString: "#000000", alpha: 0.8)
+        label.setLineSpacing(5)
         return label
     }()
     
@@ -58,15 +63,24 @@ class KeyboardVipTipView: UIView {
         let animate = LottieAnimationView(name: "vip_button")
         animate.loopMode = .loop
         animate.backgroundColor = .clear
+        
+        animate.isUserInteractionEnabled = true
+        let tap = UITapGestureRecognizer(target: self, action: #selector(unlockBtnAction))
+        animate.addGestureRecognizer(tap)
         return animate
     }()
     
     lazy var unlockLabel: UILabel = {
         
-        let name = self.getFontPostScriptName(from: "淘宝买菜体.ttf")
+        // 注册自定义字体
+        if let fontURL = Bundle.main.url(forResource: "tbmct", withExtension: "ttf") {
+            CTFontManagerRegisterFontsForURL(fontURL as CFURL, .process, nil)
+        }
+        
+//        let name = self.getFontPostScriptName(from: "淘宝买菜体.ttf")
         let label = UILabel()
         label.text = "¥0.1/天解锁"
-        label.font = UIFont(name: name ?? "", size: 24)
+        label.font = UIFont(name: "TBMCYXT", size: 24)
         label.textColor = .white
         return label
     }()
@@ -97,7 +111,12 @@ extension KeyboardVipTipView {
     
     @objc func backButtonClickAction() {
         
-        self.removeFromSuperview()
+        self.backBtnClosure?()
+    }
+    
+    @objc func unlockBtnAction() {
+        
+        self.unlockBtnClosure?()
     }
 }
 

+ 19 - 7
ios/KeyboardSharedDataManager.swift

@@ -12,7 +12,7 @@ class KeyboardSharedDataManager {
     static let shared = KeyboardSharedDataManager()
     
     // App Group标识符,需要在主应用和键盘扩展中保持一致
-    private let appGroupIdentifier = "com.qihuan.zhuiaijianpan.AiKeyboard"
+    private let appGroupIdentifier = "group.com.qihuan.zhuiaijianpan"
     
     // 共享的UserDefaults
     private lazy var sharedUserDefaults: UserDefaults? = {
@@ -38,14 +38,26 @@ class KeyboardSharedDataManager {
         sharedUserDefaults?.synchronize()
     }
     
-    // 保存亲密度
-    func saveIntimacy() {
-        
+    // 保存idfv
+    func saveInitIDFV(_ idfv: String) {
+        sharedUserDefaults?.set(idfv, forKey: "init_idfv")
+        sharedUserDefaults?.synchronize()
+    }
+    
+    // 获取idfv
+    func getInitIDFV() -> String? {
+        return sharedUserDefaults?.string(forKey: "init_idfv")
+    }
+    
+    // 保存idfa
+    func saveInitIDFA(_ idfa: String) {
+        sharedUserDefaults?.set(idfa, forKey: "init_idfa")
+        sharedUserDefaults?.synchronize()
     }
     
-    // 获取亲密度
-    func getIntimacy() {
-        
+    // 获取idfa
+    func getInitIDFA() -> String? {
+        return sharedUserDefaults?.string(forKey: "init_idfa")
     }
     
     // 检查是否已登录

+ 2 - 2
ios/Podfile.lock

@@ -206,10 +206,10 @@ SPEC CHECKSUMS:
   Toast-Swift: 7a03a532afe3a560d4044bc7c237e2864d295173
   url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
   video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
-  wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e
+  wakelock_plus: 373cfe59b235a6dd5837d0fb88791d2f13a90d56
   webview_flutter_wkwebview: a4af96a051138e28e29f60101d094683b9f82188
   wechat_kit: b6853fe0933b9a60a008a508e709c14f6ed2dc70
 
 PODFILE CHECKSUM: 7617064a1505ff5219c52712b08fe512373c13de
 
-COCOAPODS: 1.15.2
+COCOAPODS: 1.16.2

+ 14 - 4
ios/Runner.xcodeproj/project.pbxproj

@@ -28,7 +28,6 @@
 		04582A182DBB6CFA00C1792D /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04582A172DBB6CFA00C1792D /* UIImage+Extension.swift */; };
 		04582A1B2DBB7BDD00C1792D /* vip_button.json in Resources */ = {isa = PBXBuildFile; fileRef = 04582A1A2DBB7BDD00C1792D /* vip_button.json */; };
 		04582A1F2DBF227900C1792D /* KeyboardMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04582A1E2DBF227900C1792D /* KeyboardMenuView.swift */; };
-		04582A2C2DBF2A4E00C1792D /* 淘宝买菜体.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 04582A2B2DBF2A4E00C1792D /* 淘宝买菜体.ttf */; };
 		04582A302DBF5B6700C1792D /* KeyboardPrologueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04582A2F2DBF5B6700C1792D /* KeyboardPrologueView.swift */; };
 		04582A322DBF6C0800C1792D /* heart.json in Resources */ = {isa = PBXBuildFile; fileRef = 04582A312DBF6C0800C1792D /* heart.json */; };
 		04582A342DBF6CB800C1792D /* KeyboardExchangeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04582A332DBF6CB800C1792D /* KeyboardExchangeView.swift */; };
@@ -54,7 +53,11 @@
 		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
 		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
 		A21E1420BB362BC7E3EB4811 /* Pods_AiKeyboard.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B5EF92DE43959D6CF5B50A3 /* Pods_AiKeyboard.framework */; };
+		FE05FA2E2DCE0CFC00CBA944 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = FE05FA2D2DCE0CFC00CBA944 /* Settings.bundle */; };
 		FE8F000A2DCC4D6F00AA2562 /* FlutterMethodChannelManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE8F00092DCC4D6F00AA2562 /* FlutterMethodChannelManager.swift */; };
+		FED899712DD1D044001808D5 /* KeyboardTipsPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED899702DD1D044001808D5 /* KeyboardTipsPopView.swift */; };
+		FED899762DD1F5D5001808D5 /* tbmct.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FED899752DD1F5D5001808D5 /* tbmct.ttf */; };
+		FED899772DD1F5D5001808D5 /* tbmct.ttf in Resources */ = {isa = PBXBuildFile; fileRef = FED899752DD1F5D5001808D5 /* tbmct.ttf */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -121,7 +124,6 @@
 		04582A172DBB6CFA00C1792D /* UIImage+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extension.swift"; sourceTree = "<group>"; };
 		04582A1A2DBB7BDD00C1792D /* vip_button.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = vip_button.json; sourceTree = "<group>"; };
 		04582A1E2DBF227900C1792D /* KeyboardMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardMenuView.swift; sourceTree = "<group>"; };
-		04582A2B2DBF2A4E00C1792D /* 淘宝买菜体.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "淘宝买菜体.ttf"; sourceTree = "<group>"; };
 		04582A2F2DBF5B6700C1792D /* KeyboardPrologueView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPrologueView.swift; sourceTree = "<group>"; };
 		04582A312DBF6C0800C1792D /* heart.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = heart.json; sourceTree = "<group>"; };
 		04582A332DBF6CB800C1792D /* KeyboardExchangeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardExchangeView.swift; sourceTree = "<group>"; };
@@ -164,8 +166,11 @@
 		B77EC1F8B12A295BD1DC6742 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		C33B8387B7ED09CA498E0EC7 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D50315B08E93289C08ED808C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
+		FE05FA2D2DCE0CFC00CBA944 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
 		FE8F00052DCC4A7B00AA2562 /* AiKeyboard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AiKeyboard.entitlements; sourceTree = "<group>"; };
 		FE8F00092DCC4D6F00AA2562 /* FlutterMethodChannelManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlutterMethodChannelManager.swift; sourceTree = "<group>"; };
+		FED899702DD1D044001808D5 /* KeyboardTipsPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardTipsPopView.swift; sourceTree = "<group>"; };
+		FED899752DD1F5D5001808D5 /* tbmct.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = tbmct.ttf; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -201,7 +206,6 @@
 			children = (
 				FE8F00052DCC4A7B00AA2562 /* AiKeyboard.entitlements */,
 				04582A452DC07EB700C1792D /* Model */,
-				04582A2B2DBF2A4E00C1792D /* 淘宝买菜体.ttf */,
 				04582A192DBB7BBB00C1792D /* Animate */,
 				04582A3E2DBF8FBB00C1792D /* Network */,
 				045829FF2DB8EBB900C1792D /* Marco */,
@@ -283,6 +287,7 @@
 				04582A132DBB67FB00C1792D /* KeyboardLoginTipView.swift */,
 				04582A152DBB681100C1792D /* KeyboardVipTipView.swift */,
 				04582A1E2DBF227900C1792D /* KeyboardMenuView.swift */,
+				FED899702DD1D044001808D5 /* KeyboardTipsPopView.swift */,
 			);
 			path = PopView;
 			sourceTree = "<group>";
@@ -367,6 +372,7 @@
 		97C146E51CF9000F007C117D = {
 			isa = PBXGroup;
 			children = (
+				FED899752DD1F5D5001808D5 /* tbmct.ttf */,
 				043D62362DCB0E7A005D12EE /* KeyboardSharedDataManager.swift */,
 				9740EEB11CF90186004384FC /* Flutter */,
 				97C146F01CF9000F007C117D /* Runner */,
@@ -401,6 +407,7 @@
 				74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
 				74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
 				FE8F00092DCC4D6F00AA2562 /* FlutterMethodChannelManager.swift */,
+				FE05FA2D2DCE0CFC00CBA944 /* Settings.bundle */,
 			);
 			path = Runner;
 			sourceTree = "<group>";
@@ -519,12 +526,12 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				04582A2C2DBF2A4E00C1792D /* 淘宝买菜体.ttf in Resources */,
 				04582A1B2DBB7BDD00C1792D /* vip_button.json in Resources */,
 				04582A4D2DC1CFBC00C1792D /* keyboard_loading.json in Resources */,
 				04582A512DC211F100C1792D /* keyboard_dialog_loading.json in Resources */,
 				04582A322DBF6C0800C1792D /* heart.json in Resources */,
 				045828C92DB89ABE00C1792D /* Media.xcassets in Resources */,
+				FED899772DD1F5D5001808D5 /* tbmct.ttf in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -541,8 +548,10 @@
 			files = (
 				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
 				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+				FE05FA2E2DCE0CFC00CBA944 /* Settings.bundle in Resources */,
 				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
 				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+				FED899762DD1F5D5001808D5 /* tbmct.ttf in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -702,6 +711,7 @@
 				04582A532DC227E700C1792D /* PrologueModel.swift in Sources */,
 				04582A092DB9EBFC00C1792D /* KeyboardTeachView.swift in Sources */,
 				043D62382DCB0F80005D12EE /* KeyboardSharedDataManager.swift in Sources */,
+				FED899712DD1D044001808D5 /* KeyboardTipsPopView.swift in Sources */,
 				04582A422DBF910400C1792D /* KeyboardApi.swift in Sources */,
 				04582A142DBB67FB00C1792D /* KeyboardLoginTipView.swift in Sources */,
 				045829AE2DB8DD2200C1792D /* KeyboardHelpView.swift in Sources */,

+ 6 - 0
ios/Runner/AppDelegate.swift

@@ -29,6 +29,12 @@ import UIKit
             channel.invokeMethod("navigateToLogin", arguments: nil)
         } else if path == "/member" {
             channel.invokeMethod("navigateToMember", arguments: nil)
+        } else if path == "/character/market" {
+            channel.invokeMethod("navigateToCharacterMarket", arguments: nil)
+        } else if path == "/intimacy" {
+            channel.invokeMethod("navigateToIntimacy", arguments: nil)
+        } else if path == "/character/custom" {
+            channel.invokeMethod("navigateToCustomCharacter", arguments: nil)
         }
         
         return true

+ 17 - 1
ios/Runner/FlutterMethodChannelManager.swift

@@ -20,7 +20,7 @@ class FlutterMethodChannelManager: NSObject {
     private override init() {
         super.init()
     }
-    
+
     // 设置方法通道
     func setupMethodChannels(controller: FlutterViewController) {
         // 创建键盘相关的方法通道
@@ -44,6 +44,22 @@ class FlutterMethodChannelManager: NSObject {
             case "clearAuthToken":
                 KeyboardSharedDataManager.shared.clearAuthToken()
                 result(true)
+            case "saveIDFV":
+                if let args = call.arguments as? [String: Any],
+                   let idfv = args["idfv"] as? String {
+                    KeyboardSharedDataManager.shared.saveInitIDFV(idfv)
+                    result(true)
+                } else {
+                    result(FlutterError(code: "INVALID_ARGUMENTS", message: "无效的参数", details: nil))
+                }
+            case "saveIDFA":
+                if let args = call.arguments as? [String: Any],
+                   let idfa = args["idfa"] as? String {
+                    KeyboardSharedDataManager.shared.saveInitIDFA(idfa)
+                    result(true)
+                } else {
+                    result(FlutterError(code: "INVALID_ARGUMENTS", message: "无效的参数", details: nil))
+                }
             default:
                 result(FlutterMethodNotImplemented)
             }

+ 25 - 0
ios/Runner/Settings.bundle/Root.plist

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>StringsTable</key>
+	<string>Root</string>
+	<key>PreferenceSpecifiers</key>
+	<array>
+		<dict>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+			<key>FooterText</key>
+			<string>【追爱小键盘】设置教程:
+①点击【键盘】→”追爱小键盘“选择【开启】
+②“允许完全访问”选择【开启】
+
+【自动粘贴回复】设置教程:
+①选择【从其他App粘贴】→选择【允许】
+小tips:设置后下次粘贴时不再弹出询问弹窗
+
+开启允许完全访问权限,仅限于键盘请求输出内容,如未打开允许完全访问时,键盘部分功能将收到影响使用,我们将严格遵守隐私协议,不会手机您的个人敏感信息。</string>
+		</dict>
+	</array>
+</dict>
+</plist>

BIN
ios/Runner/Settings.bundle/en.lproj/Root.strings


ios/AiKeyboard/淘宝买菜体.ttf → ios/tbmct.ttf


+ 3 - 38
lib/data/repository/account_repository.dart

@@ -10,6 +10,7 @@ import 'package:keyboard/data/api/request/wechat_login_request.dart';
 import 'package:keyboard/data/api/response/wechat_login_response.dart';
 import 'package:keyboard/data/bean/member_info.dart';
 import 'package:keyboard/data/repository/keyboard_repository.dart';
+import 'package:keyboard/utils/method_chanel_ios_util.dart';
 
 import '../../base/app_base_request.dart';
 import '../../di/get_it.dart';
@@ -206,7 +207,7 @@ class AccountRepository {
   void onLoginSuccess(String phoneNum, String authToken) {
     GravityHelper.onLogin();
     AccountRepository.token = authToken;
-    saveAuthToken(authToken);
+    MethodChanelIOSUtil.saveAuthToken(authToken);
     loginPhoneNum.value = phoneNum;
     refreshUserInfo();
     KVUtil.putString(keyAccountLoginPhoneNum, phoneNum);
@@ -230,7 +231,7 @@ class AccountRepository {
 
   void logout() {
     token = null;
-    clearAuthToken();
+    MethodChanelIOSUtil.clearAuthToken();
     KVUtil.putString(keyAccountLoginPhoneNum, null);
     KVUtil.putString(keyAccountLoginToken, null);
     memberStatusInfo.value = null;
@@ -253,42 +254,6 @@ class AccountRepository {
     });
   }
 
-  // 保存token到ios端
-  Future<void> saveAuthToken(String token) async {
-    // 通知iOS键盘扩展
-    if (Platform.isIOS) {
-      const MethodChannel channel = MethodChannel('keyboard_ios');
-      channel.invokeMethod('saveAuthToken', {'token': token});
-    }
-  }
-
-  // 保存token到ios端
-  Future<void> clearAuthToken() async {
-    // 通知iOS键盘扩展
-    if (Platform.isIOS) {
-      const MethodChannel channel = MethodChannel('keyboard_ios');
-      channel.invokeMethod('clearAuthToken');
-    }
-  }
-
-  // 保存token到ios端
-  Future<void> saveAuthToken(String token) async {
-    // 通知iOS键盘扩展
-    if (Platform.isIOS) {
-      const MethodChannel channel = MethodChannel('keyboard_ios');
-      channel.invokeMethod('saveAuthToken', {'token': token});
-    }
-  }
-
-  // 保存token到ios端
-  Future<void> clearAuthToken() async {
-    // 通知iOS键盘扩展
-    if (Platform.isIOS) {
-      const MethodChannel channel = MethodChannel('keyboard_ios');
-      channel.invokeMethod('clearAuthToken');
-    }
-  }
-
   // 意见反馈
   Future<void> complaintSubmit(String? phone, String content) {
     return atmobApi

+ 4 - 0
lib/device/atmob_platform_info.dart

@@ -1,3 +1,5 @@
+import 'package:keyboard/utils/method_chanel_ios_util.dart';
+
 class AtmobPlatformInfo {
   AtmobPlatformInfo();
 
@@ -83,11 +85,13 @@ class AtmobPlatformInfo {
 
   AtmobPlatformInfo setIdfa(String? idfa) {
     _idfa = idfa;
+    MethodChanelIOSUtil.saveIDFA(idfa);
     return this;
   }
 
   AtmobPlatformInfo setIdfv(String? idfv) {
     _idfv = idfv;
+    MethodChanelIOSUtil.saveIDFV(idfv);
     return this;
   }
 

+ 2 - 2
lib/main.dart

@@ -19,7 +19,7 @@ import 'package:keyboard/utils/atmob_log.dart';
 import 'package:keyboard/utils/channel_util.dart';
 import 'package:keyboard/utils/floating_window_helper.dart';
 import 'package:keyboard/utils/mmkv_util.dart';
-import 'package:keyboard/utils/navitigation_handler.dart';
+import 'package:keyboard/utils/method_chanel_ios_util.dart';
 import 'package:keyboard/utils/privacy_compliance.dart';
 import 'package:keyboard/utils/toast_util.dart';
 import 'package:keyboard/widget/app_lifecycle_widget.dart';
@@ -44,7 +44,7 @@ void main() async {
     //键盘
     KeyboardAndroidPlatform.init();
   } else if (Platform.isIOS) {
-    NavigationHandler.initialize();
+    MethodChanelIOSUtil.initialize();
   }
   AssetLottie(Assets.anim.animSurpriseDialogData).load();
   runApp(const MyApp());

+ 79 - 0
lib/utils/method_chanel_ios_util.dart

@@ -0,0 +1,79 @@
+import 'dart:io';
+
+import 'package:flutter/services.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/module/character_custom/character_custom_page.dart';
+import 'package:keyboard/module/login/login_page.dart';
+import 'package:keyboard/module/main/main_page.dart';
+import 'package:keyboard/module/store/store_page.dart';
+
+import '../module/intimacy_scale/intimacy_scale_page.dart';
+
+class MethodChanelIOSUtil {
+  static final MethodChanelIOSUtil _instance = MethodChanelIOSUtil._internal();
+  factory MethodChanelIOSUtil() => _instance;
+  MethodChanelIOSUtil._internal();
+
+  static const MethodChannel _channel = MethodChannel('keyboard_ios');
+
+  static void initialize() {
+    _channel.setMethodCallHandler(_handleMethod);
+  }
+
+  static Future<dynamic> _handleMethod(MethodCall call) async {
+    switch (call.method) {
+      case 'navigateToLogin':
+        LoginPage.start();
+        break;
+      case 'navigateToMember':
+        StorePage.start();
+        break;
+      case 'navigateToCharacterMarket':
+        MainPage.start(arguments: {"tabName": "character"});
+        break;
+      case 'navigateToCustomCharacter':
+        CharacterCustomPage.start();
+        break;
+      case 'navigateToIntimacy':
+        IntimacyScalePage.start();
+        break;
+      default:
+        throw PlatformException(
+          code: 'Unimplemented',
+          details: '未实现的方法: ${call.method}',
+        );
+    }
+  }
+
+  // 保存token到ios端
+  static Future<void> saveAuthToken(String token) async {
+    // 通知iOS键盘扩展
+    if (Platform.isIOS) {
+      _channel.invokeMethod('saveAuthToken', {'token': token});
+    }
+  }
+
+  // 保存idfv到ios端
+  static Future<void> saveIDFV(String? idfv) async {
+    // 通知iOS键盘扩展
+    if (Platform.isIOS) {
+      _channel.invokeMethod('saveIDFV', {'idfv': idfv});
+    }
+  }
+
+  // 保存idfa到ios端
+  static Future<void> saveIDFA(String? idfa) async {
+    // 通知iOS键盘扩展
+    if (Platform.isIOS) {
+      _channel.invokeMethod('saveIDFA', {'idfa': idfa});
+    }
+  }
+
+  // 保存token到ios端
+  static Future<void> clearAuthToken() async {
+    // 通知iOS键盘扩展
+    if (Platform.isIOS) {
+      _channel.invokeMethod('clearAuthToken');
+    }
+  }
+}

+ 0 - 32
lib/utils/navitigation_handler.dart

@@ -1,32 +0,0 @@
-import 'package:flutter/services.dart';
-import 'package:get/get.dart';
-import 'package:keyboard/module/login/login_page.dart';
-import 'package:keyboard/module/store/store_page.dart';
-
-class NavigationHandler {
-  static final NavigationHandler _instance = NavigationHandler._internal();
-  factory NavigationHandler() => _instance;
-  NavigationHandler._internal();
-
-  static const MethodChannel _channel = MethodChannel('keyboard_ios');
-
-  static void initialize() {
-    _channel.setMethodCallHandler(_handleMethod);
-  }
-
-  static Future<dynamic> _handleMethod(MethodCall call) async {
-    switch (call.method) {
-      case 'navigateToLogin':
-        LoginPage.start();
-        break;
-      case 'navigateToMember':
-        StorePage.start();
-        break;
-      default:
-        throw PlatformException(
-          code: 'Unimplemented',
-          details: '未实现的方法: ${call.method}',
-        );
-    }
-  }
-}