Files
youle_app_ios_v2/ylgamehall/Source/Resource/PhoneStateMonitor.swift
T
joywayerandClaude Opus 4.7 ded6b5d86b Phase 8.4:PhoneStateMonitor → phonestate 反向 callback
契约 §3.1 [14]phonestate 落地。msext 用已废弃的 CTCallCenter(iOS 10+
deprecated);新外壳改用 CallKit CXCallObserver,对 H5 输出字符串一致:
- "2" = 来电响铃中(hasConnected=false / isOutgoing=false / hasEnded=false)
       或已连接(hasConnected=true)
- "0" = 挂断(hasEnded=true)
- 其它(拨出未连接、保持)不发

新增:
- Source/Resource/PhoneStateMonitor.swift(@MainActor singleton,与 BatteryMonitor /
  NetworkMonitor 同款 pattern;onChange closure + start/stop 生命周期)

接入:
- SubGameViewController.setupExternalSubscriptions:start + onChange
- SubGameViewController.teardownExternalSubscriptions:onChange=nil + stop
- 大厅 WebContainerViewController 不订阅(msext gameController 同款边界,
  契约表注「已注释」表示大厅不发;H5 子游戏才依赖此事件做哑麦/暂停)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-23 02:01:29 +08:00

79 lines
2.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// PhoneStateMonitor.swift
// ylgamehall
//
// 电话状态监听(Phase 8.4,契约 §3.1 14 phonestate 反向 callback)。
//
// msext 用已废弃的 `CTCallCenter`iOS 10+ deprecated);本项目用 CallKit
// `CXCallObserver`iOS 10+ 推荐),语义等价,对 H5 输出的字符串一致:
// - "2" = 来电响铃中(hasConnected=false, isOutgoing=false, hasEnded=false
// 或 已连接(hasConnected=true,无论 incoming/outgoing
// - "0" = 挂断(hasEnded=true
// - 其它(拨出中未连接、保持等)不发
//
// 仅子游戏注册,大厅不发(msext gameController.m 同款;契约表注「已注释」表示
// msext 在线版当前不主动触发,但 H5 子游戏仍按契约处理收到的事件)。
//
// 使用模式(与 BatteryMonitor / NetworkMonitor 一致):
// - viewDidLoad / setupExternalSubscriptions`shared.start()` + `onChange = { ... }`
// - viewWillDisappear / teardownExternalSubscriptions`onChange = nil` + `shared.stop()`
//
import Foundation
import CallKit
@MainActor
public final class PhoneStateMonitor: NSObject {
public static let shared = PhoneStateMonitor()
/// 状态变化回调,参数即给 H5 的 `phonestate` 字符串("2" / "0")。
/// closure 在 main actor 上调,调用方可直接拿 bridge 反向 callback。
public var onChange: ((String) -> Void)?
private var observer: CXCallObserver?
public override init() {
super.init()
}
/// 开始监听。重复调安全(已 start 则无操作)。
public func start() {
guard observer == nil else { return }
let o = CXCallObserver()
// queue: nil → main queuecallObserver:callChanged: 回调线程一致,
// 子游戏页 bridge.call 也走主线程,无并发问题)
o.setDelegate(self, queue: nil)
observer = o
}
/// 停止监听。stop 之后再 start 重新创建 observer。
public func stop() {
observer?.setDelegate(nil, queue: nil)
observer = nil
}
}
extension PhoneStateMonitor: CXCallObserverDelegate {
/// CXCallObserver 状态切换。本方法 `@objc` 由 CallKit 在主队列调(init 时传 nil)。
public nonisolated func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) {
let state: String
if call.hasEnded {
state = "0"
} else if call.hasConnected {
// 已接通(任何方向)— 视频房 / 录音应静音
state = "2"
} else if !call.isOutgoing {
// 来电响铃中(hasEnded=false, hasConnected=false, isOutgoing=false
state = "2"
} else {
// 拨出中未连接 / 保持等中间态 — H5 不关心,丢弃
return
}
Task { @MainActor in
Self.shared.onChange?(state)
}
}
}