diff --git a/hearo/HearoadWatch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json b/hearo/HearoadWatch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json index 2698331..eb5a431 100644 --- a/hearo/HearoadWatch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/hearo/HearoadWatch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Icon 오후 9.05.06.png", + "filename" : "Icon 오후 9.05.06.png", "idiom" : "universal", "platform" : "watchos", "size" : "1024x1024" diff --git a/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Caution 1.svg b/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Caution 1.svg new file mode 100644 index 0000000..5bc2eab --- /dev/null +++ b/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Caution 1.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Caution 2.svg b/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Caution 2.svg new file mode 100644 index 0000000..5bc2eab --- /dev/null +++ b/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Caution 2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Caution.svg b/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Caution.svg new file mode 100644 index 0000000..5bc2eab --- /dev/null +++ b/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Caution.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Contents.json b/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Contents.json new file mode 100644 index 0000000..26cab3d --- /dev/null +++ b/hearo/LiveActivity/Assets.xcassets/Caution.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Caution.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Caution 1.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Caution 2.svg", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/hearo/LiveActivity/LiveActivityLiveActivity.swift b/hearo/LiveActivity/LiveActivityLiveActivity.swift index 3576eb9..791230b 100644 --- a/hearo/LiveActivity/LiveActivityLiveActivity.swift +++ b/hearo/LiveActivity/LiveActivityLiveActivity.swift @@ -5,8 +5,6 @@ // Created by 김준수(엘빈) on 10/18/24. // - - import ActivityKit import WidgetKit import SwiftUI @@ -14,29 +12,32 @@ import SwiftUI // 라이브 액티비티 속성 정의 struct LiveActivityAttributes: ActivityAttributes { public struct ContentState: Codable, Hashable { - // 현재 상태를 나타내는 속성을 정의 (예: 경고 여부) - // 하지만 필요 없다면 이 속성을 추가적으로 활용할 수 있습니다. + // 현재 상태를 나타내는 속성을 정의 } - + var name: String // 이름 속성만 유지 } + struct LiveActivityLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: LiveActivityAttributes.self) { context in - // UI 구성 + // Live Activity 구성 HStack { - VStack { - Text("소리 수집이 중지되었습니다.") // 변경된 메시지 - .font(.headline) - .foregroundColor(.white) + VStack(alignment: .leading) { + Text("히어로드") + .font(.LiveActivitySub) + .foregroundColor(Color("MainFontColor")) + Text("소리수집이\n중지되었습니다.") + .font(.LiveActivityMain) + .foregroundColor(Color("MainFontColor")) } Spacer() - Image(systemName: "exclamationmark.triangle") // 경고 아이콘 + Image("Caution") // "Caution" 이미지를 왼쪽에 추가 .resizable() .scaledToFit() - .frame(width: 40, height: 40) + .frame(width: 91, height: 91) .foregroundColor(.white) } .padding() @@ -47,21 +48,42 @@ struct LiveActivityLiveActivity: Widget { .activitySystemActionForegroundColor(Color.white) } dynamicIsland: { context in DynamicIsland { - DynamicIslandExpandedRegion(.leading) { - Image(systemName: "exclamationmark.triangle") - } DynamicIslandExpandedRegion(.center) { - Text("소리 수집이 중지되었습니다.") // 동일한 메시지 - } - DynamicIslandExpandedRegion(.trailing) { - // 추가 아이콘이나 요소가 필요하다면 여기에 추가 + HStack { + Image("Caution") // 아이콘 + .resizable() + .scaledToFit() + .frame(width: 59, height: 59, alignment: .top) + .foregroundColor(.white) + + Spacer() // 아이콘과 텍스트 사이 간격 + + VStack(alignment: .leading) { // 텍스트 영역 + Text("히어로드") + .font(.LiveActivitySub) + .foregroundColor(Color("SubFontColor")) + Text("소리수집이 중지되었습니다.") + .font(.medium) + .foregroundColor(Color("MainFontColor")) + } + } + .padding() // 전체 패딩 추가 + .frame(maxWidth: .infinity, maxHeight: 82) // HStack 최대 너비 설정 } } compactLeading: { - Image(systemName: "exclamationmark.triangle") + Image("Caution") + .resizable() + .scaledToFit() + .frame(width: 30, height: 30) } compactTrailing: { - Text("수집 중지") + Text("수집중지") + .font(.DaynamicIsland) + .foregroundColor(Color("MainFontColor")) } minimal: { - Image(systemName: "exclamationmark.triangle") + Image("Caution") + .resizable() + .scaledToFit() + .frame(width: 30, height: 30) } } } diff --git a/hearo/hearo.xcodeproj/project.pbxproj b/hearo/hearo.xcodeproj/project.pbxproj index 4435e14..6ffb94e 100644 --- a/hearo/hearo.xcodeproj/project.pbxproj +++ b/hearo/hearo.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 1121777B2CC283BC000A146F /* LiveActivityLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1121777A2CC283BC000A146F /* LiveActivityLiveActivity.swift */; }; 112177812CC283BD000A146F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 112177802CC283BD000A146F /* Assets.xcassets */; }; 112177852CC283BD000A146F /* LiveActivityExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 112177712CC283BC000A146F /* LiveActivityExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 11A2D0882CDE344500BF2BAF /* FontDesignSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D1B7572CDDD9D800BA2AF1 /* FontDesignSystem.swift */; }; 64F73A252CBAC9F100D2A140 /* HornSoundDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F73A242CBAC9F100D2A140 /* HornSoundDetector.swift */; }; 64F73A272CBAD1C000D2A140 /* SoundDetectorViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F73A262CBAD1C000D2A140 /* SoundDetectorViewModel.swift */; }; /* End PBXBuildFile section */ @@ -841,6 +842,7 @@ buildActionMask = 2147483647; files = ( 112177792CC283BC000A146F /* LiveActivityBundle.swift in Sources */, + 11A2D0882CDE344500BF2BAF /* FontDesignSystem.swift in Sources */, 1121777B2CC283BC000A146F /* LiveActivityLiveActivity.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1008,7 +1010,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"hearo/Preview Content\""; - DEVELOPMENT_TEAM = J5N8Y9F8Z8; + DEVELOPMENT_TEAM = GT56H2MYWV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = hearo/Info.plist; @@ -1042,7 +1044,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"hearo/Preview Content\""; - DEVELOPMENT_TEAM = J5N8Y9F8Z8; + DEVELOPMENT_TEAM = GT56H2MYWV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = hearo/Info.plist; @@ -1148,7 +1150,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"HearoadWatch Watch App/Preview Content\""; - DEVELOPMENT_TEAM = J5N8Y9F8Z8; + DEVELOPMENT_TEAM = GT56H2MYWV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "HearoadWatch-Watch-App-Info.plist"; @@ -1179,7 +1181,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"HearoadWatch Watch App/Preview Content\""; - DEVELOPMENT_TEAM = J5N8Y9F8Z8; + DEVELOPMENT_TEAM = GT56H2MYWV; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "HearoadWatch-Watch-App-Info.plist"; @@ -1287,7 +1289,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = J5N8Y9F8Z8; + DEVELOPMENT_TEAM = GT56H2MYWV; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = LiveActivity/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = LiveActivity; @@ -1315,7 +1317,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = J5N8Y9F8Z8; + DEVELOPMENT_TEAM = GT56H2MYWV; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = LiveActivity/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = LiveActivity; diff --git a/hearo/hearo/Application/hearoApp.swift b/hearo/hearo/Application/hearoApp.swift index 580551c..d310326 100644 --- a/hearo/hearo/Application/hearoApp.swift +++ b/hearo/hearo/Application/hearoApp.swift @@ -13,19 +13,28 @@ import WatchConnectivity @main struct hearoApp: App { @StateObject var appRootManager = AppRootManager() // 앱 전역에서 사용되는 상태 관리 + @Environment(\.scenePhase) private var scenePhase // 앱 상태 감지 var body: some Scene { WindowGroup { - // ContentView가 루트로 설정됨 - ZStack{ - - - Color("Background") - .ignoresSafeArea(.all) - ContentView(appRootManager: appRootManager) + ZStack { + Color("Background") + .ignoresSafeArea(.all) + ContentView(appRootManager: appRootManager) + } + .onChange(of: scenePhase) { newPhase in + handleScenePhaseChange(newPhase) } } } + + private func handleScenePhaseChange(_ newPhase: ScenePhase) { + if newPhase == .background && appRootManager.currentRoot == .working { + appRootManager.startLiveActivity(isWarning: false) + } else if newPhase == .inactive || newPhase == .active { + appRootManager.stopLiveActivity() + } + } } // 라이브 액티비티 속성 정의 @@ -44,8 +53,6 @@ final class AppRootManager: ObservableObject { private var isWarning = false // isWarning 상태 저장 private var currentWarningState: Bool = false // 현재 isWarning 상태 저장 - - // 루트 뷰 상태를 나타내는 열거형 enum AppRoot { case splash @@ -62,9 +69,12 @@ final class AppRootManager: ObservableObject { let hasSeenOnboarding = UserDefaults.standard.bool(forKey: "hasSeenOnboarding") self.currentRoot = hasSeenOnboarding ? .home : .startOnboarding } - + // 라이브 액티비티 시작 메서드 func startLiveActivity(isWarning: Bool) { + // 항상 현재 `Live Activity` 상태를 확인하고 초기화 + isActivityActive = Activity.activities.isEmpty == false + guard !isActivityActive else { print("라이브 액티비티가 이미 활성화되어 있습니다.") return @@ -74,32 +84,30 @@ final class AppRootManager: ObservableObject { print("라이브 액티비티가 지원되지 않거나 비활성화되었습니다.") return } - + // attributes와 initialContentState를 선언 let attributes = LiveActivityAttributes(name: "주행") let initialContentState = LiveActivityAttributes.ContentState(isWarning: isWarning) - let content = ActivityContent(state: initialContentState, staleDate: nil) + + let content = ActivityContent(state: initialContentState, staleDate: Date().addingTimeInterval(6)) // 전체 6초 do { - // attributes와 content를 사용하여 라이브 액티비티 요청 let activity = try Activity.request( attributes: attributes, content: content ) isActivityActive = true - currentWarningState = isWarning // 현재 상태를 저장 + currentWarningState = isWarning print("라이브 액티비티가 시작되었습니다: \(activity.id)") } catch { print("라이브 액티비티 시작 실패: \(error)") } } - - + + + // 라이브 액티비티 종료 메서드 func stopLiveActivity() { - let initialContentState = LiveActivityAttributes.ContentState(isWarning: isWarning) - _ = ActivityContent(state: initialContentState, staleDate: nil) - guard isActivityActive else { print("라이브 액티비티가 이미 중지 상태입니다.") return @@ -107,7 +115,7 @@ final class AppRootManager: ObservableObject { Task { for activity in Activity.activities { - await activity.end(nil , dismissalPolicy: .immediate) + await activity.end(nil, dismissalPolicy: .immediate) print("라이브 액티비티가 중지되었습니다: \(activity.id)") } @@ -115,49 +123,33 @@ final class AppRootManager: ObservableObject { currentWarningState = false // 중지되었으므로 상태 리셋 } } - + // 라이브 액티비티 업데이트 메서드 - func updateLiveActivity(isWarning: Bool) { - let initialContentState = LiveActivityAttributes.ContentState(isWarning: isWarning) - let content = ActivityContent(state: initialContentState, staleDate: nil) - - guard isActivityActive else { - print("활성화된 라이브 액티비티가 없어 업데이트할 수 없습니다.") - return - } - - // 같은 경고 상태로는 업데이트하지 않도록 체크 - guard isWarning != currentWarningState else { - print("경고 상태가 이미 \(isWarning)로 설정되어 있습니다.") - return - } - guard let activity = Activity.activities.first else { - isActivityActive = false - return - } - - Task { - _ = LiveActivityAttributes.ContentState(isWarning: isWarning) - await activity.update(content) // `using` 레이블 제거 - print("라이브 액티비티 상태 업데이트: \(isWarning ? "경고" : "주행 중")") - } - } -// func updateLiveActivity(iconName: String) { -// guard let activity = activity else { -// print("라이브 액티비티가 실행 중이지 않습니다.") -// startLiveActivity(iconName: iconName) -// return -// } -// -// print("라이브 액티비티 업데이트 시도") -// let state = DynamicAttributes.ContentState(remainingTime: remainingTime, iconName: iconName) -// let content = ActivityContent(state: state, staleDate: Date().addingTimeInterval(3600)) -// -// Task { -// await activity.update(content) -// print("라이브 액티비티 업데이트됨") -// } -// } + func updateLiveActivity(isWarning: Bool) { + let initialContentState = LiveActivityAttributes.ContentState(isWarning: isWarning) + let content = ActivityContent(state: initialContentState, staleDate: Date().addingTimeInterval(6)) // 전체 6초 유지 + + guard isActivityActive else { + print("활성화된 라이브 액티비티가 없어 업데이트할 수 없습니다.") + return + } + + // 같은 경고 상태로는 업데이트하지 않도록 체크 + guard isWarning != currentWarningState else { + print("경고 상태가 이미 \(isWarning)로 설정되어 있습니다.") + return + } + guard let activity = Activity.activities.first else { + isActivityActive = false + return + } + + Task { + _ = LiveActivityAttributes.ContentState(isWarning: isWarning) + await activity.update(content) + print("라이브 액티비티 상태 업데이트: \(isWarning ? "경고" : "주행 중")") + } + } } // ContentView 정의 (AppRootManager 클래스 외부에 위치) @@ -170,7 +162,7 @@ struct ContentView: View { case .splash: SplashView(appRootManager: appRootManager) case .startOnboarding: - StartOnboardingView(appRootManager: appRootManager) + StartOnboardingView(appRootManager: appRootManager) case .onboarding: OnboardingView(viewModel: OnboardingViewModel(appRootManager: appRootManager)) case .home: @@ -185,4 +177,3 @@ struct ContentView: View { } } } - diff --git a/hearo/hearo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/hearo/hearo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json index ac08590..e38e289 100644 --- a/hearo/hearo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/hearo/hearo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Icon 오후 9.05.06.png", + "filename" : "Icon 오후 9.05.06.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -13,6 +13,7 @@ "value" : "dark" } ], + "filename" : "Icon 오후 9.05.06 1.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -24,6 +25,7 @@ "value" : "tinted" } ], + "filename" : "Icon 오후 9.05.06 2.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git "a/hearo/hearo/Resources/Assets.xcassets/AppIcon.appiconset/Icon \354\230\244\355\233\204 9.05.06 1.png" "b/hearo/hearo/Resources/Assets.xcassets/AppIcon.appiconset/Icon \354\230\244\355\233\204 9.05.06 1.png" new file mode 100644 index 0000000..a874d0e Binary files /dev/null and "b/hearo/hearo/Resources/Assets.xcassets/AppIcon.appiconset/Icon \354\230\244\355\233\204 9.05.06 1.png" differ diff --git "a/hearo/hearo/Resources/Assets.xcassets/AppIcon.appiconset/Icon \354\230\244\355\233\204 9.05.06 2.png" "b/hearo/hearo/Resources/Assets.xcassets/AppIcon.appiconset/Icon \354\230\244\355\233\204 9.05.06 2.png" new file mode 100644 index 0000000..a874d0e Binary files /dev/null and "b/hearo/hearo/Resources/Assets.xcassets/AppIcon.appiconset/Icon \354\230\244\355\233\204 9.05.06 2.png" differ diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Car.imageset/Car 1.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Car.imageset/Car 1.svg new file mode 100644 index 0000000..aa479d3 --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Car.imageset/Car 1.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Car.imageset/Car 2.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Car.imageset/Car 2.svg new file mode 100644 index 0000000..aa479d3 --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Car.imageset/Car 2.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Car.imageset/Contents.json b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Car.imageset/Contents.json index 0f2ca9d..c6e81ec 100644 --- a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Car.imageset/Contents.json +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Car.imageset/Contents.json @@ -6,10 +6,12 @@ "scale" : "1x" }, { + "filename" : "Car 1.svg", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "Car 2.svg", "idiom" : "universal", "scale" : "3x" } diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Caution.imageset/Caution 1.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Caution.imageset/Caution 1.svg new file mode 100644 index 0000000..5bc2eab --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Caution.imageset/Caution 1.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Caution.imageset/Caution 2.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Caution.imageset/Caution 2.svg new file mode 100644 index 0000000..5bc2eab --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Caution.imageset/Caution 2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Caution.imageset/Contents.json b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Caution.imageset/Contents.json index f57f8bf..26cab3d 100644 --- a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Caution.imageset/Contents.json +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Caution.imageset/Contents.json @@ -6,10 +6,12 @@ "scale" : "1x" }, { + "filename" : "Caution 1.svg", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "Caution 2.svg", "idiom" : "universal", "scale" : "3x" } diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/CompletionCheck.imageset/CompletionCheck 1.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/CompletionCheck.imageset/CompletionCheck 1.svg new file mode 100644 index 0000000..b735006 --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/CompletionCheck.imageset/CompletionCheck 1.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/CompletionCheck.imageset/CompletionCheck 2.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/CompletionCheck.imageset/CompletionCheck 2.svg new file mode 100644 index 0000000..b735006 --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/CompletionCheck.imageset/CompletionCheck 2.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/CompletionCheck.imageset/Contents.json b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/CompletionCheck.imageset/Contents.json index 8768ca8..6fbba65 100644 --- a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/CompletionCheck.imageset/Contents.json +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/CompletionCheck.imageset/Contents.json @@ -6,10 +6,12 @@ "scale" : "1x" }, { + "filename" : "CompletionCheck 1.svg", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "CompletionCheck 2.svg", "idiom" : "universal", "scale" : "3x" } diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Copyright.imageset/Contents.json b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Copyright.imageset/Contents.json index 7d84448..7bf8ca9 100644 --- a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Copyright.imageset/Contents.json +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Copyright.imageset/Contents.json @@ -6,10 +6,12 @@ "scale" : "1x" }, { + "filename" : "Copyright 1.svg", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "Copyright 2.svg", "idiom" : "universal", "scale" : "3x" } diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Copyright.imageset/Copyright 1.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Copyright.imageset/Copyright 1.svg new file mode 100644 index 0000000..b712c6e --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Copyright.imageset/Copyright 1.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Copyright.imageset/Copyright 2.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Copyright.imageset/Copyright 2.svg new file mode 100644 index 0000000..b712c6e --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Copyright.imageset/Copyright 2.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/HearoadLetters.imageset/Contents.json b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/HearoadLetters.imageset/Contents.json index bdcde5d..d03f45f 100644 --- a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/HearoadLetters.imageset/Contents.json +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/HearoadLetters.imageset/Contents.json @@ -6,10 +6,12 @@ "scale" : "1x" }, { + "filename" : "HearoadLetters 1.svg", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "HearoadLetters 2.svg", "idiom" : "universal", "scale" : "3x" } diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/HearoadLetters.imageset/HearoadLetters 1.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/HearoadLetters.imageset/HearoadLetters 1.svg new file mode 100644 index 0000000..f1fa768 --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/HearoadLetters.imageset/HearoadLetters 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/HearoadLetters.imageset/HearoadLetters 2.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/HearoadLetters.imageset/HearoadLetters 2.svg new file mode 100644 index 0000000..f1fa768 --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/HearoadLetters.imageset/HearoadLetters 2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Icon.imageset/Contents.json b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Icon.imageset/Contents.json index 5df36d0..b95e909 100644 --- a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Icon.imageset/Contents.json +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Icon.imageset/Contents.json @@ -1,15 +1,17 @@ { "images" : [ { - "filename" : "Icon 오후 9.05.06.png", + "filename" : "Icon 오후 9.05.06 1.png", "idiom" : "universal", "scale" : "1x" }, { + "filename" : "Icon 오후 9.05.06.png", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "Icon 오후 9.05.06 2.png", "idiom" : "universal", "scale" : "3x" } diff --git "a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Icon.imageset/Icon \354\230\244\355\233\204 9.05.06 1.png" "b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Icon.imageset/Icon \354\230\244\355\233\204 9.05.06 1.png" new file mode 100644 index 0000000..a874d0e Binary files /dev/null and "b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Icon.imageset/Icon \354\230\244\355\233\204 9.05.06 1.png" differ diff --git "a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Icon.imageset/Icon \354\230\244\355\233\204 9.05.06 2.png" "b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Icon.imageset/Icon \354\230\244\355\233\204 9.05.06 2.png" new file mode 100644 index 0000000..a874d0e Binary files /dev/null and "b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/Icon.imageset/Icon \354\230\244\355\233\204 9.05.06 2.png" differ diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/MainCircle.imageset/Contents.json b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/MainCircle.imageset/Contents.json index 82ef3b3..42a22c1 100644 --- a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/MainCircle.imageset/Contents.json +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/MainCircle.imageset/Contents.json @@ -6,10 +6,12 @@ "scale" : "1x" }, { + "filename" : "MainCircle 1.svg", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "MainCircle 2.svg", "idiom" : "universal", "scale" : "3x" } diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/MainCircle.imageset/MainCircle 1.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/MainCircle.imageset/MainCircle 1.svg new file mode 100644 index 0000000..a296ccc --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/MainCircle.imageset/MainCircle 1.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/MainCircle.imageset/MainCircle 2.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/MainCircle.imageset/MainCircle 2.svg new file mode 100644 index 0000000..a296ccc --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/MainCircle.imageset/MainCircle 2.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/OnboardingCircle.imageset/Contents.json b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/OnboardingCircle.imageset/Contents.json index 2194a37..fece7d9 100644 --- a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/OnboardingCircle.imageset/Contents.json +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/OnboardingCircle.imageset/Contents.json @@ -6,10 +6,12 @@ "scale" : "1x" }, { + "filename" : "OnboardingCircle 1.svg", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "OnboardingCircle 2.svg", "idiom" : "universal", "scale" : "3x" } diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/OnboardingCircle.imageset/OnboardingCircle 1.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/OnboardingCircle.imageset/OnboardingCircle 1.svg new file mode 100644 index 0000000..b23a52e --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/OnboardingCircle.imageset/OnboardingCircle 1.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/OnboardingCircle.imageset/OnboardingCircle 2.svg b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/OnboardingCircle.imageset/OnboardingCircle 2.svg new file mode 100644 index 0000000..b23a52e --- /dev/null +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/OnboardingCircle.imageset/OnboardingCircle 2.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/StartButton.imageset/Contents.json b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/StartButton.imageset/Contents.json index f5e64f7..4c2be32 100644 --- a/hearo/hearo/Resources/Assets.xcassets/SVGIcon/StartButton.imageset/Contents.json +++ b/hearo/hearo/Resources/Assets.xcassets/SVGIcon/StartButton.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "시작하기.svg", + "filename" : "시작하기.svg", "idiom" : "universal", "scale" : "1x" }, diff --git a/hearo/hearo/Resources/FontDesignSystem.swift b/hearo/hearo/Resources/FontDesignSystem.swift index a5bbf26..2fc916b 100644 --- a/hearo/hearo/Resources/FontDesignSystem.swift +++ b/hearo/hearo/Resources/FontDesignSystem.swift @@ -12,4 +12,8 @@ extension Font { static let light = Font.custom("Pretendard-Light", size: 18) // Light - title1 static let regular = Font.custom("Pretendard-Regular", size: 15) // Regular - title2 static let mainTitle = Font.custom("Pretendard-Medium", size: 24) // Medium + static let LiveActivityMain = Font.custom("Pretendard-Medium", size: 20) + static let LiveActivitySub = Font.custom("Pretendard-Light", size: 13) + static let DaynamicIsland = Font.custom("Pretendard-Medium", size: 8) + } diff --git a/hearo/hearo/Sources/Helper/HornSoundDetector.swift b/hearo/hearo/Sources/Helper/HornSoundDetector.swift index 088e993..65f681f 100644 --- a/hearo/hearo/Sources/Helper/HornSoundDetector.swift +++ b/hearo/hearo/Sources/Helper/HornSoundDetector.swift @@ -1,4 +1,3 @@ - // // HornSoundDetector.swift // hearo @@ -20,6 +19,16 @@ class HornSoundDetector: NSObject, ObservableObject { private var inputNode: AVAudioInputNode! private var soundClassifier: HornSoundClassifier_V11? private var streamAnalyzer: SNAudioStreamAnalyzer? +//fix 시작 +// @Published var isRecording = false +// @Published var classificationResult = "녹음 시작 전" +// @Published var detectedHornSound = false +// @Published var topClassification: SNClassification? // 가장 높은 분류 저장 +// @Published var mlConfidences: [Double] = Array(repeating: 0.0, count: 4) // 각 채널의 신뢰도 배열 +// private var cancellables = Set() + +// override init() { +//fix 끝 private var appRootManager: AppRootManager // appRootManager 속성 추가 @Published var isRecording = false @@ -28,10 +37,16 @@ class HornSoundDetector: NSObject, ObservableObject { init(appRootManager: AppRootManager) { self.appRootManager = appRootManager + super.init() setupAudioSession() setupAudioEngine() setupSoundClassifier() +//fix +// checkNotificationPermission() + + // 앱이 백그라운드로 전환될 때 녹음을 중지하도록 옵저버 설정 +//fix NotificationCenter.default.addObserver(self, selector: #selector(stopRecording), name: UIApplication.didEnterBackgroundNotification, object: nil) } @@ -44,6 +59,13 @@ class HornSoundDetector: NSObject, ObservableObject { print("오디오 세션 설정 실패: \(error)") } } + //fix +// private func setupAudioEngine() { +// audioEngine = AVAudioEngine() +// inputNode = audioEngine.inputNode +// } + +//fix private func setupAudioEngine() { audioEngine = AVAudioEngine() @@ -63,18 +85,58 @@ class HornSoundDetector: NSObject, ObservableObject { print("소리 분류기 생성 실패: \(error)") } } +//fix + +// private func checkNotificationPermission() { +// UNUserNotificationCenter.current().getNotificationSettings { settings in +// switch settings.authorizationStatus { +// case .notDetermined: +// self.requestNotificationPermission() +// case .denied: +// print("알림 권한이 거부되었습니다. 설정에서 권한을 변경해주세요.") +// case .authorized, .provisional, .ephemeral: +// print("알림 권한이 허용되었습니다.") +// @unknown default: +// break +// } +// } +// } + +// private func requestNotificationPermission() { +// UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in +// if granted { +// print("알림 권한이 허용되었습니다.") +// } else if let error = error { +// print("알림 권한 요청 중 오류 발생: \(error.localizedDescription)") +// } +// } +// } + +//fix + func startRecording() { guard !isRecording else { print("녹음이 이미 시작된 상태입니다.") return } - +//fix + +// let format = inputNode.outputFormat(forBus: 0) +// streamAnalyzer = SNAudioStreamAnalyzer(format: format) + +// guard let streamAnalyzer = streamAnalyzer, +// let soundClassifier = soundClassifier else { +// print("스트림 분석기 또는 소리 분류기 생성 실패") +// return +// } +//fix guard let streamAnalyzer = streamAnalyzer, let soundClassifier = soundClassifier else { print("스트림 분석기 또는 소리 분류기 생성 실패") return } + do { let request = try SNClassifySoundRequest(mlModel: soundClassifier.model) try streamAnalyzer.add(request, withObserver: self) @@ -82,12 +144,20 @@ class HornSoundDetector: NSObject, ObservableObject { print("분류 요청 생성 실패: \(error)") return } +// //fix + +// inputNode.installTap(onBus: 0, bufferSize: 8192, format: format) { [weak self] buffer, time in +// self?.streamAnalyzer?.analyze(buffer, atAudioFramePosition: time.sampleTime) +// } + +//fix let format = inputNode.outputFormat(forBus: 0) inputNode.installTap(onBus: 0, bufferSize: 8192, format: format) { [weak self] buffer, time in self?.streamAnalyzer?.analyze(buffer, atAudioFramePosition: time.sampleTime) } + audioEngine.prepare() do { try audioEngine.start() @@ -112,7 +182,10 @@ class HornSoundDetector: NSObject, ObservableObject { print("오디오 엔진 중지됨") } + func sendNotification(title: String, body: String) { + print("알림 발송 시도") + let content = UNMutableNotificationContent() content.title = title content.body = body @@ -133,6 +206,27 @@ extension HornSoundDetector: SNResultsObserving { func request(_ request: SNRequest, didProduce result: SNResult) { guard let result = result as? SNClassificationResult else { return } +//fix +// let topClassifications = result.classifications.prefix(3) + +// DispatchQueue.main.async { +// // 첫 번째 분류를 가장 신뢰도 높은 것으로 설정 +// if let topClassification = topClassifications.first(where: { classification in +// return classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren" +// }) { +// self.topClassification = topClassification // 가장 높은 분류 저장 +// self.classificationResult = topClassification.identifier // 소리 종류만 저장 +// } + +// for (index, classification) in topClassifications.enumerated() { +// if classification.identifier == "Bicyclebell" || classification.identifier == "Carhorn" || classification.identifier == "Siren" { +// // 경적 및 사이렌 소리 감지 +// if classification.confidence >= 1.0 { +// self.mlConfidences[index] = classification.confidence +// // 원하는 로직을 추가하세요 +// } +// } +//fix DispatchQueue.main.async { if let topClassification = result.classifications.first, topClassification.confidence >= 1.0 { let isRelevantSound = ["Bicyclebell", "Carhorn", "Siren"].contains(topClassification.identifier) @@ -144,6 +238,7 @@ extension HornSoundDetector: SNResultsObserving { } } else { print("신뢰도 부족 또는 관련 없는 소리 감지됨") + } } } diff --git a/hearo/hearo/Sources/Helper/SoundDetectorViewModel.swift b/hearo/hearo/Sources/Helper/SoundDetectorViewModel.swift index e643a22..0a77a5e 100644 --- a/hearo/hearo/Sources/Helper/SoundDetectorViewModel.swift +++ b/hearo/hearo/Sources/Helper/SoundDetectorViewModel.swift @@ -10,9 +10,17 @@ import WatchConnectivity class SoundDetectorViewModel: NSObject, ObservableObject, WCSessionDelegate { @Published var isRecording = false +//fix +// @Published var classificationResults: [String] = Array(repeating: "녹음 시작 전", count: 4) +// @Published var detectedHornSounds: [Bool] = Array(repeating: false, count: 4) + +// private var soundDetectors: [HornSoundDetector] = [] +// private var mlConfidences: [Double] = Array(repeating: 0.0, count: 4) +//fix @Published var classificationResult: String = "녹음 시작 전" private var soundDetector: HornSoundDetector + private var cancellables = Set() private var appRootManager: AppRootManager private var isActivityActive = false @@ -30,15 +38,82 @@ class SoundDetectorViewModel: NSObject, ObservableObject, WCSessionDelegate { WCSession.default.activate() } +//fix +// for _ in 0..<4 { +// let soundDetector = HornSoundDetector() +// soundDetectors.append(soundDetector) +// } +//fix // for _ in 0..<4 { // let soundDetector = HornSoundDetector() // soundDetectors.append(soundDetector) // } + setupBindings() } private func setupBindings() { +//fix +// for (index, soundDetector) in soundDetectors.enumerated() { +// soundDetector.$isRecording +// .assign(to: \.isRecording, on: self) +// .store(in: &cancellables) + +// soundDetector.$classificationResult +// .sink { [weak self] result in +// self?.classificationResults[index] = result +// } +// .store(in: &cancellables) + +// soundDetector.$detectedHornSound +// .sink { [weak self] detected in +// self?.detectedHornSounds[index] = detected +// } +// .store(in: &cancellables) + +// soundDetector.$topClassification +// .sink { [weak self] topClassification in +// guard let self = self else { return } +// if let classification = topClassification { +// self.mlConfidences[index] = classification.confidence +// self.checkAllConfidences() +// } +// } +// .store(in: &cancellables) +// } +// } + +// private func checkAllConfidences() { +// // 모든 마이크의 신뢰도가 0.99 이상인지 확인 +// if mlConfidences.allSatisfy({ $0 >= 0.99 }) { +// DispatchQueue.main.async { +// self.appRootManager.currentRoot = .warning +// self.sendWarningToWatch() // 애플워치에 경고 전송 +// self.updateApplicationContext() // 애플워치에 데이터 전송 +// } +// } +// } + +// private func getHighestConfidenceSound() -> String? { +// if let highestConfidenceIndex = mlConfidences.enumerated().max(by: { $0.element < $1.element })?.offset, +// highestConfidenceIndex < classificationResults.count { +// return classificationResults[highestConfidenceIndex] +// } +// return nil +// } + +// private func updateApplicationContext() { +// do { +// if let highestConfidenceSound = getHighestConfidenceSound() { +// let context = ["highestConfidenceSound": highestConfidenceSound] +// try WCSession.default.updateApplicationContext(context) +// print("applicationContext 데이터 전송 성공: \(context)") +// } +// } catch { +// print("applicationContext 데이터 전송 실패: \(error.localizedDescription)") +// } +//fix // for (index, soundDetector) in soundDetectors.enumerated() { // soundDetector.$isRecording @@ -116,6 +191,40 @@ class SoundDetectorViewModel: NSObject, ObservableObject, WCSessionDelegate { print("애플워치가 연결되지 않음") return } +//fix + + +// if let highestConfidenceSound = getHighestConfidenceSound() { + +// let message = ["alert": highestConfidenceSound] + +// WCSession.default.sendMessage(message, replyHandler: nil) { error in +// print("애플워치로 경고 메시지 전송 오류: \(error.localizedDescription)") +// } +// } else { +// print("경고를 보낼 신뢰도 높은 소리가 없음") +// } +// } + +// func toggleRecording(start: Bool) { +// for detector in soundDetectors { +// if start { +// detector.startRecording() +// } else { +// detector.stopRecording() +// } +// } +// } + +// func startRecording() { +// toggleRecording(start: true) +// } + +// func stopRecording() { +// toggleRecording(start: false) +// } + +//fix @@ -157,6 +266,7 @@ class SoundDetectorViewModel: NSObject, ObservableObject, WCSessionDelegate { // // } // + // 필수 WCSessionDelegate 메서드 구현 func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { print("MLWCSession 활성화 완료. 상태: \(activationState)") diff --git a/hearo/hearo/Sources/Presentations/Home/View/HomeView.swift b/hearo/hearo/Sources/Presentations/Home/View/HomeView.swift index 60c2970..6b6ec94 100644 --- a/hearo/hearo/Sources/Presentations/Home/View/HomeView.swift +++ b/hearo/hearo/Sources/Presentations/Home/View/HomeView.swift @@ -145,7 +145,7 @@ struct HomeView: View { if startLottieAnimation { LottieView(animationName: "start_view", animationScale: 1) .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) - .offset(y: targetOffset - UIScreen.main.bounds.height / 57) + .offset(y: 274) .edgesIgnoringSafeArea(.all) } diff --git a/hearo/hearo/Sources/Presentations/Working/View/WorkingView.swift b/hearo/hearo/Sources/Presentations/Working/View/WorkingView.swift index ea45dc7..f559d4d 100644 --- a/hearo/hearo/Sources/Presentations/Working/View/WorkingView.swift +++ b/hearo/hearo/Sources/Presentations/Working/View/WorkingView.swift @@ -28,7 +28,7 @@ struct WorkingView: View { LottieView(animationName: "sound_collection", animationScale: 1) .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) // .offset(y: targetOffset - UIScreen.main.bounds.height / 4) - .offset(x: -3,y: 60) + .offset(x: -1.4 ,y: 60) .edgesIgnoringSafeArea(.all) diff --git a/hearo/hearo/Sources/Presentations/Working/ViewModel/WorkingViewModel.swift b/hearo/hearo/Sources/Presentations/Working/ViewModel/WorkingViewModel.swift index 1daa172..3b1ea21 100644 --- a/hearo/hearo/Sources/Presentations/Working/ViewModel/WorkingViewModel.swift +++ b/hearo/hearo/Sources/Presentations/Working/ViewModel/WorkingViewModel.swift @@ -8,8 +8,11 @@ import Foundation import Combine import SwiftUI import AVFoundation + + //import AudioToolbox + class WorkingViewModel: ObservableObject { @Published var appRootManager: AppRootManager @Published var soundDetectorViewModel: SoundDetectorViewModel @@ -17,10 +20,15 @@ class WorkingViewModel: ObservableObject { private var cancellables = Set() // Combine 구독을 저장하는 변수 추가 private var hornSoundDetector: HornSoundDetector // HornSoundDetector 사용 - init(appRootManager: AppRootManager) { self.appRootManager = appRootManager self.soundDetectorViewModel = SoundDetectorViewModel(appRootManager: appRootManager) +//fix +// configureAudioSession() // AVAudioSession 설정 +// } +//fix + // 오디오 세션 설정 + self.hornSoundDetector = HornSoundDetector(appRootManager: appRootManager) // AVAudioSession 설정 @@ -29,6 +37,7 @@ class WorkingViewModel: ObservableObject { observeSoundDetection() } + func configureAudioSession() { do { let audioSession = AVAudioSession.sharedInstance() @@ -39,6 +48,15 @@ class WorkingViewModel: ObservableObject { print("오디오 세션 설정 중 오류 발생: \(error.localizedDescription)") } } +//fix + // 모든 채널의 분류 결과를 출력 +// var classificationResult: String { +// var results = "" +// for (index, result) in soundDetectorViewModel.classificationResults.enumerated() { +// results += "채널 \(index + 1): \(result)\n" +// } +// return results +//fix private func observeSoundDetection() { soundDetectorViewModel.$classificationResult @@ -51,34 +69,54 @@ class WorkingViewModel: ObservableObject { } } .store(in: &cancellables) + } + // 녹음 시작 func startRecording() { guard !isRecording else { print("녹음이 이미 진행 중입니다.") return } +//fix +// isRecording = true +//fix print("WorkingViewModel: startRecording() 호출됨") hornSoundDetector.startRecording() // HornSoundDetector에서 처리 print("WorkingViewModel: 녹음 시작 완료") - appRootManager.startLiveActivity(isWarning: false) + appRootManager.startLiveActivity(isWarning: false) // 녹음 시작 시 라이브 액티비티 활성화 } + // 녹음 중지 func stopRecording() { guard isRecording else { print("녹음이 이미 중지된 상태입니다.") return } +//fix +// isRecording = false +//fix print("WorkingViewModel: stopRecording() 호출됨") hornSoundDetector.stopRecording() // HornSoundDetector에서 처리 print("녹음 중지 완료") - - appRootManager.stopLiveActivity() + + + appRootManager.stopLiveActivity() // 녹음 중지 시 라이브 액티비티 비활성화 + } + // 녹음 완료 및 종료 화면으로 이동 func finishRecording() { + stopRecording() // 녹음 중지 메서드 호출 triggerErrorHaptic() - appRootManager.currentRoot = .finish + appRootManager.currentRoot = .finish // 종료 화면으로 전환 } + + + // 햅틱 피드백 트리거 + private func triggerErrorHaptic() { + AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) + } + }