diff --git a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayerAgent.swift b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayerAgent.swift index a0df40b6d..8ba21f151 100644 --- a/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayerAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/AudioPlayer/AudioPlayerAgent.swift @@ -253,6 +253,7 @@ public extension AudioPlayerAgent { audioPlayerDispatchQueue.async { [weak self] in guard let player = self?.latestPlayer else { return } + self?.currentPlaylist = nil self?.stop(player: player, cancelAssociation: true) } } diff --git a/NuguAgents/Sources/CapabilityAgents/System/SystemAgent.swift b/NuguAgents/Sources/CapabilityAgents/System/SystemAgent.swift index 96709c931..fc7b11f10 100644 --- a/NuguAgents/Sources/CapabilityAgents/System/SystemAgent.swift +++ b/NuguAgents/Sources/CapabilityAgents/System/SystemAgent.swift @@ -27,7 +27,7 @@ import RxSwift public final class SystemAgent: SystemAgentProtocol { // CapabilityAgentable - public var capabilityAgentProperty: CapabilityAgentProperty = CapabilityAgentProperty(category: .system, version: "1.3") + public var capabilityAgentProperty: CapabilityAgentProperty = CapabilityAgentProperty(category: .system, version: "1.4") // Private private let contextManager: ContextManageable @@ -43,7 +43,9 @@ public final class SystemAgent: SystemAgentProtocol { DirectiveHandleInfo(namespace: capabilityAgentProperty.name, name: "Revoke", blockingPolicy: BlockingPolicy(medium: .none, isBlocking: false), directiveHandler: handleRevoke), DirectiveHandleInfo(namespace: capabilityAgentProperty.name, name: "NoDirectives", blockingPolicy: BlockingPolicy(medium: .none, isBlocking: false), directiveHandler: handleNoDirectives), DirectiveHandleInfo(namespace: capabilityAgentProperty.name, name: "Noop", blockingPolicy: BlockingPolicy(medium: .none, isBlocking: false), directiveHandler: { { $1(.finished) } }), - DirectiveHandleInfo(namespace: capabilityAgentProperty.name, name: "ResetConnection", blockingPolicy: BlockingPolicy(medium: .none, isBlocking: false), directiveHandler: handleResetConnection) + DirectiveHandleInfo(namespace: capabilityAgentProperty.name, name: "ResetConnection", blockingPolicy: BlockingPolicy(medium: .none, isBlocking: false), directiveHandler: handleResetConnection), + DirectiveHandleInfo(namespace: capabilityAgentProperty.name, name: "TerminateApp", blockingPolicy: BlockingPolicy(medium: .none, isBlocking: false), directiveHandler: handleTerminateApp), + DirectiveHandleInfo(namespace: capabilityAgentProperty.name, name: "RequireUpdate", blockingPolicy: BlockingPolicy(medium: .none, isBlocking: false), directiveHandler: handleRequireUpdate) ] private var disposeBag = DisposeBag() @@ -158,6 +160,28 @@ private extension SystemAgent { } } } + + func handleTerminateApp() -> HandleDirective { + return { [weak self] directive, completion in + defer { completion(.finished) } + + self?.systemDispatchQueue.async { [weak self] in + log.info("") + self?.post(NuguAgentNotification.System.TermiateApp(header: directive.header, data: directive.payload)) + } + } + } + + func handleRequireUpdate() -> HandleDirective { + return { [weak self] directive, completion in + defer { completion(.finished) } + + self?.systemDispatchQueue.async { [weak self] in + log.info("") + self?.post(NuguAgentNotification.System.RequireUpdate(header: directive.header, data: directive.payload)) + } + } + } } // MARK: - Private (handle directive) @@ -184,6 +208,10 @@ extension Notification.Name { static let systemAgentDidReceiveExceptionFail = Notification.Name("com.sktelecom.romaine.notification.name.system_agent_did_receive_exception_fail") static let systemAgentDidReceiveRevokeDevice = Notification.Name("com.sktelecom.romaine.notification.name.system_agent_did_revoke_device") static let systemAgentDidReceiveNoDirective = Notification.Name("com.sktelecom.romaine.notification.name.system_agent_no_directive") + static let systemAgentDidReceiveTermiateApp = + Notification.Name("com.sktelecom.romaine.notification.name.system_agent_termiate_app") + static let systemAgentDidReceiveRequireUpdate = + Notification.Name("com.sktelecom.romaine.notification.name.system_agent_require_update") } public extension NuguAgentNotification { @@ -224,5 +252,31 @@ public extension NuguAgentNotification { return NoDirective(header: header) } } + + public struct TermiateApp: TypedNotification { + static public var name: Notification.Name = .systemAgentDidReceiveTermiateApp + public let header: Downstream.Header + public let data: Data + + public static func make(from: [String : Any]) -> TermiateApp? { + guard let header = from["header"] as? Downstream.Header, + let data = from["data"] as? Data else { return nil } + + return TermiateApp(header: header, data: data) + } + } + + public struct RequireUpdate: TypedNotification { + static public var name : Notification.Name = .systemAgentDidReceiveRequireUpdate + public let header: Downstream.Header + public let data: Data + + public static func make(from: [String : Any]) -> RequireUpdate? { + guard let header = from["header"] as? Downstream.Header, + let data = from["data"] as? Data else { return nil } + + return RequireUpdate(header: header, data: data) + } + } } } diff --git a/NuguAgents/Sources/CapabilityAgents/System/SystemAgentExceptionCode.swift b/NuguAgents/Sources/CapabilityAgents/System/SystemAgentExceptionCode.swift index 990d9a2ae..de3291b2c 100644 --- a/NuguAgents/Sources/CapabilityAgents/System/SystemAgentExceptionCode.swift +++ b/NuguAgents/Sources/CapabilityAgents/System/SystemAgentExceptionCode.swift @@ -28,6 +28,7 @@ public enum SystemAgentExceptionCode: Equatable { case playRouterProcessingException = "PLAY_ROUTER_PROCESSING_EXCEPTION" case ttsSpeakingException = "TTS_SPEAKING_EXCEPTION" case internalServiceException = "INTERNAL_SERVICE_EXCEPTION" + case concurrentConnectionException = "CONCURRENT_CONNECTION_EXCEPTION" } /// <#Description#> diff --git a/NuguCore/Sources/MediaPlayer/MediaPlayer.swift b/NuguCore/Sources/MediaPlayer/MediaPlayer.swift index 1060da85c..bdc75c0f4 100644 --- a/NuguCore/Sources/MediaPlayer/MediaPlayer.swift +++ b/NuguCore/Sources/MediaPlayer/MediaPlayer.swift @@ -81,6 +81,7 @@ extension MediaPlayer { mediaPlayer.replaceCurrentItem(with: nil) removePlayerItemObserver() // CHECK-ME: 타이밍 이슈 없을지 확인 + downloadAudioData = nil playerItem = nil player = nil @@ -114,13 +115,12 @@ extension MediaPlayer { } public func seek(to offset: TimeIntervallic, completion: ((EndedUp) -> Void)?) { - guard - let mediaPlayer = player, - mediaPlayer.currentItem != nil else { - completion?(.failure(MediaPlayableError.notPrepareSource)) - return + guard let mediaPlayer = player, + mediaPlayer.currentItem != nil else { + completion?(.failure(MediaPlayableError.notPrepareSource)) + return } - + mediaPlayer.seek(to: offset.cmTime) completion?(.success) } @@ -204,13 +204,13 @@ extension MediaPlayer: MediaUrlDataSource { playerItem.cacheKey = cacheKey addPlayerItemObserver(object: playerItem) self.playerItem = playerItem - + player = AVQueuePlayer(playerItem: playerItem) if offset.seconds > 0 { player?.seek(to: offset.cmTime) } - + } } @@ -244,28 +244,29 @@ extension MediaPlayer { extension MediaPlayer: AVAssetResourceLoaderDelegate { public func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool { - guard let originalScheme = originalScheme else { - log.error("originalScheme should not be nil") - return false - } - pendingRequestQueue.sync { _ = pendingRequests.insert(loadingRequest) } - if sessionHasFinishedLoading == true { + guard (sessionHasFinishedLoading ?? false) == false else { processPendingRequests() + return true } if downloadSession == nil { guard let url = loadingRequest.request.url, - var urlToConvert = URLComponents(url: url, resolvingAgainstBaseURL: false) else { + var urlToConvert = URLComponents(url: url, resolvingAgainstBaseURL: false) else { log.error("intercepted url is invalid") return false } - sessionHasFinishedLoading = false + guard let originalScheme = originalScheme else { + log.error("originalScheme should not be nil") + return false + } + urlToConvert.scheme = originalScheme + sessionHasFinishedLoading = false guard let convertedUrl = urlToConvert.url else { log.error("intercepted url is invalid") @@ -314,7 +315,7 @@ private extension MediaPlayer { func fillInContentInformation(contentInformationRequest: AVAssetResourceLoadingContentInformationRequest?) { guard let downloadResponse = downloadResponse, - let mimeType = downloadResponse.mimeType else { return } + let mimeType = downloadResponse.mimeType else { return } let unmanagedFileUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType as CFString, nil) guard let contentType = unmanagedFileUTI?.takeRetainedValue() else { @@ -361,11 +362,10 @@ private extension MediaPlayer { downloadDataTask?.cancel() downloadDataTask = nil downloadSession = nil - downloadAudioData = nil downloadResponse = nil originalScheme = nil - - if pendingRequests.count > 0 { + + if pendingRequests.isEmpty == false { for request in pendingRequests { request.finishLoading() } @@ -406,7 +406,7 @@ extension MediaPlayer: URLSessionDataDelegate { self.downloadAudioData?.append(data) if let downloadAudioData = downloadAudioData, - let expectedDataLength = expectedDataLength { + let expectedDataLength = expectedDataLength { log.debug("\(Float(downloadAudioData.count) / Float(expectedDataLength))") } else { log.debug("expectedDataLength should not be nil!") @@ -418,10 +418,9 @@ extension MediaPlayer: URLSessionDataDelegate { public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { defer { releaseCacheData() + sessionHasFinishedLoading = true } - sessionHasFinishedLoading = true - if error != nil { log.error("\(error!)") return @@ -430,14 +429,14 @@ extension MediaPlayer: URLSessionDataDelegate { processPendingRequests() guard let audioDataToWrite = downloadAudioData, - let itemKeyForCache = playerItem?.cacheKey else { + let itemKeyForCache = playerItem?.cacheKey else { return } MediaCacheManager.saveMediaData( mediaData: audioDataToWrite, cacheKey: itemKeyForCache - ) ? log.debug("SaveComplete: \(itemKeyForCache)") : log.error("SaveFailed") + ) ? log.debug("SaveComplete: \(itemKeyForCache)") : log.error("SaveFailed") } } @@ -453,11 +452,11 @@ private extension MediaPlayer { if let cacheKey = object.cacheKey { MediaCacheManager.setModifiedDateForCacheFile(key: cacheKey) } - + case .failed: log.debug("playback failed reason: \(object.error.debugDescription)") self.delegate?.mediaPlayerStateDidChange(.error(error: object.error ?? MediaPlayableError.unknown), mediaPlayer: self) - + default: break } diff --git a/SampleApp/Sources/Manager/NuguCentralManager.swift b/SampleApp/Sources/Manager/NuguCentralManager.swift index c15ea3334..da79a31bb 100644 --- a/SampleApp/Sources/Manager/NuguCentralManager.swift +++ b/SampleApp/Sources/Manager/NuguCentralManager.swift @@ -332,7 +332,7 @@ private extension NuguCentralManager { self.localTTSAgent.playLocalTTS(type: .deviceGatewayPlayRouterConnectionError) case .ttsSpeakingException: self.localTTSAgent.playLocalTTS(type: .deviceGatewayTTSConnectionError) - case .unauthorizedRequestException: + case .unauthorizedRequestException, .concurrentConnectionException: self.refreshToken { [weak self] result in switch result { case .success: